You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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, default True
  • InputType: ActionInputType (None, Object, Character, ObjectOrCharacter, Text, Custom, Inventory)
  • CustomChoiceTitle: string, default ""
  • CustomChoices: CustomChoice[], only if array not empty
  • EnhancedInputData: EnhancedInputData
  • Conditions: Condition[], only if array not empty
  • PassCommands: variant<Command, Condition>[], only if array not empty
  • FailCommands: variant<Command, Condition>[], only if array not empty

CustomChoice

  • Name: string

EnhancedInputData

  • BackgroundColor: color: color.ToArgb() == 0xaarrggbb reinterpret_casted to int32_t, default rgba(255, 255, 255, 140)
  • TextColor: color, default rgba(0, 0, 0, 255)
  • Imagename: string, default "". Ignored?
  • UseEnhancedGraphics: bool, default true. Called Use 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 when AllowCancel is false).
  • AllowCancel: bool, default true
  • NewImage: string, default ""
  • TextFont: font, default Times New Roman Bold, 12pt. Ignored/buggy?

Condition

  • Name: string, default ""
  • Checks: Check[], only if array not empty
  • PassCommands: only if array not empty
  • FailCommands: only if array not empty

Check

  • CondType: ConditionType, default CT_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, when FailOnFirst 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, with FailOnFirst it means all checks passed (so and with short-circuit), and without FailOnFirst it means at least one check passed (so or without short-circuit).
  • EnhancedData: only used when InputType != 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 with UseEnhancedGraphics...
    • 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:
    1. Clear AdditionalData
    2. When there is an EhancedData with an image, ShowMainImage and OverlayGraphics is true, and InputType is not None or Text, set the image to that.
    3. When InputType is not None, ask the input. If canceled, return (action result is false). Otherwise get the AdditionalData 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.
    4. Execute conditions
    5. Execute pass/fail commands

Command

  • CustomChoices: can be set on CT_SETVARIABLEBYINPUT
  • EnhancedData: can be set on CT_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 and Text 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,
    Action
    \-PassCommands
    . |-Condition
    . | |-PassCommands
    . | | |-Command cmd0
    . | | |-Command cmd1
    . | | \-Command cmd2
    . | \-FailCommands
    . | . \-Command cmd3
    . |-Command cmd4
    . \-Command cmd5
    
    If cmd1 aborts, cmd2 won't be executed (and neither cmd3), but instead it will continue with cmd4 and cmd5. This means that a CT_LOOP_BREAK not directly in the loop's PassCommands 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."
  • 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 as action_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 as action_object.
    • If the object is a Container and it is Open or not Openable, 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.
  • 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 without action_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 without action_object.

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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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 and UseInlineImages: 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 not UseInlineImages: 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 room
    • 00000000-0000-0000-0000-000000000002: "void" room
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part3 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part3 is <Void>, replace it with 00000000-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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Text: 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 name
  • Part4: ignored
  • Text: 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: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Text: 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: ignored
  • Part4: new character name override
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: none

This doesn't do anything.

CT_DEBUGTEXT

  • Part2: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: text
  • FIXUP: none

Prints Text to the log.

CT_EXPORTVARIABLE

  • Part2: variable name (case insensitive)
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: none

Prints -------------------------------- (32 hyphens), then pauses the game.

CT_DISPLAYLAYEREDPICTURE

  • Part2: file name (case insensitive)
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: none

What it does depends on the value of HideMainPicDisplay and UseInlineImages:

  • HideMainPicDisplay and UseInlineImages: 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 not UseInlineImages: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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 name
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Text: 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 :: use action_object
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 is Openable and Open.
  • ". It is closed." (double space!) if it is Openable and not Open.
  • ". It contains:" (double space!) if it is a Container, and (not Openable or Open), 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: ignored
  • Part4: ignored
  • Text: 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 :: use action_object
  • Part3: character name
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: object UUID (can be parsed) or name (case insensitive).
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 with the object's UUID. Same with Part3.

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 Part2s 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 :: use action_object
  • Part3: room UUID (can be parsed) or name (case insensitive). Special values:
    • 00000000-0000-0000-0000-000000000001: player's current room
    • 00000000-0000-0000-0000-000000000002: "void" room
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 with the object's UUID. If Part3 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part3 is <Void>, replace it with 00000000-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 :: use action_object
  • Part3: action_name-state
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: ignored
  • Part4: ignored
  • Text: new description
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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: ignored
  • Part4: new name override
  • Text: 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 :: use action_object
  • Part3: Locked / anything else (case sensitive)
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: Open / anything else (case sensitive)
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: Worn / anything else (case sensitive)
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: Visible / anything else (case sensitive)
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 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 :: use action_object
  • Part3: object UUID or name (case insensitive). Special values:
    • 00000000-0000-0000-0000-000000000004 :: use action_object
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: none

