104 KiB
XML syntax
Sample:
<Action>
<Name><![CDATA[Examine]]></Name>
<OverrideName><![CDATA[]]></OverrideName>
<actionparent><![CDATA[None]]></actionparent>
<Active><![CDATA[True]]></Active>
<FailOnFirst><![CDATA[True]]></FailOnFirst>
<InputType><![CDATA[None]]></InputType>
<CustomChoiceTitle><![CDATA[]]></CustomChoiceTitle>
<EnhancedInputData>
<BackgroundColor><![CDATA[-1929379841]]></BackgroundColor>
<TextColor><![CDATA[-16777216]]></TextColor>
<Imagename><![CDATA[]]></Imagename>
<UseEnhancedGraphics><![CDATA[True]]></UseEnhancedGraphics>
<AllowCancel><![CDATA[True]]></AllowCancel>
<NewImage><![CDATA[]]></NewImage>
<TextFont><![CDATA[Microsoft Sans Serif, 12pt]]></TextFont>
</EnhancedInputData>
<PassCommands>
<Command>
<CmdType><![CDATA[CT_DISPLAYPICTURE]]></CmdType>
<CommandText><![CDATA[]]></CommandText>
<Part2><![CDATA[Toybox.jpg]]></Part2>
<Part3><![CDATA[]]></Part3>
<Part4><![CDATA[]]></Part4>
<EnhancedInputData>
<BackgroundColor><![CDATA[-1929379841]]></BackgroundColor>
<TextColor><![CDATA[-16777216]]></TextColor>
<Imagename><![CDATA[]]></Imagename>
<UseEnhancedGraphics><![CDATA[True]]></UseEnhancedGraphics>
<AllowCancel><![CDATA[True]]></AllowCancel>
<NewImage><![CDATA[]]></NewImage>
<TextFont><![CDATA[Microsoft Sans Serif, 12pt]]></TextFont>
</EnhancedInputData>
</Command>
</PassCommands>
</Action>
Action
Name
:string
OverrideName
:string
, default""
actionparent
:string
, default"None"
Active
:bool
(True
/False
)FailOnFirst
:bool
, defaultTrue
InputType
:ActionInputType
(None
,Object
,Character
,ObjectOrCharacter
,Text
,Custom
,Inventory
)CustomChoiceTitle
:string
, default""
CustomChoices
:CustomChoice[]
, only if array not emptyEnhancedInputData
:EnhancedInputData
Conditions
:Condition[]
, only if array not emptyPassCommands
:variant<Command, Condition>[]
, only if array not emptyFailCommands
:variant<Command, Condition>[]
, only if array not empty
CustomChoice
Name
:string
EnhancedInputData
BackgroundColor
:color
:color.ToArgb() == 0xaarrggbb
reinterpret_casted toint32_t
, defaultrgba(255, 255, 255, 140)
TextColor
:color
, defaultrgba(0, 0, 0, 255)
Imagename
:string
, default""
. Ignored?UseEnhancedGraphics
:bool
, default true. CalledUse Overlay Graphics
in GUI. When true, replace image + choices overlaid, when false,NewImage
ignored and popup window with normal winforms table and ok/cancel button (even whenAllowCancel
is false).AllowCancel
:bool
, default trueNewImage
:string
, default""
TextFont
:font
, defaultTimes New Roman Bold, 12pt
. Ignored/buggy?
Condition
Name
:string
, default""
Checks
:Check[]
, only if array not emptyPassCommands
: only if array not emptyFailCommands
: only if array not empty
Check
CondType
:ConditionType
, defaultCT_Item_Held_By_Player
CkType
:CheckType
(CT_Uninitialized
,And
,Or
)Step2
:string
, default""
Step3
:string
, default""
Step4
:string
, default""
Command
CmdType
:CommandType
CommandText
:string
, default""
Part2
:string
, default""
Part3
:string
, default""
Part4
:string
, default""
CustomChoices
:CustomChoice[]
, only if not empty.EnhancedInputData
:EnhancedInputData
.
Action
ActionParent
: used to generate submenus in right-click menu (yet another ad-hoc grouping). If parent is inactive, action is inactive too.FailOnFirst
: pseudocode,c.Check()
runs PassCommands for each iteration when it is a loop (yes, whenFailOnFirst
is false,PassCommands
is executed one more time)bool success = FailOnFirst || Conditions.empty(); for (c : Conditions) { if (c.Check()) // check succeeded or it was a loop { if (!FailOnFirst) success = true; if (!FailOnFirst || !c.loop) c.PassCommands.Run(); } else { c.FailCommands.Run(); if (FailOnFirst) { success = false; break; } } } (success ? PassCommands : FailCommands).Run();
PassCommands
,FailCommands
: one of them is executed based on the conditions' result, see above for exact semantics, but generally, withFailOnFirst
it means all checks passed (soand
with short-circuit), and withoutFailOnFirst
it means at least one check passed (soor
without short-circuit).EnhancedData
: only used whenInputType != None && InputType != Text && UseEnhancedGraphics && !HideMainPicDisplay
InputType
: ask a question at the beginning of the action if not None.Object
: select an object currently visible in the player GUI. In practice, this means any object in the inventory, any object in the current room (including portals), any subobject of any of them; or any object held by a character in the current room with allow inventory interactions. (Subobjects inside characters don't appear but subobjects inside subobjects do, albeit buggy! Just what did you expect?) List items: object DisplayNames, no tag when!UseEnhancedGraphics
.AdditionalData
value: object's name (override ignored).Character
: select a character in the current room. List object: character DisplayName with tag.AdditionalData
value: character's name (override ignored).ObjectOrCharacter
: union of the previous two, except characters lack tag withUseEnhancedGraphics
...Text
: input is not a selection from a list, but a free form text.EnhancedData
is ignored in this case.Custom
:CustomChoices
contains a list of choices. When not using overlay graphics, the color of the text can be changed with[c r,g,b]...[/c]
(except the actual ranges are ignored,foo[c 0,255,0]bar
sets the full text to green, in case of multiple[c]
tags, the last one wins. When using overlay graphics, these tags are displayed as-is, without any processing. Did you expect any logic behind this?)Inventory
: objects in the player's inventory. No tag when!UseEnhancedGraphics
.- Note that this doesn't nest properly. Executing an action inside an action means that after returning to the parent action, the selection is lost.
action_object
: a hidden parameter. This is set to the object when executing an action on an object, null otherwise (this is nesting properly).player_moving
: another hidden parameter, only available in<<On Player Leave First Time>>
and<<On Player Leave>>
actions, when triggered by a the player (the 3DPD) trying to leave the current room. It is set to invalid in other cases.- Execution:
- Clear
AdditionalData
- When there is an
EhancedData
with an image,ShowMainImage
andOverlayGraphics
is true, andInputType
is notNone
orText
, set the image to that. - When
InputType
is notNone
, ask the input. If canceled, return (action result is false). Otherwise get theAdditionalData
value to set, then print it, unless it is an uuid and there is an object with that uuid, because in that case it prints that object's name. - Execute conditions
- Execute pass/fail commands
- Clear
Command
CustomChoices
: can be set onCT_SETVARIABLEBYINPUT
EnhancedData
: can be set onCT_SETVARIABLEBYINPUT
- Exception handling: exceptions are caught when executing commands. Rags displays a message, then continues with the next command.
Generic command info
Part2
,Part3
,Part4
andText
values are always run through the text replacement function before usage.- Object/room UUID or name: this generally means rags first tries to find a
room/object with the given UUID, and if there's no one, it tries to find one
with the name. In this case everything is processed as strings, so if you end
up with an object whose UUID is in fact not a valid UUID, it will still find
it. On the other hand, this requires exact match, i.e. 8-4-4-4-12 format
without white spaces. However, there are a few cases when Rags actually tries
to parse the given string as a UUID and somehow work differently depending on
the outcome, but they're clearly marked as "room UUID (can be parsed) or
name". In this case you can ue other formats, see allowed GUID formats
(archive) for details. (And undocumented details: except the
last format, spaces are not allowed, but the string is trimmed before use, so
" {123..."
is valid, but"{ 123..."
is not). - Aborts the command list: subsequent items in the currently executing
PassCommands
/FailCommands
will not run, but it does not affect commands above. So for example,
IfAction \-PassCommands . |-Condition . | |-PassCommands . | | |-Command cmd0 . | | |-Command cmd1 . | | \-Command cmd2 . | \-FailCommands . | . \-Command cmd3 . |-Command cmd4 . \-Command cmd5
cmd1
aborts,cmd2
won't be executed (and neithercmd3
), but instead it will continue withcmd4
andcmd5
. This means that aCT_LOOP_BREAK
not directly in the loop'sPassCommands
can act weird.
Set custom property commands
Part2
:name:property_name
Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored
The exact interpretation of Part2
varies, but generally if it doesn't have
exactly one colon, or if the entity doesn't exists, it doesn't do anything. If
the entity doesn't have a property with name property_name
(case
sensitively), it also doesn't do anything.
Part4
is double evaluated, then in case of *_JS
commands, it is evaluated as
a JavaScript code (whatever dialect Microsoft's JScript supports), unless it is
an empty string anfter trimming. In this case, it is replaced with -1
. If the
return value is null
, it is replaced with "Null value returned from evaluate."
string, otherwise it is converted to a string, using whatever rules
this this shit (archive) uses.
If Part3
is not one of the allowed values, it doesn't do anything. If Part3
is Equals
, set the property's value to Part4
. Otherwise it tries to
interpret both the property's current value and Part4
as a double and do the
arithmetic operation. If one of the values are not a number, it doesn't do
anything, except that with Subtract
if Part4
is not a double, it throws an
exception.
Room enter procedure
This is used by multiple commands. Action and timer executions are skipped in some cases.
- Prints an empty line to the log.
- Sets room & main image (inline or to the main image area, handling layered images) to the room's image (removes it if the room doesn't have one).
- When not skipping actions: when the room's
EnterFirstTime
is false, set it to true and execute inner procedure with<<On Player Enter First Time>>
. Afterwards, execute inner procedure with<<On Player Enter>>
. - If the actions changed the player's room, don't do anything else.
- Prints the player's room description.
- If notifications are enabled:
- If the room has visible objects, it prints
"You can see "
, for each visible object preposition +" "
+ display name (if preposition after trimming is empty, preposition + space is skipped), separated by", "
, all on one line. - For each character in the room, it prints display name +
" is here."
- If the room has visible objects, it prints
- When not skipping timers: execute multi timer procedure.
Inner procedure
- There's a try-catch around the whole stuff, silently discarding any error inside (but action execution already contains a try-catch inside, so nothing should throw in this shit).
- First save the player's current room (original room)
- If the player's room has the specified action, execute it without
action_object
. - If the player has the specified action, execute it without
action_object
. - Go through all objects in the player's current room.
- If the object's
EnterFirstTime
flag is false, set it to true and execute its<<On Player Enter First Time>>
action if it exists with object asaction_object
. - When the inner procedure is called with
<<On Player Enter>>
and the object has an action with that name, and the player is still in the original room, execute it with object asaction_object
. - If the object is a
Container
and it isOpen
or notOpenable
, go through all subobjects and do the previous two points for these subobjects. Note that this operation is not recursive, so subobjects of the subobjects are not considered.
- If the object's
- Go through all characters in the player's current room.
- If the character's
EnterFirstTime
flag is false, set it to true and execute its<<On Player Enter First Time>>
action if it exists withoutaction_object
. - When the inner procedure is called with
<<On Player Enter>>
and the character has an action with that name, and the player is still in the original room, execute it withoutaction_object
.
- If the character's
This means that for example if the player in entering the room for the first time, object/character enter first time events are executed before the room's ``<>`, but after if the player is entering a second time...
Available CommandTypes
CT_UNINITIALIZED
CT_ACTION_ADD_CUSTOMCHOICE
Part2
:type:name:action_name
Part3
: ignoredPart4
: ignoredText
: choice to add (case sensitive)- FIXUP: none
If Part2
doesn't contain a colon, this action doesn't do anything. If it only
contains one colon, it is parsed as type:action_name
and name is treated as
empty. A third colon and everything after it is ignored.
type
is case sensitively one of the following:
Chr
:name
is a character name (case insensitive). If the character does not exists, it throws an exception.Obj
:name
is an object UUID or name (case insensitive). If the object does not exist, it doesn't do anything.Player
:name
is ignored.Room
:name
is a room UUID (can be parsed) or name (case insensitive). If the room doesn't exist, it doesn't do anything.Timer
:name
is a timer (case insensitive). If the timer doesn't exist, it doesn't do anything.- Anything else: it doesn't do anything.
action_name
names an action inside the specified entity (case insensitive). If
it doesn't exists, this action doesn't do anything. If the action already has a
custom choice named Text
, it doesn't do anything. Otherwise it adds Text
to
the end of the custom choices.
CT_ACTION_CLEAR_CUSTOMCHOICE
Part2
:type:name:action_name
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
See CT_ACTION_ADD_CUSTOMCHOICE for Part2
handling. If it specifies a valid action, this action removes all custom choices
from it.
CT_ACTION_REMOVE_CUSTOMCHOICE
Same as CT_ACTION_ADD_CUSTOMCHOICE, except
this action removes Text
from the custom choices, if the custom choice exists.
CT_DISPLAYCHARDESC
Part2
: character name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the specified character does not exist, it throws an exception. Otherwise, it prints the characters description to the log.
Additionally, if notifications are not turned off, and the character has any
Visible
and Worn
object in it's inventory, it prints <character display name> is wearing:
, then the display names of all worn objects in separate
lines. If the character has any Visible
but not Worn
objects, it prints
<character display name> is carrying:
, then the display names of all not worn
objects, separated by newlines.
CT_CHAR_DISPLAYPORT
Part2
: character name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the specified character does not exist, it doesn't do anything. Otherwise
what it does depends on the value of HideMainPicDisplay
and UseInlineImages
:
HideMainPicDisplay
andUseInlineImages
: if the character doesn't have a portrait, throw an exception. If the portrait is an image, paste it into the log (without scaling and buggily, so sometimes it will just insert an empty line...). If it is an audio/video, print the full path of the temporary file to the log (where rags extracted the media file)...HideMainPicDisplay
and notUseInlineImages
: it doesn't do anything.- Else: if the character doesn't have a portrait, or it is not an image, it doesn't do anything. Otherwise, it sets the main picture and its overlay(!) (right to the log) to character's portrait, including layered images. (Since file overlays are over the main overlay, this will work, but if the base image has transparency, it will be weird because of the double draw.) (I think rags author wanted to support videos here based on the code, he just fucked it up.)
CT_MOVECHAR
Part2
: character name (case insensitive)Part3
: room UUID (can be parsed) or name (case insensitive). Special values (not parsed, must match exactly):00000000-0000-0000-0000-000000000001
: player's current room00000000-0000-0000-0000-000000000002
: "void" room
Part4
: ignoredText
: ignored- FIXUP: if
Part3
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart3
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
If the specified character does not exist, throws an exception. If the
character is in the same room as the player, and it has an action named <<On Character Leave>>
, execute that (without a loop_item
).
If Part3
is 00000000-0000-0000-0000-000000000001
, move the character to the
same room as the character, and if it has an action named <<On Character Enter>>
, execute it. If Part3
is 00000000-0000-0000-0000-000000000002
, move
the character nowhere.
If Part3
is not one of those special values, check if it can be parsed as
a UUID. If yes, use that as-is, otherwise look up the room with that name. If
there is no room with that name, DISPLAY A FUCKING MESSAGE BOX ("Error
in command CT_MoveChar. Could not locate a room called "
+ Part3
)
and returns. (But UUIDs are not checked for validity, so it's possible to move
someone to a not existing room.) Otherwise, move the character to the specified
room. If the room is the player's current room, and the character has an <<On Character Enter>>
action, execute it.
CT_MOVECHARINVTOPLAYER
Part2
: character name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the specified character does not exist, it doesn't do anything. Otherwise
take all Carryable
and Visible
objects from the character's inventory and
move them to the player's inventory.
CT_CHAR_MOVETOOBJ
Part2
: character name (case insensitive)Part3
: object UUID (case insensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the specified character or object doesn't exist, it doesn't do anything. If the object is in a room, move the character to that room. If the object's location's UUID (after move) is the same as the player's room UUID (this can be actually true when the object is in a character, but the character's UUID is the same as the player's room's UUID...), execute room enter procedure without actions.
CT_CHAR_SETPORT
Part2
: character name (case insensitive)Part3
: file namePart4
: ignoredText
: ignored- FIXUP: none
If the specified character doesn't exist, it doesn't do anything. Otherwise set the character's image to the specified file name, without error checking (so it is possible to set it to a non-existing file).
CT_SETCHARACTION
Part2
: character name (case insensitive)Part3
:action_name-state
Part4
: ignoredText
: ignored- FIXUP: none
If the specified character doesn't exist, it doesn't do anything. If Part3
doesn't contain a hyphen, it throws an exception. Otherwise everything until
the last hyphen is the action_name
. If the character doesn't have an action
with that name (case insensitively), it doesn't do anything. Otherwise it sets
its active flag, to true if state
is Active
(case sensitively), to false
otherwise.
CT_CHAR_SET_CUSTOM_PROPERTY
Part2
:character_name:property_name
Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored- FIXUP: none
See for more info on setting custom properties. If a
character named character_name
doesn't exists (case insensitively) it doesn't
do anything.
CT_CHAR_SET_CUSTOM_PROPERTY_JS
Same as CT_CHAR_SET_CUSTOM_PROPERTY, but with JavaScript.
CT_SETCHARDESC
Part2
: character name (case insensitive)Part3
: ignoredPart4
: ignoredText
: new character description- FIXUP: none
If the specified character does not exist, throws an exception. Otherwise
sets the character's description to Text
.
CT_CHAR_SET_GENDER
Part2
: character name (case insensitive)Part3
:Male
/Female
/Other
(case sensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the specified character doesn't exist, it doesn't do anything. If Part3
after trimming is not one of the allowed values, it throws an exception.
Otherwise it sets the character's gender.
CT_CHAR_SET_NAME
Part2
: character name (case insensitive)Part3
: ignoredPart4
: new character name overrideText
: ignored- FIXUP: none
If the specified character doesn't exist, it doesn't do anything. Otherwise it sets the character's name override.
CT_COMMENT
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
This doesn't do anything.
CT_DEBUGTEXT
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: debug text- FIXUP: none
If the second command line argument is DEBUG
(case sensitively), it prints
Text
to the log.
CT_JAVA_SET_RAGS
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: JavaScript code (double replaced)- FIXUP: none
If the JavaScript code does not return an array, it does nothing. Otherwise it iterates through the returned array:
- non array elements are skipped
- if the element array doesn't have at least 2 items, it throws an exception
- otherwise the element is
[key, value]
- convert them to strings
- if
value
is an empty string, replace it with a space (i.e." "
) - call text replacement function in set mode with
"[" + key + "]
CT_DISPLAYTEXT
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: text- FIXUP: none
Prints Text
to the log.
CT_EXPORTVARIABLE
Part2
: variable name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the specified variable doesn't exist, it doesn't do anything. Otherwise it pops up a save file dialog, and if the user doesn't cancel it, it serializes the full variable state to a file. If the save fails, it displays a message box.
CT_IMPORTVARIABLE
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
Pops up a file load dialog. If the user cancels it, it doesn't do anything. If the load fails, it displays a message box. If there is already a variable with the given name, it is removed, then the imported variable is added.
Note: this means it is possible to create a new variable by importing an unrelated game's exported variable...
CT_PAUSEGAME
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
Prints --------------------------------
(32 hyphens), then pauses the game.
CT_DISPLAYLAYEREDPICTURE
Part2
: file name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
When HideMainPicDisplay
, it doesn't do anything. Otherwise, it sets the
temporary overlay image, it sets it to Part2
if it exist, removes the overlay
otherwise.
Temporary overlay is something that is drawn on top of the base image, but below other overlay images and removed when a different image is displayed. Doesn't work with videos.
Note: base image is set as BackgroundImage
on a PictureBox
, this action
sets the Image
of the same PictureBox
temporarily, Other overlays are drawn
from a paint event handler on top of this. This also has a weird side effect
that gif animations only work when set with this action, and only if the game is
not paused in an action.
CT_DISPLAYPICTURE
Part2
: file name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
What it does depends on the value of HideMainPicDisplay
and UseInlineImages
:
HideMainPicDisplay
andUseInlineImages
: if the file doesn't exist, throw an exception. If it is an image, paste it into the log (without scaling and buggily, so sometimes it will just insert an empty line...). If it is an audio/video, print the full path of the temporary file to the log (where rags extracted the media file)...HideMainPicDisplay
and notUseInlineImages
: it doesn't do anything.- Else: If the file is an image, it sets the main picture (right to the log), including layered images. If it is something else, it switches to a windows media player control and tries to open it. If the file doesn't exist, it still switches to WMP, but doesn't load anything into it.
CT_MM_SET_BACKGROUND_MUSIC
Part2
: file name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the file exists and it is an image, it doesn't do anything. Otherwise it sets the background music volume to zero by decreasing it by 1% every 15ms (so it will take 1.5s, and it blocks the execution while doing this (it looks like it blocks the whole fucking GUI thread, but I'm not 100% sure on this). Then it sets the WMP control to the file if it exists, it unloads whatever was loaded if it doesn't exist.
CT_MM_STOP_BACKGROUND_MUSIC
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
It sets the background music volume to zero in the same way as in CT_MM_SET_BACKGROUND_MUSIC, then it stops the playback. This doesn't unload anything.
CT_MM_PLAY_SOUNDEFFECT
Part2
: file name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the file exists and it is an image, it doesn't do anything. Otherwise it sets
the background sound effect to Part2
if it exists, it unloads it otherwise.
There is no fiddling with the volume like with background music and you can't
play more than one sound effect at the same time.
CT_MM_SET_MAIN_COMPASS
Part2
: file name or<Default>
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If Part2
is <Default>
(case sensitively), it sets the compass image back
to rags' built-in default. Otherwise, it is a (case insensitive) filename. If
the file doesn't exists, or if it is not an image, it doesn't do anything.
Otherwise it sets the compass image (anim gifs and layered images not
supported).
CT_MM_SET_UD_COMPASS
Same as CT_MM_SET_MAIN_COMPASS, except it sets the up-down compass image on success.
CT_LAYEREDIMAGE_ADD
Part2
: base image file name (case insensitive)Part3
: layer file namePart4
: ignoredText
: ignored- FIXUP: none
If the base image specified by Part2
doesn't exist (or it's not an image), it
doesn't do anything. Otherwise it adds Part3
to the end of the layered images
list (so it will be on the top) (even if it doesn't exist, it will simply be
ignored when drawing).
CT_LAYEREDIMAGE_CLEAR
Part2
: base image file name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the base image specified by Part2
doesn't exist (or it's not an image), it
doesn't do anything. Otherwise it clears all layered images.
CT_LAYEREDIMAGE_REMOVE
Part2
: base image file name (case insensitive)Part3
: layer file name (case sensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the base image specified by Part2
doesn't exist (or it's not an image), it
doesn't do anything. Otherwise, remove the first (i.e. most bottom) occurrence of
Part3
from Part2
's layered images.
CT_LAYEREDIMAGE_REPLACE
Part2
: base image file name (case insensitive)Part3
: layer to replace file name (case sensitive)Part4
: layer to add file name (case insensitive)Text
: ignored- FIXUP: none
If the image specified by Part2
or Part4
doesn't exist (or not an image), it
doesn't do anything. Otherwise it removes the first occurrence of Part3
from
Part2
's layers, and if found, add Part4
to the end of the layers.
CT_DISPLAYITEMDESC
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
When Part2
is 00000000-0000-0000-0000-000000000004
: if there is a
action_object
, it displays that object's description, otherwise it does
nothing.
When Part2
is not a special value, try to get an object with than UUID,
failing that name. If it exists, it displays the description, otherwise it does
nothing.
Display description: if notifications are turned off, it just prints the object's description. Otherwise, it prints the object's description, then:
". It is open."
(double space!) if it isOpenable
andOpen
.". It is closed."
(double space!) if it isOpenable
and notOpen
.". It contains:"
(double space!) if it is aContainer
, and (notOpenable
orOpen
), and it is not a portal. Then it prints every visible subobject's preposition + space + name on a new line, where every line except the first is prefixed with"and "
. If there are no visible subobjects, it prints"Nothing"
.
CT_ITEM_LAYERED_REMOVE
Part2
: object UUID or name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the specified object doesn't exist, it doesn't do anything. Otherwise it
iterates over all other Worn
objects in the same location (actually it's
buggy: if the object is not in a character/player, it iterates over all objects
in the same location type). If the two objects share a clothing zone, and if
Part2's level <= other's level
, it fails.
The action collects all failures, and print a message to the log and returns. If
the object is in a character's inventory, the message prefix is: character name
(override ignored!) + " cannot remove "
+ Part2
's display name + ". "
+
character name + " will need to remove "
, otherwise the message prefix is:
"You cannot remove "
+ Part2
's display name + ". You need to remove "
. The
message continues with the display names of all conflicting objects, separated
by " and "
, and the message suffix is " first."
.
If there are no failures, it prints a confirmation message. If the object is in
a character, it prints: character name (override ignored) + " takes off "
+
Part2
's display name + "."
, otherwise it prints "You take off "
+
Part2
's display name + "."
. Finally, it set's Part2
's Worn
to false.
Display name in this case: if preposition not empty, prefix is preposition
+
" "
. If name override after trim is not empty, name override without trim,
otherwise name.
CT_ITEM_LAYERED_WEAR
Same as CT_ITEM_LAYERED_REMOVE, except it sets Worn
to true and the messages are different. You/character_name cannot wear
on fail
and You put on/character_name puts on
on failure.
CT_MOVEITEMTOCHAR
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: character namePart4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the specified object doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise it sets the character's location to be inside Part3
without
any checking (so it is possible to move the object into a non-existing
character).
CT_MOVEITEMTOINV
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the specified object doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise, if the player has enforce weight limit set, checks if the
recursive sum of the carried items + the recursive weight of the new item does
not exceed the limit. If it does, it DISPLAY A FUCKING MESSAGE BOX with the
text "The "
+ object name (override ignored) + " is too heavy to lift at
the moment. unload some stuff first."
(with two spaces before
unload and a lower case character at the beginning of the sentence), then
CANCELS LOOP BREAK and ABORTS THE FUCKING COMMAND LIST.
Otherwise it sets the object location to the player.
CT_MOVEITEMTOOBJ
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: object UUID (can be parsed) or name (case insensitive).Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID. Same withPart3
.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. If Part3
can be parsed as a UUID, set Part2
s location to
Part3
without any checking (so it is possible to move the object into a
non-existing object). Otherwise, it tries to find an object with the name given
in Part3
, if it finds one, it moves Part2
into it, otherwise it does
nothing.
CT_MOVEITEMTOROOM
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
: player's current room00000000-0000-0000-0000-000000000002
: "void" room
Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID. IfPart3
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart3
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Get a room depending on Part3
:
00000000-0000-0000-0000-000000000001
: get the player's current room.00000000-0000-0000-0000-000000000002
: no room (i.e. move the object to nowhere)- can be parsed as a UUID: use the specified UUID directly without any checking (so it is possible to move the object to a non-existing room).
- anything else: find a room with the specified name. If the room doesn't
exists, it DISPLAY A FUCKING MESSAGE BOX (
"Error in command CT_MoveItemToRoom. Could not locate a room called "
+Part3
) and returns.
It sets the object location to the room.
CT_SETOBJECTACTION
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
:action_name-state
Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. If Part3
doesn't contain a hyphen, it throws an exception.
Otherwise everything until the last hyphen is the action_name
. If the
object doesn't have an action with that name (case insensitively), it doesn't
do anything. Otherwise it sets its active flag, to true if state
is Active
(case sensitively), to false otherwise.
CT_ITEM_SET_CUSTOM_PROPERTY
Part2
:object_name:property_name
Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored- FIXUP: none
See for more info on setting custom properties. If
object name is <Self>
(case sensitively, single <>
), it operates on
action_object
, otherwise it finds an object with name object_name
(case
insensitively). If it doesn't exist, it doesn't do anything.
CT_ITEM_SET_CUSTOM_PROPERTY_JS
Same as CT_ITEM_SET_CUSTOM_PROPERTY, but with JavaScript.
CT_SETITEMDESC
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: ignoredPart4
: ignoredText
: new description- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise it sets the object's description to Text
.
CT_ITEM_SET_NAME_OVERRIDE
Part2
: object UUID or name (case insensitive).Part3
: ignoredPart4
: new name overrideText
: ignored- FIXUP: none
It the object specified by Part2
doesn't exists, it does nothing. Otherwise it
sets the object's name override to Part4
.
CT_SETLOCKEDUNLOCKED
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
:Locked
/ anything else (case sensitive)Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise if Part3
is Locked
, it sets the object's Locked
property to true, false otherwise.
CT_SETOPENCLOSED
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
:Open
/ anything else (case sensitive)Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise if Part3
is Open
, it sets the object's Open
property to
true, false otherwise.
CT_SETITEMTOWORN
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
:Worn
/ anything else (case sensitive)Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise if Part3
is Worn
, it sets the object's Worn
property to
true, false otherwise.
CT_ITEM_SET_VISIBILITY
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
:Visible
/ anything else (case sensitive)Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID.
It the object specified by Part2
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise if Part3
is Visible
, it sets the object's Visible
property to true, false otherwise.
CT_ITEMS_MOVE_CONTAINER_TO_CONTAINER
Part2
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part3
: object UUID or name (case insensitive). Special values:00000000-0000-0000-0000-000000000004
:: useaction_object
Part4
: ignoredText
: ignored- FIXUP: none
It the object specified by Part2
or Part3
doesn't exists, or in case of
00000000-0000-0000-0000-000000000004
there is no action_object
, it does
nothing. Otherwise it moves all objects directly in Part2
to Part3
.
CT_LOOP_BREAK
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
Schedules a loop break and aborts the current command list.
CT_CANCELMOVE
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
Sets a flag to cancel the player's current move. This can be called anywhere,
but only has an effect inside <<On Player Leave First Time>>
and <<On Player Leave>>
player actions when executed as a result of the player (the 3DPD one)
selecting a move target on the GUI.
CT_DISPLAYPLAYERDESC
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
Prints the player's description after replacement (without loop_item
) to the
log.
CT_SETLAYEREDPLAYERPORTRAIT
Part2
: file name or<None>
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If Part2
is <None>
(case sensitively), it is treated as an empty string.
This sets the player's overlay image to Part2
without any checking (so it is
possible to set it to a non-existing file, it will be ignored when drawing).
CT_MOVEINVENTORYTOCHAR
Part2
: character namePart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<<Self>>
(case sensitively), replace it with00000000-0000-0000-0000-000000000004
. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replacePart2
with the object's UUID. (This is completely pointless, asPart2
must be a character name, not an object UUID).
Moves every item from the player's inventory to Part2
's inventory without any
checking (so it is possible to move it into a non-existing character).
CT_MOVEINVENTORYTOROOM
Part2
: room's UUID. Special values (not parsed, must match exactly):00000000-0000-0000-0000-000000000001
:: player's current room00000000-0000-0000-0000-000000000002
:: "void" room
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
Moves every item from the player's inventory to the specified location:
00000000-0000-0000-0000-000000000001
: move to the player's current room.00000000-0000-0000-0000-000000000002
: move to nowhere.- anything else: move to the room with the specified UUID without any checking (so it is possible to move items to a not existing room).
CT_MOVEPLAYER
Part2
: room uuid (can be parsed) or name (case insensitive).Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
If Part2
can be parsed as a UUID, find a room with that UUID, else find a
room with that name, and move the player there. If the room doesn't exist, move
the player to nowhere instead (which is a surefire way to fuck up rags and
receive exceptions from everywhere). Otherwise, execute room enter
procedure with actions but without timers.
CT_MOVETOCHAR
Part2
: character name (case insensitive).Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the character doesn't exist, or the character is nowhere or in the same room as the player, it doesn't do anything. Otherwise it moves the player to the same room as the character is in, if it exists, it moves the player to nowhere if it doesn't exist. Finally, if the player is in a room, it executes room enter procedure with actions but without timers.
Note: this action catches any inner exceptions and silently discards them.
CT_MOVETOOBJ
Part2
: object UUID (case insensitive).Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the object doesn't exist, it doesn't do anything. If the object is in a room, moves the player to that room, or nowhere if it doesn't. Finally, if the player is in a room, it executes room enter procedure with actions but without timers.
Note: this action catches any inner exceptions and silently discards them.
Note: unlike CT_MOVETOCHAR, this executes room enter actions when the object is not in a room or it is already in the same room as the player.
CT_PLAYER_SET_CUSTOM_PROPERTY
Part2
:property_name
Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored- FIXUP: none
See for more info on setting custom properties. Since
there is only one player, Part2
is treated as property_name
, even if it
contains colons.
CT_PLAYER_SET_CUSTOM_PROPERTY_JS
Same as CT_PLAYER_SET_CUSTOM_PROPERTY, but with JavaScript.
CT_SETPLAYERACTION
Part2
: ignoredPart3
:action_name-state
Part4
: ignoredText
: ignored- FIXUP: none
If Part3
doesn't contain a hyphen, it throws an exception. Otherwise
everything until the last hyphen is the action_name
. If the player doesn't
have an action with that name (case insensitively), it doesn't do anything.
Otherwise it sets its active flag, to true if state
is Active
(case
sensitively), to false otherwise.
CT_SETPLAYERDESC
Part2
: ignoredPart3
: ignoredPart4
: ignoredText
: new description- FIXUP: none
Set the player's description to Text
.
CT_SETPLAYERGENDER
Part2
:Male
/Female
/Other
(case sensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If Part2
is not one of the allowed values, it throws an exception,
otherwise it sets the player's gender.
CT_SETPLAYERNAME
Part2
: ignoredPart3
: ignoredPart4
: new player name (double replaced)Text
: ignored- FIXUP: none
Sets the player's name.
CT_SETPLAYERPORTRAIT
Part2
: file namePart3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
Sets the player's image to Part2
without any checking (so it is possible to
set it to a non-existing file).
CT_DISPLAYROOMDESCRIPTION
Part2
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
: player's current room
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
If Part2
is 00000000-0000-0000-0000-000000000001
it uses the player's
current room, otherwise it finds a room with the given UUID if it can be
parsed as a UUID, by a name otherwise. If the room doesn't exists it doesn't
do anything, otherwise it prints its description (and just the description, and
not 28472 unrelated things).
CT_DISPLAYROOMPICTURE
Part2
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
: player's current room
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
If Part2
is 00000000-0000-0000-0000-000000000001
it uses the player's
current room, otherwise it finds a room with the given UUID if it can be
parsed as a UUID, by a name otherwise. If the room doesn't exists it doesn't
do anything. What it does depends on the value of HideMainPicDisplay
and
UseInlineImages
:
HideMainPicDisplay
andUseInlineImages
: if the file doesn't exist, throw an exception. If it is an image, paste it into the log (without scaling and buggily, so sometimes it will just insert an empty line...). If it is an audio/video, print the full path of the temporary file to the log (where rags extracted the media file)...HideMainPicDisplay
and notUseInlineImages
: it doesn't do anything.- Else: If the file is an image, it sets the main picture (right to the log), including (single) layered image. If both of them are images, it works correctly. If any of them missing, they're not set. If one is an image and and the other is a video, the last one wins (so overlay beats normal image).
CT_ROOM_MOVE_ITEMS_TO_PLAYER
Part2
: room UUID (case insensitive). Special values (not parsed, must match exactly):00000000-0000-0000-0000-000000000001
: player's current room
Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If Part2
is not a valid UUID, it throws an exception. Otherwise, if it is
00000000-0000-0000-0000-000000000001
uses the player's current room, otherwise
it finds the room with the given UUID. If there is no such room, it doesn't do
anything. Otherwise it moves all Carryable
and Visible
in the room to the
player's inventory.
CT_SETROOMACTION
Part2
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
: player's current room
Part3
:action_name-state
Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
It the room specified by Part2
doesn't exists, it does nothing. If Part3
doesn't contain a hyphen, it throws an exception. Otherwise everything until
the last hyphen is the action_name
. If the room doesn't have an action with
that name (case insensitively), it doesn't do anything. Otherwise it sets its
active flag, to true if state
is Active
(case sensitively), to false
otherwise.
CT_ROOM_SET_CUSTOM_PROPERTY
Part2
:room_name:property_name
Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored- FIXUP: none
See for more info on setting custom properties. If room
name is <CurrentRoom>
(case sensitively), it operates on the player's
current room, otherwise it finds a room with name room_name
(case
insensitively). If it doesn't exist, it doesn't do anything.
CT_ROOM_SET_CUSTOM_PROPERTY_JS
Same as CT_ROOM_SET_CUSTOM_PROPERTY, but with JavaScript.
CT_SETROOMDESCRIPTION
Part2
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
: player's current room
Part3
: ignoredPart4
: ignoredText
: new description- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
If Part2
is 00000000-0000-0000-0000-000000000001
, it operates on the
player's current room, otherwise if it can be parsed as a UUID, it finds a
room with that UUID, else it find a room with that name. If the specified room
doesn't exist, it doesn't do anything, otherwise it sets the room's description.
CT_SETEXIT
Part2
: room UUID (can be parsed) or name (case insensitive).Part3
:direction-active
(case sensitive)Part4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID. (Note: the action doesn't hande these values...)
If Part2
can be parsed as a UUID, it finds a room with that UUID, else it
find a room with that name. If the specified room doesn't exist, it doesn't do
anything. If Part3
doesn't contain a hyphen, it throws an exception,
otherwise everything until the first hyphen is the direction
. If there is a
second hyphen, it and everything after it is ignored. active
is trimmed of
white-space, but direction
is not (unlike other enum parsing places...).
If direction
does not refer to a valid direction (North
, South
,
NorthEast
, ...), it doesn't do anything. Otherwise it sets its Active
status, to true if active
is Active
, false otherwise.
CT_SETEXITDESTINATION
Part2
: room UUID (can be parsed) or name (case insensitive).Part3
: direction (case sensitive)Part4
:<None>
or destination room UUID (can be parsed) or nameText
: ignored- FIXUP: none
If Part2
can be parsed as a UUID, it finds a room with that UUID, else it
find a room with that name. If the specified room doesn't exist, it doesn't do
anything. If direction
does not refer to a valid direction (without trimming),
it doesn't do anything.
If Part4
is <None>
(case sensitively), it sets the exit's destination to
nowhere and sets its Active
flag to false. Otherwise, if Part4
can be
parsed as a UUID, it finds a room with that UUID, otherwise it finds a room
with that name. If the room does not exists, it does nothing, otherwise it sets
the exit's destination to the room and the exit's Active
status to true.
CT_ROOM_SET_NAME_OVERRIDE
Part2
: room UUID (can be parsed) or name (case insensitive).Part3
: ignoredPart4
: name overrideText
: ignored- FIXUP: none
If Part2
can be parsed as a UUID, it finds a room with that UUID, else it
find a room with that name. If the specified room doesn't exist, it doesn't do
anything, otherwise it sets the room's name override.
CT_SETROOMLAYEREDPIC
Part2
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
:: player's current room
Part3
: file namePart4
: ignoredText
: ignored- FIXUP: none
If Part2
is 00000000-0000-0000-0000-000000000001
, it operates on the
player's current room, otherwise if it can be parsed as a UUID, it finds a
room with that UUID, else it find a room with that name. If the specified room
doesn't exist, it doesn't do anything.
If Part3
is <None>
, it is treated as empty string, then it sets the room's
overlay image to Part3
without any checking (so it can be set to a
non-existing image).
If the room is the current player's room, and HideMainPicDisplay
is false, it
updates the main image and room image, but without handling videos.
CT_SETROOMPIC
Part2
: room UUID (can be parsed) or name (case insensitive). Special values:00000000-0000-0000-0000-000000000001
:: player's current room
Part3
: file namePart4
: ignoredText
: ignored- FIXUP: if
Part2
is<CurrentRoom>
(case sensitively), replace it with00000000-0000-0000-0000-000000000001
. IfPart2
is<Void>
, replace it with00000000-0000-0000-0000-000000000002
. Otherwise, if it can't be parsed as an UUID and there is a room with that name, replace it with the room's UUID.
If Part2
is 00000000-0000-0000-0000-000000000001
, it operates on the
player's current room, otherwise if it can be parsed as a UUID, it finds a
room with that UUID, else it find a room with that name. If the specified room
doesn't exist, it doesn't do anything, otherwise it sets the room's image
without any checking (so it can be set to a non-existing image).
If the room is the current player's room, it tries to redisplay the room image:
- If
HideMainPicDisplay
andUseInlineImages
: paste the room image into the log with the usual bugs. - If
HideMainPicDisplay
: no further actions. - Else: it updates the main image (but not the room image), without handling videos.
CT_Status_ItemVisibleInvisible
Part2
: status bar item name (case insensitive)Part3
:Visible
/ anything else (case insensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the status bar item specified by Part2
doesn't exist, it doesn't do
anything. Otherwise it sets its Visible
flag, to true if Part3
is Visible
,
to false otherwise.
CT_EXECUTETIMER
Part2
: timer name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the timer with the given name doesn't exist, or it doesn't have an action
with name <<On Each Turn>>
, it doesn't do anything. Otherwise it executes the
action (without an action_object
), repeating it as many times as it is reset.
CT_RESETTIMER
Part2
: timer name (case insensitive)Part3
: ignoredPart4
: ignoredText
: ignored- FIXUP: none
If the timer with the given name doesn't exist, it doesn't do anything.
Otherwise it sets the timer's TurnNumber
to zero. If Part2
is the currently
executing timer, it queues a reset (which will cause
CT_EXECUTETIMER to execute it again, or when normally
executing timers, it will skip subsequent On ... Turn actions), otherwise it
clears the queued reset. In any case it CANCELS LOOP BREAK and ABORTS
THE FUCKING COMMAND LIST.
CT_TIMER_SET_CUSTOM_PROPERTY
Part2
:timer_name:property_name
(case insensitive)Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored- FIXUP: none
See for more info on setting custom properties. It finds
a timer with name timer_name
(case insensitively). If it doesn't exist, it
doesn't do anything.
CT_TIMER_SET_CUSTOM_PROPERTY_JS
Same as CT_TIMER_SET_CUSTOM_PROPERTY, but with JavaScript.
CT_SETTIMER
Part2
: timer name (case insensitive)Part3
:Active
/ anything else (case insensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the timer with the given name doesn't exist, it doesn't do anything.
Otherwise it sets the timer's Active
flag, to true if Part3
is Active
, to
false if it is anything else.
CT_DISPLAYVARIABLE
Part2
: variable name + optional indices (case insensitive)Part3
:Display Date & Time
/Display Date Only
/Display Time Only
/Display Weekday Only
(case sensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the variable specified by Part2
doesn't exist, it doesn't do anything. Here
is what happens, when the number of indices specified doesn't match the variable
type:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | single val | single val | single val |
1D | nothing | value | exception |
Num/Str 2D | nothing | System.Collections.ArrayList |
value |
DT 2D | nothing | exception | value |
(Array indices are completely ignored with non-array variables.) Out-of-range accesses generate exceptions except in "single val" and "nothing" cells.
In case of number or string variables, Part3
is ignored, they're converted to
string normally and printed. With DateTime variables it specifies a date format:
Display Date & Time
:dddd, MMMM, dd yyyy hh:mm:ss tt
, except in case of 2D arrays, where it just prints whatever string representation the date is stored in (so it doesn't throw if you only specify one index, it printsSystem.Collections.ArrayList
).Display Date Only
:dddd, MMMM, dd yyyy
Display Time Only
:hh:mm:ss tt
Display Weekday Only
:dddd
IfPart3
is not one of the allowed values, it doesn't print anything.
CT_SETVARIABLE
Part2
: variable name + optional indices (case insensitive)Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
/Add Days
/Add Hours
/Add Minutes
/Add Seconds
/Subtract Days
/Subtract Hours
/Subtract Minutes
/Subtract Seconds
/Set Day Of Month To
/Set Hours To
/Set Minutes To
/Set Seconds To
(case sensitive)Part4
: value (double replaced)Text
: also value (double replaced)- FIXUP: none
If Part2
contains the string Array:
(anywhere, case sensitive), the
first 6 characters of Part2
is removed and it will be an array set, otherwise
a normal set. The rest is parsed for variable name and indices as usual, if the
variable doesn't exists, this action does nothing.
In case of a JavaScript set
(CT_VARIABLE_SET_JAVASCRIPT), it runs Part4
through the JavaScript interpreter (after double replace), and converts the
result to string.
Here is what happens generally when the number of indices doesn't match, but watch out for exceptions below.
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | set single | ignore/exception | ignore/exception |
1D | set single | set | exception |
2D | set single | fuckup | set |
- ignore/exception: this means that the code will try to overwrite the array part of the variable, which depending on what leftover garbage is there, might actually work, but you're more likely to get an exception.
- fuckup: in this case you'll end up with an array where a row is replaced by a string/number/dt, and... you can expect things to break left and right if you do this.
With number and datetime variables, array set is only working when Part3
is
Equals
, but for string variables Part3
is ignored. An array set is
only working with JS set, without JS it just clears the variable (it will be 0
rows, indefinite columns, it leaves single value alone).
With a normal set, with a number variable, if Part4
is not a number, it
throws an exception, otherwise it just sets the specified cell with the
error handling above. In case of Add
, Subtract
, Multiply
and Divide
only
normal sets are possible, Array:
is ignored and fuckup changes into
exception. If Part3
has any other value, the value is not updated.
If the variable has EnforceRestrictions
, the set (or not set in case Part3
is invalid...) value is validated. If it is smaller than replaced and converted
to double min, it is set to min, and if after it is larger than max, it is set
to max (if the result of replacement is not a number, it throws an
exception). In case of Equals
and fuckup, this will end up in an
exception, but only after fucking up the variable...
In case of string variables, Part3
is ignored (only simple settings is
supported) and the value is read from Text
instead of Part4
(Text
is also
evaluated with JS, but only with string variables). Array set and normal set is
supported.
DateTime works similarly to numbers, except: every possible value of Part3
is
supported except Add
, Subtract
, Multiply
, Divide
, and there are no
min-max checks. Equals
expects a date time, parsed by .NET's super permissive
parser, everything else an int32, if the conversion fails it throws an
exception.
CT_ITEM_GETRANDOMGROUP
Part2
: variable name (without indices!) (case insensitive)Part3
: group name (case insensitive)Part4
: ignoredText
: ignored- FIXUP: none
If the variable specified by Part2
doesn't exist, it doesn't do anything.
This operates directly on SQL, not the in-memory structures (that gets
serialized into savegames). If Part3
doesn't name a valid object group, it is
treated as being empty. Otherwise it collects all objects recursively in the
object group.
It sets the variable's single string value (even if it is not a string variable) to the name of a random object in this set, or to the empty string if the object group is empty.
CT_MM_GETRANDOMGROUP
Part2
: variable name (without indices!) (case insensitive)Part3
: group name (case insensitive)Part4
: ignoredText
: ignored- FIXUP: none
Exactly the same as CT_ITEM_GETRANDOMGROUP, except it gets a random file name from a file group.
CT_VARIABLE_SET_JAVASCRIPT
See CT_SETVARIABLE for details. I'm only going to document how JavaScript arrays are turned into rags variable arrays:
If the returned value is not a JavaScript array, the result is an empty array. Otherwise each item of the array is converted as follows:
- it is not an array and variable type is number: the value is converted to a
string, then to a double. If any of this fails, the value is
-1
. - it is not an array and variable type is string: the value is converted to a string. If this fails, the exception is quietly swallowed and it leaves the array in a half-set state.
- it is not an array and variable type is DateTime: it is converted to a string,
then parsed with .net's super permissive parser. If this fails somehow, it is
set to
0001-01-01 00:00:00
unspecified timezone (can be treated as local). - it is an array: if it is an empty array, it is skipped, otherwise each item of this inner array is simply converted to a string. (2D arrays are always stored as strings in rags.) (If it is not a 2D array, fuckup.)
CT_SETVARIABLEBYINPUT
Part2
: input type:Text
/Characters
/Characters And Objects
/Objects
/Custom
/Inventory
(case sensitive)Part3
: variable name + optional indices (case insensitive)Part4
: custom choice title (double replaced)Text
: ignoredEnhancedData
: one of the few functions that use it- FIXUP: none
If input type is not one of the allowed values, it is treated as being None
(which means an empty selection list. Interestingly, without ovelays, you can
still click on OK even with zero items, overlay forces a cancel button in this
case). In case of a custom choice, custom values are taken from EnhancedData
,
after replacement, otherwise the same as with normal action input.
When using overlay input, the title is taken from Part4
, or if it empty after
replace, it is Please make a selection:
. Without overlay, it is always taken
from Part4
, even if it is empty. Non overlay query is never cancelable, while
overlay input is cancelable if the list is empty or EnhancedData.AllowCancel
.
Without overlay, it never uses tags, with overlay it's same as the normal action. There's no post-processing of the selected tag. If the selection is canceled, it CANCELS LOOP BREAK and ABORTS THE FUCKING COMMAND LIST.
If the variable does not exists, or if input type is None
, it doesn't do
anything (but it only checks this after the user made a selection...). This
action expects the variable to be a string, it sets single and array string
values without checking, ending up in ignore/fuckup cases when done. With Text
input, the selection is simply stored. Otherwise, first the single value is set
to an empty string, and if the selection is not an empty string, the normal
value is set. Here is what happens when the number of indices doesn't match the
variable type:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | set single | ignore/exception | ignore/exception |
1D | set single | set | exception |
2D | set single | fuckup | set |
CT_SETVARIABLE_NUMERIC_BYINPUT
Part2
: input type:Text
/Custom
(case sensitive)Part3
: variable name + optional indices (case insensitive)Part4
: custom choice title (double replaced)Text
: ignoredEnhancedData
: one of the few functions that use it- FIXUP: none
If input type is not one of the allowed values, it is treated as being None
.
Largely the same as CT_SETVARIABLEBYINPUT, except it
shows a Sorry, you must enter a valid number.
message with Text
input when
you enter an invalid input (custom choices are not checked, you get an
exception when the code actually tries to set the value.
It sets the variable's number fields (with a possibility to fuckup), except
that it also handles EnforceRestrictions
.
CT_VARIABLE_SET_CUSTOM_PROPERTY
Part2
:variable_name:property_name
(case insensitive)Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
(case sensitive)Part4
: value (double replaced)Text
: ignored- FIXUP: none
See for more info on setting custom properties. It finds
a variable with name variable_name
(case insensitively, without indices). If
it doesn't exist, it doesn't do anything.
CT_VARIABLE_SET_CUSTOM_PROPERTY_JS
Same as CT_VARIABLE_SET_CUSTOM_PROPERTY, but with JavaScript.
CT_VARIABLE_SET_RANDOMLY
Step2
: variable name + optional indices (case insensitive)Step3
: ignoredStep4
: ignoredText
: ignored- FIXUP: none
If the variable doesn't exist, it doesn't do anything. It will always set number
fields, so using this on a non-number variable allows a fuckup. It generates
an integer random number between [int32(double(min)), int32(double(max))+1)
(string is first converted to double, then converted to int32 by rounding to
zero). Throws an error if the upper bound is smaller than the lower, returns min
when they're equal. Here is what happens when the number of indices doesn't
match the variable type:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | set single | ignore/exception | ignore/exception |
1D | set single | set | exception |
2D | set single | fuckup | set |
CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE
Step2
: variable name + optional indices (case insensitive)Step3
:character_name:property_name
Step4
: ignored (double replaced)Text
: ignored- FIXUP: none
If the specified variable doesn't exist, it doesn't do anything. If Step3
doesn't exactly have one colon, or the character (case insensitively) doesn't
exist, or the custom property (case sensitively) doesn't exist, it is
treated as being an empty string. If the variable is a DateTime variable, it
doesn't do anything.
Here is what happens when the number of indices doesn't match the variable type:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | set single | ignore/exception | ignore/exception |
1D | set single | set | exception |
2D | set single | fuckup | set |
In case of a number variable, if it is a "set single", the string value is converted to a double, if it fails it prints some kind of debug log and does nothing. In case of arrays, it just stores the string into the array (even in case of an 1D array), for random fuckups in the future.
CT_VARIABLE_SET_WITH_ITEMPROPERTYVALUE
Step2
: variable name + optional indices (case insensitive)Step3
:object_name:property_name
Step4
: ignored (double replaced)Text
: ignored- FIXUP: none
Copy-paste of
CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE,
but with objects instead of characters. Additionally, object_name
can be
<Self>
(case sensitive) to mean action_object
if it is specified, it
uses empty string if it is not specified.
CT_VARIABLE_SET_WITH_PLAYERPROPERTYVALUE
Step2
: variable name + optional indices (case insensitive)Step3
:property_name
(case sensitive)Step4
: ignored (double replaced)Text
: ignored- FIXUP: none
Copy-paste of
CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE,
but with player instead of characters. Step3
is treated as a property name,
even if it contains colons.
CT_VARIABLE_SET_WITH_ROOMPROPERTYVALUE
Step2
: variable name + optional indices (case insensitive)Step3
:room_name:property_name
Step4
: ignored (double replaced)Text
: ignored- FIXUP: none
Copy-paste of
CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE,
but with rooms instead of characters. Additionally, room_name
can be
<CurrentRoom>
(case sensitively) to mean the player's current room.
CT_VARIABLE_SET_WITH_TIMERPROPERTYVALUE
Step2
: variable name + optional indices (case insensitive)Step3
:timer_name:property_name
Step4
: ignored (double replaced)Text
: ignored- FIXUP: none
Copy-paste of CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE, but with timers instead of characters.
CT_VARIABLE_SET_WITH_VARIABLE
Step2
: variable to set name + optional indices (case insensitive)Part3
:Equals
/Add
/Subtract
/Multiply
/Divide
/Add Days
/Add Hours
/Add Minutes
/Add Seconds
/Subtract Days
/Subtract Hours
/Subtract Minutes
/Subtract Seconds
/Set Day Of Month To
/Set Hours To
/Set Minutes To
/Set Seconds To
(case sensitive)Step4
: variable to read name + optional indices (case insensitive)Text
: ignored
If the variable specified by Step4
doesn't exist, it throws an exception.
Here is what happens when the number of indices doesn't match the variable type:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | single val | garbage/exception | garbage/exception |
1D | single val | OK | exception |
2D | single val | exception | OK |
The variable is also treated as a number variable, even if not: in case of single values, this will read garbage, with 1D it throws an exception, and with 2D it tries to parse the stored string as a number and throws an excepton if it fails.
With two indices, if the value is not an integer, it throws an exception. If the value can not be represented as an int32 (after tuncation), it also throws an exception.
If the variable specified by Step2
doesn't exist, or it is a string variable,
it doesn't do anything.
Here is what happens generally when the number of indices doesn't match with
Step2
:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | set single | ignore/exception | ignore/exception |
1D | set single | set | exception |
2D | set single | fuckup | set |
With number variables, Step4
is treated as a double, except with 2D arrays and
Equal
, or single values and Divide
, where it is treated as an integer. If
Part2
is not Equals
, Add
, Subtract
, Multiply
or Divide
, it doesn't
update the variable. EnforceRestrictions
are handled on number variables, even
if there was no update.
DateTime with Equals
is a buggy piece of shit. With not 2D arrays, it just
throws an exception, with 2D arrays, it sets the the number converted to
string, which rags later won't be able to parse back (so it's a kind of
fuckup). The rest date-related operations actually work though, but they
operate on the truncated integers (so you can't add 1.5 days to a DT).
CT_VARIABLE_SET_WITH_VARIABLEPROPERTYVALUE
Step2
: variable name + optional indices (case insensitive)Step3
:timer_name:property_name
Step4
: ignored (double replaced)Text
: ignored- FIXUP: none
Copy-paste of CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE, but with variables instead of characters.
CT_ENDGAME
Step2
: ignoredStep3
: ignoredStep4
: ignoredText
: display text- FIXUP: none
Prints Text
to the log and ends the game. It also displays a dialog box
The game has ended. Would you like to play again?
(with two
spaces between the sentences) and buttons Restart
, Load
and No
. Unless
selecting Restart
, the remaining commands will be run.
Condition
It has two subtypes, loop and if.
Loop
- It has a single check,
CT_Loop_*
PassCommands
will be called for each iteration, settingloop_item
FailCommands
are ignoredCT_LOOP_BREAK
can be used to break out of the innermost loop. However, if break is inside a Condition'sPassCommands
orFailCommads
, and there are subsequent Commands in the same list, some later commands can cancel the break. See the individual command documentations.
If
- Normally it has one or more checks, neither of them being
CT_Loop_*
- Rags designer doesn't allow you to create a condition with zero checks, but if you manage to create one it will be regarded as true.
- However you can create an if with loops inside, if you want a bugfest: first,
the loop body will be executed when the check is evaluated. Second, they don't
modify the variable used to store the return value, so their "value" will be
whatever before them produced (or true if they're the first in the list).
Third, since they're not loops,
PassCommands
orFailCommands
will be executed after them, but withoutloop_item
. For example, having an if with "loop exits and loop exits", PassCommands will be executed 25 times (unless it breaks): 12 times with the exits of the first room, 12 times with the exits of the second room, then one more time withoutloop_item
. PassCommands
orFailCommands
are executed depending on the check- Handling multiple checks: every check has a
CkType
, but it's ignored for the first check. For the rest, they kinda also short-circuit:Or
check following a true: result is true, no other checks are executed.And
check following a false: executing theAnd
check is skipped, but subsequent checks are still executed This (I think) corresponds toAnd
having higher precedence, so a list ofIgnored, A
,And, B
,Or, C
,And, D
is parsed as(A && B) || (C && D)
.
Generic check info
Step2
, Step3
and Step4
values are always run through the text replacement
function before usage.
Note for X in Y checks: rags store the location of the objects/characters by string, so it's possible for an object/character to be in a room/object/character that doesn't exists. Unfortunately this information is lost during import to scraps.
CustomPropertyChecks
Step2
: colon separated pair:name:custom_property_name
Step3
:Equals
/Not Equals
/Contains
/Greater Than
/Greater Than or Equals
/Less Than
/Less Than or Equals
(case sensitive)Step4
: value to compare with. Double replaced!
Interpretation of name
varies, but generally if Step2
doesn't have exactly
one colon, or the named entity doesn't exists, or it doesn't have a custom
property named custom_property_name
(case insensitively), the check
doesn't return anything. If Step3
is not one of the allowed values, it
returns true.
If both the custom property's value and Step4
can be converted to a double,
they're treated as numbers otherwise as strings, except Contains
which is
always string based. (This means that for example 012
equals to 12
but
012x
doesn't equal to 12x
.) String compare is REVERSED and case
insensitive (i.e. 1 < 2
but 1x > 2x
). Returns the result of the comparison.
Available ConditionTypes
CT_Uninitialized
Not exactly a valid check, Rags will treat it as any other invalid option: don't return anything.
CT_AdditionalDataCheck
Step2
: object/character name to compare with (case insensitive)Step3
: ignoredStep4
: text value to compare with (case insensitive)
Checks the value selected at the beginning of the action. If InputType
is
Text
, it compares the entered string with Step4
(Step2
is ignored in this
case). Otherwise, Step4
is ignored and:
- If selection and
Step2
equals, return true. - If there is an object named selection, returns
object.uuid == Step2
. - If there is a character named selection, returns
character.ToString() == Step2
, whereToString
isName
ifNameOverride
is not empty, elseName
if we're outside a game (?), elseNameOverride
with text replacements (!!). - If there is an object uuid matching the selection, returns
object.name == Step2
. - Otherwise returns false.
CT_Character_CustomPropertyCheck
Step2
:character_name:custom_property_name
(case insensitive)Step3
: comparison typeStep4
: value
See for more info on CustomPropertyChecks.
character_name
is a name of a character.
CT_Character_Gender
Step2
: character's name (case insensitive)Step3
:Male
/Female
/ anything else (case sensitive)Step4
: ignored
If the character doesn't exist, doesn't return anything. If Step3
is
neither Male
nor Female
, it's treated as Other
. Returns whether the
character's gender equals to Step3
.
CT_Character_In_Room
Step2
: character's name (case insensitive)Step3
: room's uuid (case sensitive). Special values (not parsed, must match exactly):00000000-0000-0000-0000-000000000001
:: player's current room00000000-0000-0000-0000-000000000002
:: "void" room
Step4
: ignored
If the character doesn't exists, throws an exception. Returns whether the character is currently in the specified room (see special values above).
CT_Character_In_RoomGroup
Step2
: character's name (case insensitive)Step3
: room group orNone
(case sensitive)Step4
: ignored
If the character doesn't exists, throws an exception. If the character is
not in a room, returns false. Returns whether character's current room's group's
name equals to Step3
(use None
to check if the room is not in any group).
Note: in rags, room groups are not recursive, but they are in scraps. To be
consistent with CT_Item_InGroup
and CT_MultiMedia_InGroup
, scraps does a
recursive check here aswell.
CT_Item_CustomPropertyCheck
Step2
:object_name:custom_property_name
(case insensitive)Step3
: comparison typeStep4
: value
See for more info on CustomPropertyChecks. If
object_name
is <Self>
(case sensitively), it refers to the hidden object
parameter, otherwise it is an object's name (case insensitively).
CT_Item_InGroup
Step2
: name of the object (case insensitive)Step3
: group name (case sensitive)Step4
: ignored
This is buggy in designer! Rags designer puts the object's uuid into
Step2
, but the player expects an object name. To fix it, you have to type
the object name into the Choose Item
box, and not select from the drop-down
menu.
This operates directly on SQL, not the in-memory structures (that gets
serialized into savegames). If there is no such object in the SQL, it is treated
as having an empty group name. If the object's group name equals to Step3
,
returns true. Otherwise it gets the children of the specified group and
recursively checks all of them (same as getting the parent of the object group,
and recursively checking the parent groups, except much more inefficient).
Returns false if not found.
CT_Item_Held_By_Character
Step2
: character name (case sensitive)Step3
: object UUID or name (case insensitive)Step4
: ignored- FIXUP: if
Step3
can't be parsed as an UUID, and there is an object with that name, replaceStep3
with the object's UUID.
If the object doesn't exist, returns false. Otherwise returns whether the object's location is directly a character with the specified name (not subobject).
CT_Item_Held_By_Player
Step2
: object UUID or name (case insensitive)Step3
: ignoredStep4
: ignored- FIXUP: if
Step2
can't be parsed as an UUID, and there is an object with that name, replaceStep2
with the object's UUID.
If the object doesn't exist, returns false. Otherwise returns whether the object's location is a player with the specified name or it is recursively inside an object held by the player.
CT_Item_In_Object
Step2
: object UUID or name (case insensitive)Step3
: other object's UUID (case sensitive)Step4
: ignored- FIXUP: if
Step2
can't be parsed as an UUID, and there is an object with that name, replaceStep2
with the object's UUID.
If the object specified by Step2
doesn't exists, return false. Otherwise
returns whether Step2
is directly inside Step3
.
CT_Item_In_Room
Step2
: object UUID or name (case insensitive)Step3
: room UUID (case sensitive). Special values (not parsed, must match exactly):00000000-0000-0000-0000-000000000001
:: player's current room
Step4
: ignored- FIXUP: if
Step2
can't be parsed as an UUID, and there is an object with that name, replaceStep2
with the object's UUID. Same withStep3
and room.
If the object doesn't exists, returns false. Otherwise returns whether the object is currently in the specified room (see special values above) directly (i.e. not subobjects).
CT_Item_In_RoomGroup
Step2
: object UUID (case insensitive)Step3
: room group orNone
(case sensitive)Step4
: ignored
If the object doesn't exists or it is not in a room, returns false. Returns
whether the object's current room's group's name equals to Step3
.
See also CT_Character_In_RoomGroup.
CT_Item_Not_Held_By_Player
Step2
: object UUID or name (case insensitive)Step3
: ignoredStep4
: ignored- FIXUP: if
Step2
can't be parsed as an UUID, and there is an object with that name, replaceStep2
with the object's UUID.
If the object doesn't exists, return false. Otherwise returns whether the object is not in the player's inventory DIRECTLY. Consistency, where the fuck are you!?
CT_Item_Not_In_Object
Step2
: object UUID or name (case insensitive)Step3
: other object's UUID (case sensitive)Step4
: ignored
If the object specified by Step2
doesn't exists, return false. Otherwise
returns whether Step2
is not directly inside Step3
.
CT_Item_State_Check
Step2
: object UUID or name (case insensitive)Step3
:Open
/Closed
/Locked
/Unlocked
/Worn
/Removed
/Read
/Unread
/Visible
/Invisible
(case sensitive)Step4
: ignored- FIXUP: if
Step2
can't be parsed as an UUID, and there is an object with that name, replaceStep2
with the object's UUID.
If the object doesn't exists, it returns false. Otherwise, if Step3
is not one
of the allowed values, it doesn't return anything. Otherwise, returns
whether the object is in the specified state. (Every second item in the list is
the negation of the previous one, so Closed
is checking for not Open
,
Removed
is checking for not Worn
, etc.)
CT_Loop_While
Parameters are the same as CT_Variable_Comparison,
execute PassCommands
until variable comparison is true. loop_item
is passed
through unmodified.
CT_Loop_Characters
Step2
: ignoredStep3
: ignoredStep4
: ignored
Iterate through each character in the game (skipping player, since player is not
a character in rags), setting loop_item
.
CT_Loop_Items
Step2
: ignoredStep3
: ignoredStep4
: ignored
Iterate through each object in the game, setting loop_item
.
CT_Loop_Item_Group
Step2
: object group name (case sensitive)Step3
: ignoredStep4
: ignored
Iterate through each object in the game. If the object is in the specified
object group directly, call PassCommands
setting loop_item
.
CT_Loop_Item_Container
Step2
: object UUID (case sensitive)Step3
: ignoredStep4
: ignored
Iterate through each object in the game. If the object is in the specified
object, call PassCommands
setting loop_item
.
CT_Loop_Item_Room
Step2
: room UUID (case sensitive)Step3
: ignoredStep4
: ignored
Iterate through each object in the game. If the object is in the specified
room, call PassCommands
setting loop_item
.
CT_Loop_Item_Inventory
Step2
: ignoredStep3
: ignoredStep4
: ignored
Iterate through each object in the game. If the object is in the player's
inventory, call PassCommands
setting loop_item
.
CT_Loop_Item_Char_Inventory
Step2
: character name (case sensitive)Step3
: ignoredStep4
: ignored
Iterate through each object in the game. If the object is in the specified
character's inventory, call PassCommands
setting loop_item
.
CT_Loop_Rooms
Step2
: ignoredStep3
: ignoredStep4
: ignored
Iterate through each room in the game, setting loop_item
.
CT_Loop_Exits
Step2
: room UUID (can be parsed) or name (case insensitive)Step3
: ignoredStep4
: ignored
If the specified room doesn't exists, doesn't do anything. Otherwise call
PassCommands
settings loop_item
with each exit of the room (including
disabled ones, i.e. the loop will be executed exactly 12 times, unless broken).
CT_MultiMedia_InGroup
Step2
: name of the object (case insensitive)Step3
: group name (case sensitive)Step4
: ignored
Works similarly to CT_Item_InGroup.
CT_Player_CustomPropertyCheck
Step2
:custom_property_name
Step3
: comparison typeStep4
: value
See for more info on CustomPropertyChecks. Since there
is only one player in rags, Step2
only contains a custom property name. Colons
are treated as part of the property name!
CT_Player_Gender
Step2
:Male
/Female
/ anything else (case sensitive)Step3
: ignoredStep4
: ignored
If Step2
is neither Male
nor Female
, it's treated as Other
. Returns
whether the player's gender equals to Step2
.
CT_Player_In_Room
Step2
: room's UUID (case sensitive). Special values (not parsed, must match exactly):00000000-0000-0000-0000-000000000002
:: "void" room (crashes rags if you actually manage to put the player here, so probably can be ignored)
Step3
: ignoredStep4
: ignored- FIXUP: if
Step2
can't be parsed as an UUID, and there is a room with that name, replaceStep2
with the room's UUID.
Returns whether the player is currently in the specified room.
CT_Player_In_RoomGroup
Step2
: room group orNone
(case sensitive)Step3
: ignoredStep4
: ignored
(If the player is not in a room, throws an exception.) Returns whether player's
current room's group's name equals to Step2
.
See also CT_Character_In_RoomGroup.
CT_Player_In_Same_Room_As
Step2
: character name (case insensitive)Step3
: ignoredStep4
: ignored
If the character doesn't exists, throws an exception. Returns whether the player and the specified character is in the same room.
CT_Player_Moving
Step2
:Empty
/North
/South
/ ... (case sensitive)Step3
: ignoredStep4
: ignored
If Step2
is not a valid direction, throws an exception. Otherwise checks
whether the player currently moves in the specified direction (only in room's on
player leave events, otherwise the player is moving in the Empty
direction).
CT_Room_CustomPropertyCheck
Step2
:room_uuid:custom_property_name
(case insensitive)Step3
: comparison typeStep4
: value
See for more info on CustomPropertyChecks. room_uuid
can be <CurrentRoom>
(case sensitively!) to mean the player's room,
otherwise it is a room uuid.
CT_Timer_CustomPropertyCheck
Step2
:timer_name:custom_property_name
(case insensitive)Step3
: comparison typeStep4
: value
See for more info on CustomPropertyChecks. timer_name
is a name of a timer.
CT_Variable_Comparison
Step2
: variable name + optional indices (case insensitive)Step3
:Equals
/Not Equals
/Greater Than
/Greater Than or Equals
/Less Than
/Less Than or Equals
/Contains
/DayOfWeek Is
/Hour Equals
/Hour Is Greater Than
/Hour Is Less Than
/Minute Equals
/Minute Is Greater Than
/Minute Is Less Than
/Seconds Equals
/Seconds Is Greater Than
/Seconds Is Less Than
(case sensitive)Step4
: value to compare with. Double replaced!
Optional index handling: everything before the first (
is the variable name,
without trimming. (No (
-> full string is the variable name.) There is a (
and a )
afterwards: everything between them is converted to int32, if fails
treat as no index. There is a second (
and a )
afterwards: do the same
conversion. Examples: foo(0)(0)
-> name "foo"
, indices 0 and 0. foo((2)
->
name "foo "
, indices invalid and 2.
If the variable does not exists, returns true. Here is what happens when the number of indices specified doesn't match the variable type (invalid first and valid second indices: 2 idx, but later exception due to out of range):
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Num single | OK | garbage/exception | garbage/exception |
Num 1D | garbage | OK | exception |
Num 2D | garbage | exception | OK |
Str single | OK | garbage/exception | garbage/exception |
Str 1D | garbage | OK | exception |
Str 2D | garbage | System.Collections.ArrayList |
OK |
DT single | OK | garbage/exception | garbage/exception |
DT 1D | garbage | OK | exception |
DT 2D | garbage | exception | OK |
Explanations:
- Garbage in 0 idx column: rags doesn't store variable values in a union, and selecting a different type in rags designer doesn't clear out the values stored for other types. It also treats array and single as separate types. Thus when not specifying any index, rags will use this single type's value with whatever garbage value left behind. Scraps emulates this behavior.
- garbage/exception: similar to the previous. If the variable happen to have a garbage array data, it will use it, otherwise exception. Scraps always throws an exception in this case.
System.Collections.ArrayList
: rags will treat the variable as having this value.
If the index is out of range, it throws an exception. If Step2
has an
index, the value used to store single number values (refer to garbage in 0 idx
column above) is overwritten with the current array cell value. (Unlike
CT_Variable_To_Variable_Comparison
, this is done for every type, not just
numbers).
If an unknown/invalid comparison type is specified, it returns true. Different variable types support an arbitrary subset of the possible comparisons (O=supported, X=not supported):
Comparison | Num | Str | DT |
---|---|---|---|
Equals |
O | O | O |
Not Equals |
O | O | O |
Greater Than |
O | O | O |
Greater Than or Equals |
O | X | O |
Less Than |
O | O | O |
Less Than or Equals |
O | X | O |
Contains |
X | O | X |
DayOfWeek Is |
X | X | O |
Hour Equals |
X | X | O |
Hour Is Greater Than |
X | X | O |
Hour Is Less Than |
X | X | O |
Minute Equals |
X | X | O |
Minute Is Greater Than |
X | X | O |
Minute Is Less Than |
X | X | O |
Seconds Equals |
X | X | O |
Seconds Is Greater Than |
X | X | O |
Seconds Is Less Than |
X | X | O |
In case of a number variable, Step4
is converted to a double, if it fails it
throws an exception. In case of a string variable, formatting macros are
stripped from the variable's value (see text.md for
details), but not from Step4
. Comparison is case
insensitive. In case of a DateTime variable, normal comparisons are parsed with
.NET's super permissive parser, hour/minute/second checks are parsed as int32,
if it fails it throws an exception. DayOfWeek check is special, Step4
is
one of Sunday
/ Monday
/ Tuesday
/ Wednesday
/ Thursday
/ Friday
/
Saturday
(checked case insensitively).
Finally it returns the result of the comparison.
CT_Variable_To_Variable_Comparison
Step2
: variable name + optional indices (case insensitive)Step3
:Equals
/Not Equals
/Greater Than
/Greater Than or Equals
/Less Than
/Less Than or Equals
(case sensitive)Step4
: variable name + optional indices (case insensitive)
If the variable specified by Step4
doesn't exists, throws an exception. If
Step4
is a DateTime variable, it is treated as having an empty string as
value. Here is what happens, when the number of indices specified in Step4
doesn't match the variable type:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Single | OK | garbage/exception | garbage/exception |
1D | garbage | OK | exception |
2D | garbage | System.Collections.ArrayList |
OK |
See the previous section for explanations. Rags
converts number values to string, this is why you have
System.Collections.ArrayList
even with numbers.
If the variable specified by Step2
doesn't exists or it is a DateTime
variable, it doesn't return anything. Here is what happens when the indices
don't match, this time for Step2
:
Var type | 0 idx | 1 idx | 2 idx |
---|---|---|---|
Num single | OK | garbage/exception | garbage/exception |
Num 1D | garbage | OK | exception |
Num 2D | garbage | exception | OK |
Str single | OK | garbage/exception | garbage/exception |
Str 1D | garbage | OK | exception |
Str 2D | garbage | System.Collections.ArrayList |
OK |
If Step3
does not have an allowed value (string variables only support
Equals
and Not Equals
), it doesn't return anything.
In case of a number variable, the string representation of whatever that was
read from Step4
is converted to a double, if this fails it is treated as
0.0
. If Step2
has an index, the value used to store single number values
(refer to garbage in 0 idx column above) is overwritten with the current
array cell value. (This does not happen with string vars!) Afterward, it returns
the value of the comparison.
In case of a string variable, formatting macros are stripped from Step2
's
value (see text.md for details), but not from Step4
,
then they're compared for equality. (Less than, greater than not supported).
CT_Variable_CustomPropertyCheck
Step2
:variable_name:custom_property_name
(case insensitive)Step3
: comparison typeStep4
: value
See for more info on CustomPropertyChecks.
variable_name
is a name of a variable and can contain indices, but they're
ignored.