Schedules a loop break and aborts the current command list.

CT_CANCELMOVE

  • Part2: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: none

Prints the player's description after replacement (without loop_item) to the log.

CT_SETLAYEREDPLAYERPORTRAIT

  • Part2: file name or <None>
  • Part3: ignored
  • Part4: ignored
  • Text: 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 name
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <<Self>> (case sensitively), replace it with 00000000-0000-0000-0000-000000000004. Otherwise, if it can't be parsed as an UUID, and there is an object with that name, replace Part2 with the object's UUID. (This is completely pointless, as Part2 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 room
    • 00000000-0000-0000-0000-000000000002 :: "void" room
  • Part3: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: action_name-state
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: ignored
  • Text: new description
  • FIXUP: none

Set the player's description to Text.

CT_SETPLAYERGENDER

  • Part2: Male / Female / Other (case sensitive)
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part3: ignored
  • Part4: new player name (double replaced)
  • Text: ignored
  • FIXUP: none

Sets the player's name.

CT_SETPLAYERPORTRAIT

  • Part2: file name
  • Part3: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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: ignored
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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 and UseInlineImages: 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 not UseInlineImages: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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: ignored
  • Part4: ignored
  • Text: new description
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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 name
  • Text: 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: ignored
  • Part4: name override
  • Text: 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 name
  • Part4: ignored
  • Text: 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 name
  • Part4: ignored
  • Text: ignored
  • FIXUP: if Part2 is <CurrentRoom> (case sensitively), replace it with 00000000-0000-0000-0000-000000000001. If Part2 is <Void>, replace it with 00000000-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 and UseInlineImages: 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: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Part4: ignored
  • Text: 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: ignored
  • Text: 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: ignored
  • Text: 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 prints System.Collections.ArrayList).
  • Display Date Only: dddd, MMMM, dd yyyy
  • Display Time Only: hh:mm:ss tt
  • Display Weekday Only: dddd If Part3 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: ignored
  • Text: 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: ignored
  • Text: 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: ignored
  • EnhancedData: 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: ignored
  • EnhancedData: 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: ignored
  • Step4: ignored
  • Text: 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: ignored
  • Step3: ignored
  • Step4: ignored
  • Text: 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, setting loop_item
  • FailCommands are ignored
  • CT_LOOP_BREAK can be used to break out of the innermost loop. However, if break is inside a Condition's PassCommands or FailCommads, 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 or FailCommands will be executed after them, but without loop_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 without loop_item.
  • PassCommands or FailCommands 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 the And check is skipped, but subsequent checks are still executed This (I think) corresponds to And having higher precedence, so a list of Ignored, 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: ignored
  • Step4: 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:

  1. If selection and Step2 equals, return true.
  2. If there is an object named selection, returns object.uuid == Step2.
  3. If there is a character named selection, returns character.ToString() == Step2, where ToString is Name if NameOverride is not empty, else Name if we're outside a game (?), else NameOverride with text replacements (!!).
  4. If there is an object uuid matching the selection, returns object.name == Step2.
  5. Otherwise returns false.

CT_Character_CustomPropertyCheck

  • Step2: character_name:custom_property_name (case insensitive)
  • Step3: comparison type
  • Step4: 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 room
    • 00000000-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 or None (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 type
  • Step4: 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, replace Step3 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: ignored
  • Step4: ignored
  • FIXUP: if Step2 can't be parsed as an UUID, and there is an object with that name, replace Step2 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, replace Step2 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, replace Step2 with the object's UUID. Same with Step3 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 or None (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: ignored
  • Step4: ignored
  • FIXUP: if Step2 can't be parsed as an UUID, and there is an object with that name, replace Step2 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, replace Step2 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: ignored
  • Step3: ignored
  • Step4: 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: ignored
  • Step3: ignored
  • Step4: ignored

Iterate through each object in the game, setting loop_item.

CT_Loop_Item_Group

  • Step2: object group name (case sensitive)
  • Step3: ignored
  • Step4: 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: ignored
  • Step4: 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: ignored
  • Step4: 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: ignored
  • Step3: ignored
  • Step4: 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: ignored
  • Step4: 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: ignored
  • Step3: ignored
  • Step4: 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: ignored
  • Step4: 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 type
  • Step4: 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: ignored
  • Step4: 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: ignored
  • Step4: ignored
  • FIXUP: if Step2 can't be parsed as an UUID, and there is a room with that name, replace Step2 with the room's UUID.

Returns whether the player is currently in the specified room.

CT_Player_In_RoomGroup

  • Step2: room group or None (case sensitive)
  • Step3: ignored
  • Step4: 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: ignored
  • Step4: 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: ignored
  • Step4: 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 type
  • Step4: 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 type
  • Step4: 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 type
  • Step4: value

See for more info on CustomPropertyChecks. variable_name is a name of a variable and can contain indices, but they're ignored.