actions.md (105979B)
1 # XML syntax 2 Sample: 3 4 ```xml 5 <Action> 6 <Name><![CDATA[Examine]]></Name> 7 <OverrideName><![CDATA[]]></OverrideName> 8 <actionparent><![CDATA[None]]></actionparent> 9 <Active><![CDATA[True]]></Active> 10 <FailOnFirst><![CDATA[True]]></FailOnFirst> 11 <InputType><![CDATA[None]]></InputType> 12 <CustomChoiceTitle><![CDATA[]]></CustomChoiceTitle> 13 <EnhancedInputData> 14 <BackgroundColor><![CDATA[-1929379841]]></BackgroundColor> 15 <TextColor><![CDATA[-16777216]]></TextColor> 16 <Imagename><![CDATA[]]></Imagename> 17 <UseEnhancedGraphics><![CDATA[True]]></UseEnhancedGraphics> 18 <AllowCancel><![CDATA[True]]></AllowCancel> 19 <NewImage><![CDATA[]]></NewImage> 20 <TextFont><![CDATA[Microsoft Sans Serif, 12pt]]></TextFont> 21 </EnhancedInputData> 22 <PassCommands> 23 <Command> 24 <CmdType><![CDATA[CT_DISPLAYPICTURE]]></CmdType> 25 <CommandText><![CDATA[]]></CommandText> 26 <Part2><![CDATA[Toybox.jpg]]></Part2> 27 <Part3><![CDATA[]]></Part3> 28 <Part4><![CDATA[]]></Part4> 29 <EnhancedInputData> 30 <BackgroundColor><![CDATA[-1929379841]]></BackgroundColor> 31 <TextColor><![CDATA[-16777216]]></TextColor> 32 <Imagename><![CDATA[]]></Imagename> 33 <UseEnhancedGraphics><![CDATA[True]]></UseEnhancedGraphics> 34 <AllowCancel><![CDATA[True]]></AllowCancel> 35 <NewImage><![CDATA[]]></NewImage> 36 <TextFont><![CDATA[Microsoft Sans Serif, 12pt]]></TextFont> 37 </EnhancedInputData> 38 </Command> 39 </PassCommands> 40 </Action> 41 ``` 42 43 ## Action 44 * `Name`: `string` 45 * `OverrideName`: `string`, default `""` 46 * `actionparent`: `string`, default `"None"` 47 * `Active`: `bool` (`True` / `False`) 48 * `FailOnFirst`: `bool`, default `True` 49 * `InputType`: `ActionInputType` (`None`, `Object`, `Character`, 50 `ObjectOrCharacter`, `Text`, `Custom`, `Inventory`) 51 * `CustomChoiceTitle`: `string`, default `""` 52 * `CustomChoices`: `CustomChoice[]`, only if array not empty 53 * `EnhancedInputData`: `EnhancedInputData` 54 * `Conditions`: `Condition[]`, only if array not empty 55 * `PassCommands`: `variant<Command, Condition>[]`, only if array not empty 56 * `FailCommands`: `variant<Command, Condition>[]`, only if array not empty 57 58 ## CustomChoice 59 * `Name`: `string` 60 61 ## EnhancedInputData 62 * `BackgroundColor`: `color`: `color.ToArgb() == 0xaarrggbb` reinterpret_casted 63 to `int32_t`, default `rgba(255, 255, 255, 140)` 64 * `TextColor`: `color`, default `rgba(0, 0, 0, 255)` 65 * `Imagename`: `string`, default `""`. Ignored? 66 * `UseEnhancedGraphics`: `bool`, default true. Called `Use Overlay Graphics` in 67 GUI. When true, replace image + choices overlaid, when false, `NewImage` 68 ignored and popup window with normal winforms table and ok/cancel button (even 69 when `AllowCancel` is false). 70 * `AllowCancel`: `bool`, default true 71 * `NewImage`: `string`, default `""` 72 * `TextFont`: `font`, default `Times New Roman Bold, 12pt`. Ignored/buggy? 73 74 ## Condition 75 * `Name`: `string`, default `""` 76 * `Checks`: `Check[]`, only if array not empty 77 * `PassCommands`: only if array not empty 78 * `FailCommands`: only if array not empty 79 80 ## Check 81 * `CondType`: `ConditionType`, default `CT_Item_Held_By_Player` 82 * `CkType`: `CheckType` (`CT_Uninitialized`, `And`, `Or`) 83 * `Step2`: `string`, default `""` 84 * `Step3`: `string`, default `""` 85 * `Step4`: `string`, default `""` 86 87 ## Command 88 * `CmdType`: `CommandType` 89 * `CommandText`: `string`, default `""` 90 * `Part2`: `string`, default `""` 91 * `Part3`: `string`, default `""` 92 * `Part4`: `string`, default `""` 93 * `CustomChoices`: `CustomChoice[]`, only if not empty. 94 * `EnhancedInputData`: `EnhancedInputData`. 95 96 # Action 97 * `ActionParent`: used to generate submenus in right-click menu (yet another 98 ad-hoc grouping). If parent is inactive, action is inactive too. 99 * `FailOnFirst`: pseudocode, `c.Check()` runs PassCommands for each iteration 100 when it is a loop (yes, when `FailOnFirst` is false, `PassCommands` is 101 executed one more time) 102 ```c++ 103 bool success = FailOnFirst || Conditions.empty(); 104 for (c : Conditions) 105 { 106 if (c.Check()) // check succeeded or it was a loop 107 { 108 if (!FailOnFirst) success = true; 109 if (!FailOnFirst || !c.loop) c.PassCommands.Run(); 110 } 111 else 112 { 113 c.FailCommands.Run(); 114 if (FailOnFirst) { success = false; break; } 115 } 116 } 117 (success ? PassCommands : FailCommands).Run(); 118 ``` 119 * `PassCommands`, `FailCommands`: one of them is executed based on the 120 conditions' result, see above for exact semantics, but generally, with 121 `FailOnFirst` it means all checks passed (so `and` with short-circuit), and 122 without `FailOnFirst` it means at least one check passed (so `or` without 123 short-circuit). 124 * `EnhancedData`: only used when `InputType != None && InputType != Text && 125 UseEnhancedGraphics && !HideMainPicDisplay` 126 * `InputType`: ask a question at the beginning of the action if not None. 127 * `Object`: select an object currently visible in the player GUI. In practice, 128 this means any object in the inventory, any object in the current room 129 (including portals), any subobject of any of them; or any object held by a 130 character in the current room with allow inventory interactions. (Subobjects 131 inside characters don't appear but subobjects inside subobjects do, albeit 132 buggy! Just what did you expect?) List items: object DisplayNames, no tag 133 when `!UseEnhancedGraphics`. `AdditionalData` value: object's name (override 134 ignored). 135 * `Character`: select a character in the current room. List object: character 136 DisplayName with tag. `AdditionalData` value: character's name (override 137 ignored). 138 * `ObjectOrCharacter`: union of the previous two, except characters lack tag 139 with `UseEnhancedGraphics`... 140 * `Text`: input is not a selection from a list, but a free form text. 141 `EnhancedData` is ignored in this case. 142 * `Custom`: `CustomChoices` contains a list of choices. When not using overlay 143 graphics, the color of the text can be changed with `[c r,g,b]...[/c]` 144 (except the actual ranges are ignored, `foo[c 0,255,0]bar` sets the full 145 text to green, in case of multiple `[c]` tags, the last one wins. When using 146 overlay graphics, these tags are displayed as-is, without any processing. 147 Did you expect any logic behind this?) 148 * `Inventory`: objects in the player's inventory. No tag when 149 `!UseEnhancedGraphics`. 150 * Note that this doesn't nest properly. Executing an action inside an action 151 means that after returning to the parent action, the selection is lost. 152 * `action_object`: a hidden parameter. This is set to the object when executing 153 an action on an object, null otherwise (this is nesting properly). 154 * `player_moving`: another hidden parameter, only available in `<<On Player 155 Leave First Time>>` and `<<On Player Leave>>` actions, when triggered by a the 156 player (the 3DPD) trying to leave the current room. It is set to invalid in 157 other cases. 158 * Execution: 159 1. Clear `AdditionalData` 160 2. When there is an `EhancedData` with an image, `ShowMainImage` and 161 `OverlayGraphics` is true, and `InputType` is not `None` or `Text`, set the 162 image to that. 163 3. When `InputType` is not `None`, ask the input. If canceled, return (action 164 result is false). Otherwise get the `AdditionalData` value to set, then 165 print it, unless it is an uuid and there is an object with that uuid, 166 because in that case it prints that object's name. 167 4. Execute conditions 168 5. Execute pass/fail commands 169 170 # Command 171 * `CustomChoices`: can be set on `CT_SETVARIABLEBYINPUT` 172 * `EnhancedData`: can be set on `CT_SETVARIABLEBYINPUT` 173 * Exception handling: exceptions are caught when executing commands. Rags 174 displays a message, then continues with the next command. 175 176 ## Generic command info 177 * `Part2`, `Part3`, `Part4` and `Text` values are always run through the [text 178 replacement function][] before usage. 179 * Object/room UUID or name: this generally means rags first tries to find a 180 room/object with the given UUID, and if there's no one, it tries to find one 181 with the name. In this case everything is processed as strings, so if you end 182 up with an object whose UUID is in fact not a valid UUID, it will still find 183 it. On the other hand, this requires exact match, i.e. 8-4-4-4-12 format 184 without white spaces. However, there are a few cases when Rags actually tries 185 to parse the given string as a UUID and somehow work differently depending on 186 the outcome, but they're clearly marked as "room UUID (**can be parsed**) or 187 name". In this case you can ue other formats, see [allowed GUID formats][guid] 188 [(archive)][guid_archive] for details. (And undocumented details: except the 189 last format, spaces are not allowed, but the string is trimmed before use, so 190 `" {123..."` is valid, but `"{ 123..."` is not). 191 * Aborts the command list: subsequent items in the currently executing 192 `PassCommands`/`FailCommands` will not run, but it does not affect commands 193 above. So for example, 194 ``` 195 Action 196 \-PassCommands 197 . |-Condition 198 . | |-PassCommands 199 . | | |-Command cmd0 200 . | | |-Command cmd1 201 . | | \-Command cmd2 202 . | \-FailCommands 203 . | . \-Command cmd3 204 . |-Command cmd4 205 . \-Command cmd5 206 ``` 207 If `cmd1` aborts, `cmd2` won't be executed (and neither `cmd3`), but instead 208 it will continue with `cmd4` and `cmd5`. This means that a `CT_LOOP_BREAK` not 209 directly in the loop's `PassCommands` can act weird. 210 211 [guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid.-ctor?view=net-5.0#System_Guid__ctor_System_String_ 212 [guid_archive]: https://archive.md/wtYUh 213 214 ### Set custom property commands {#set-custom-property} 215 * `Part2`: `name:property_name` 216 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 217 sensitive**) 218 * `Part4`: value (**double replaced**) 219 * `Text`: ignored 220 221 The exact interpretation of `Part2` varies, but generally if it doesn't have 222 exactly one colon, or if the entity doesn't exists, it doesn't do anything. If 223 the entity doesn't have a property with name `property_name` (**case 224 sensitively**), it also doesn't do anything. 225 226 `Part4` is double evaluated, then in case of `*_JS` commands, it is evaluated as 227 a JavaScript code (whatever dialect Microsoft's JScript supports), unless it is 228 an empty string anfter trimming. In this case, it is replaced with `-1`. If the 229 return value is `null`, it is replaced with `"Null value returned from 230 evaluate."` string, otherwise it is converted to a string, using whatever rules 231 this [this shit][jscript] [(archive)][jscript_archive] uses. 232 233 If `Part3` is not one of the allowed values, it doesn't do anything. If `Part3` 234 is `Equals`, set the property's value to `Part4`. Otherwise it tries to 235 interpret both the property's current value and `Part4` as a double and do the 236 arithmetic operation. If one of the values are not a number, it doesn't do 237 anything, except that with `Subtract` if `Part4` is not a double, it **throws an 238 exception**. 239 240 [jscript]: https://docs.microsoft.com/en-us/dotnet/api/microsoft.jscript.eval.jscriptevaluate?view=netframework-4.8 241 [jscript_archive]: https://archive.md/YhYP6 242 243 ### Room enter procedure {#room-enter-procedure} 244 This is used by multiple commands. Action and timer executions are skipped in 245 some cases. 246 247 * Prints an empty line to the log. 248 * Sets room & main image (inline or to the main image area, handling layered 249 images) to the room's image (removes it if the room doesn't have one). 250 * When not skipping actions: when the room's `EnterFirstTime` is false, set it 251 to true and execute inner procedure with `<<On Player Enter First Time>>`. 252 Afterwards, execute inner procedure with `<<On Player Enter>>`. 253 * If the actions changed the player's room, don't do anything else. 254 * Prints the player's room description. 255 * If notifications are enabled: 256 * If the room has visible objects, it prints `"You can see "`, for each 257 visible object preposition + `" "` + display name (if preposition after 258 trimming is empty, preposition + space is skipped), separated by `", "`, all 259 on one line. 260 * For each character in the room, it prints display name + `" is here."` 261 * When not skipping timers: execute [multi timer 262 procedure](general.md#timer-multi). 263 264 #### Inner procedure 265 * There's a try-catch around the whole stuff, silently discarding any error 266 inside (but action execution already contains a try-catch inside, so nothing 267 should throw in this shit). 268 * First save the player's current room (original room) 269 * If the player's room has the specified action, execute it without 270 `action_object`. 271 * If the player has the specified action, execute it without `action_object`. 272 * Go through all objects in the player's current room. 273 * If the object's `EnterFirstTime` flag is false, set it to true and execute 274 its `<<On Player Enter First Time>>` action if it exists with object as 275 `action_object`. 276 * When the inner procedure is called with `<<On Player Enter>>` and the object 277 has an action with that name, and the player is still in the original room, 278 execute it with object as `action_object`. 279 * If the object is a `Container` and it is `Open` or not `Openable`, go 280 through all subobjects and do the previous two points for these subobjects. 281 Note that this operation is not recursive, so subobjects of the subobjects 282 are not considered. 283 * Go through all characters in the player's current room. 284 * If the character's `EnterFirstTime` flag is false, set it to true and 285 execute its `<<On Player Enter First Time>>` action if it exists without 286 `action_object`. 287 * When the inner procedure is called with `<<On Player Enter>>` and the 288 character has an action with that name, and the player is still in the 289 original room, execute it without `action_object`. 290 291 This means that for example if the player in entering the room for the first 292 time, object/character enter first time events are executed before the room's 293 ``<<On Player Enter>>`, but after if the player is entering a second time... 294 295 ## Available CommandTypes 296 ### CT_UNINITIALIZED 297 ### CT_ACTION_ADD_CUSTOMCHOICE {#CT_ACTION_ADD_CUSTOMCHOICE} 298 * `Part2`: `type:name:action_name` 299 * `Part3`: ignored 300 * `Part4`: ignored 301 * `Text`: choice to add (**case sensitive**) 302 * FIXUP: none 303 304 If `Part2` doesn't contain a colon, this action doesn't do anything. If it only 305 contains one colon, it is parsed as `type:action_name` and name is treated as 306 empty. A third colon and everything after it is ignored. 307 308 `type` is **case sensitively** one of the following: 309 * `Chr`: `name` is a character name (case insensitive). If the character does 310 not exists, it **throws an exception**. 311 * `Obj`: `name` is an object UUID or name (case insensitive). If the object does 312 not exist, it doesn't do anything. 313 * `Player`: `name` is ignored. 314 * `Room`: `name` is a room UUID (**can be parsed**) or name (case insensitive). 315 If the room doesn't exist, it doesn't do anything. 316 * `Timer`: `name` is a timer (case insensitive). If the timer doesn't exist, it 317 doesn't do anything. 318 * Anything else: it doesn't do anything. 319 320 `action_name` names an action inside the specified entity (case insensitive). If 321 it doesn't exists, this action doesn't do anything. If the action already has a 322 custom choice named `Text`, it doesn't do anything. Otherwise it adds `Text` to 323 the end of the custom choices. 324 325 ### CT_ACTION_CLEAR_CUSTOMCHOICE 326 * `Part2`: `type:name:action_name` 327 * `Part3`: ignored 328 * `Part4`: ignored 329 * `Text`: ignored 330 * FIXUP: none 331 332 See [CT_ACTION_ADD_CUSTOMCHOICE](#CT_ACTION_ADD_CUSTOMCHOICE) for `Part2` 333 handling. If it specifies a valid action, this action removes all custom choices 334 from it. 335 336 ### CT_ACTION_REMOVE_CUSTOMCHOICE 337 Same as [CT_ACTION_ADD_CUSTOMCHOICE](#CT_ACTION_ADD_CUSTOMCHOICE), except 338 this action removes `Text` from the custom choices, if the custom choice exists. 339 340 ### CT_DISPLAYCHARDESC 341 * `Part2`: character name (case insensitive) 342 * `Part3`: ignored 343 * `Part4`: ignored 344 * `Text`: ignored 345 * FIXUP: none 346 347 If the specified character does not exist, it **throws an exception**. 348 Otherwise, it prints the characters description to the log. 349 350 Additionally, if notifications are not turned off, and the character has any 351 `Visible` and `Worn` object in it's inventory, it prints `<character display 352 name> is wearing:`, then the display names of all worn objects in separate 353 lines. If the character has any `Visible` but not `Worn` objects, it prints 354 `<character display name> is carrying:`, then the display names of all not worn 355 objects, separated by newlines. 356 357 ### CT_CHAR_DISPLAYPORT 358 * `Part2`: character name (case insensitive) 359 * `Part3`: ignored 360 * `Part4`: ignored 361 * `Text`: ignored 362 * FIXUP: none 363 364 If the specified character does not exist, it doesn't do anything. Otherwise 365 what it does depends on the value of `HideMainPicDisplay` and `UseInlineImages`: 366 * `HideMainPicDisplay` and `UseInlineImages`: if the character doesn't have a 367 portrait, **throw an exception**. If the portrait is an image, paste it into 368 the log (without scaling and buggily, so sometimes it will just insert an 369 empty line...). If it is an audio/video, print the full path of the temporary 370 file to the log (where rags extracted the media file)... 371 * `HideMainPicDisplay` and not `UseInlineImages`: it doesn't do anything. 372 * Else: if the character doesn't have a portrait, or it is not an image, it 373 doesn't do anything. Otherwise, it sets the main picture and its overlay(!) 374 (right to the log) to character's portrait, including layered images. (Since 375 file overlays are over the main overlay, this will work, but if the base image 376 has transparency, it will be weird because of the double draw.) (I think rags 377 author wanted to support videos here based on the code, he just fucked it up.) 378 379 ### CT_MOVECHAR 380 * `Part2`: character name (case insensitive) 381 * `Part3`: room UUID (**can be parsed**) or name (case insensitive). Special 382 values (not parsed, must match exactly): 383 * `00000000-0000-0000-0000-000000000001`: player's current room 384 * `00000000-0000-0000-0000-000000000002`: "void" room 385 * `Part4`: ignored 386 * `Text`: ignored 387 * FIXUP: if `Part3` is `<CurrentRoom>` (**case sensitively**), replace it with 388 `00000000-0000-0000-0000-000000000001`. If `Part3` is `<Void>`, replace it 389 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 390 as an UUID and there is a room with that name, replace it with the room's 391 UUID. 392 393 If the specified character does not exist, **throws an exception**. If the 394 character is in the same room as the player, and it has an action named `<<On 395 Character Leave>>`, execute that (without a `loop_item`). 396 397 If `Part3` is `00000000-0000-0000-0000-000000000001`, move the character to the 398 same room as the character, and if it has an action named `<<On Character 399 Enter>>`, execute it. If `Part3` is `00000000-0000-0000-0000-000000000002`, move 400 the character nowhere. 401 402 If `Part3` is not one of those special values, check if it **can be parsed** as 403 a UUID. If yes, use that as-is, otherwise look up the room with that name. If 404 there is no room with that name, **DISPLAY A FUCKING MESSAGE BOX** (<code>"Error 405 in command CT_MoveChar. Could not locate a room called "</code> + `Part3`) 406 and returns. (But UUIDs are not checked for validity, so it's possible to move 407 someone to a not existing room.) Otherwise, move the character to the specified 408 room. If the room is the player's current room, and the character has an `<<On 409 Character Enter>>` action, execute it. 410 411 ### CT_MOVECHARINVTOPLAYER 412 * `Part2`: character name (case insensitive) 413 * `Part3`: ignored 414 * `Part4`: ignored 415 * `Text`: ignored 416 * FIXUP: none 417 418 If the specified character does not exist, it doesn't do anything. Otherwise 419 take all `Carryable` and `Visible` objects from the character's inventory and 420 move them to the player's inventory. 421 422 ### CT_CHAR_MOVETOOBJ 423 * `Part2`: character name (case insensitive) 424 * `Part3`: object UUID (case insensitive) 425 * `Part4`: ignored 426 * `Text`: ignored 427 * FIXUP: none 428 429 If the specified character or object doesn't exist, it doesn't do anything. If 430 the object is in a room, move the character to that room. If the object's 431 location's UUID (after move) is the same as the player's room UUID (this can be 432 actually true when the object is in a character, but the character's UUID is the 433 same as the player's room's UUID...), [execute room enter 434 procedure](#room-enter-procedure) without actions. 435 436 ### CT_CHAR_SETPORT 437 * `Part2`: character name (case insensitive) 438 * `Part3`: file name 439 * `Part4`: ignored 440 * `Text`: ignored 441 * FIXUP: none 442 443 If the specified character doesn't exist, it doesn't do anything. Otherwise set 444 the character's image to the specified file name, without error checking (so it 445 is possible to set it to a non-existing file). 446 447 ### CT_SETCHARACTION 448 * `Part2`: character name (case insensitive) 449 * `Part3`: `action_name-state` 450 * `Part4`: ignored 451 * `Text`: ignored 452 * FIXUP: none 453 454 If the specified character doesn't exist, it doesn't do anything. If `Part3` 455 doesn't contain a hyphen, it **throws an exception**. Otherwise everything until 456 the last hyphen is the `action_name`. If the character doesn't have an action 457 with that name (case insensitively), it doesn't do anything. Otherwise it sets 458 its active flag, to true if `state` is `Active` (**case sensitively**), to false 459 otherwise. 460 461 ### CT_CHAR_SET_CUSTOM_PROPERTY {#CT_CHAR_SET_CUSTOM_PROPERTY} 462 * `Part2`: `character_name:property_name` 463 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 464 sensitive**) 465 * `Part4`: value (**double replaced**) 466 * `Text`: ignored 467 * FIXUP: none 468 469 [See for more info on setting custom properties](#set-custom-property). If a 470 character named `character_name` doesn't exists (case insensitively) it doesn't 471 do anything. 472 473 ### CT_CHAR_SET_CUSTOM_PROPERTY_JS 474 Same as [CT_CHAR_SET_CUSTOM_PROPERTY](#CT_CHAR_SET_CUSTOM_PROPERTY), but with 475 JavaScript. 476 477 ### CT_SETCHARDESC 478 * `Part2`: character name (case insensitive) 479 * `Part3`: ignored 480 * `Part4`: ignored 481 * `Text`: new character description 482 * FIXUP: none 483 484 If the specified character does not exist, **throws an exception**. Otherwise 485 sets the character's description to `Text`. 486 487 ### CT_CHAR_SET_GENDER 488 * `Part2`: character name (case insensitive) 489 * `Part3`: `Male` / `Female` / `Other` (**case sensitive**) 490 * `Part4`: ignored 491 * `Text`: ignored 492 * FIXUP: none 493 494 If the specified character doesn't exist, it doesn't do anything. If `Part3` 495 after trimming is not one of the allowed values, it **throws an exception**. 496 Otherwise it sets the character's gender. 497 498 ### CT_CHAR_SET_NAME 499 * `Part2`: character name (case insensitive) 500 * `Part3`: ignored 501 * `Part4`: new character name override 502 * `Text`: ignored 503 * FIXUP: none 504 505 If the specified character doesn't exist, it doesn't do anything. Otherwise it 506 sets the character's name override. 507 508 ### CT_COMMENT 509 * `Part2`: ignored 510 * `Part3`: ignored 511 * `Part4`: ignored 512 * `Text`: ignored 513 * FIXUP: none 514 515 This doesn't do anything. 516 517 ### CT_DEBUGTEXT 518 * `Part2`: ignored 519 * `Part3`: ignored 520 * `Part4`: ignored 521 * `Text`: debug text 522 * FIXUP: none 523 524 If the second command line argument is `DEBUG` (**case sensitively**), it prints 525 `Text` to the log. 526 527 ### CT_JAVA_SET_RAGS 528 * `Part2`: ignored 529 * `Part3`: ignored 530 * `Part4`: ignored 531 * `Text`: JavaScript code (**double replaced**) 532 * FIXUP: none 533 534 If the JavaScript code does not return an array, it does nothing. Otherwise it 535 iterates through the returned array: 536 * non array elements are skipped 537 * if the element array doesn't have at least 2 items, it **throws an exception** 538 * otherwise the element is `[key, value]` 539 * convert them to strings 540 * if `value` is an empty string, replace it with a space (i.e. `" "`) 541 * call [text replacement function][] in set mode with `"[" + key + "]` 542 543 ### CT_DISPLAYTEXT 544 * `Part2`: ignored 545 * `Part3`: ignored 546 * `Part4`: ignored 547 * `Text`: text 548 * FIXUP: none 549 550 Prints `Text` to the log. 551 552 ### CT_EXPORTVARIABLE 553 * `Part2`: variable name (case insensitive) 554 * `Part3`: ignored 555 * `Part4`: ignored 556 * `Text`: ignored 557 * FIXUP: none 558 559 If the specified variable doesn't exist, it doesn't do anything. Otherwise it 560 pops up a save file dialog, and if the user doesn't cancel it, it serializes the 561 full variable state to a file. If the save fails, it **displays a message box**. 562 563 ### CT_IMPORTVARIABLE 564 * `Part2`: ignored 565 * `Part3`: ignored 566 * `Part4`: ignored 567 * `Text`: ignored 568 * FIXUP: none 569 570 Pops up a file load dialog. If the user cancels it, it doesn't do anything. If 571 the load fails, it **displays a message box**. If there is already a variable 572 with the given name, it is removed, then the imported variable is added. 573 574 _Note_: this means it is possible to create a new variable by importing an 575 unrelated game's exported variable... 576 577 ### CT_PAUSEGAME 578 * `Part2`: ignored 579 * `Part3`: ignored 580 * `Part4`: ignored 581 * `Text`: ignored 582 * FIXUP: none 583 584 Prints `--------------------------------` (32 hyphens), then pauses the game. 585 586 ### CT_DISPLAYLAYEREDPICTURE 587 * `Part2`: file name (case insensitive) 588 * `Part3`: ignored 589 * `Part4`: ignored 590 * `Text`: ignored 591 * FIXUP: none 592 593 When `HideMainPicDisplay`, it doesn't do anything. Otherwise, it sets the 594 temporary overlay image, it sets it to `Part2` if it exist, removes the overlay 595 otherwise. 596 597 Temporary overlay is something that is drawn on top of the base image, but below 598 other overlay images and removed when a different image is displayed. Doesn't 599 work with videos. 600 601 _Note_: base image is set as `BackgroundImage` on a `PictureBox`, this action 602 sets the `Image` of the same `PictureBox` temporarily, Other overlays are drawn 603 from a paint event handler on top of this. This also has a weird side effect 604 that gif animations only work when set with this action, and only if the game is 605 not paused in an action. 606 607 ### CT_DISPLAYPICTURE 608 * `Part2`: file name (case insensitive) 609 * `Part3`: ignored 610 * `Part4`: ignored 611 * `Text`: ignored 612 * FIXUP: none 613 614 What it does depends on the value of `HideMainPicDisplay` and `UseInlineImages`: 615 * `HideMainPicDisplay` and `UseInlineImages`: if the file doesn't exist, **throw 616 an exception**. If it is an image, paste it into the log (without scaling and 617 buggily, so sometimes it will just insert an empty line...). If it is an 618 audio/video, print the full path of the temporary file to the log (where rags 619 extracted the media file)... 620 * `HideMainPicDisplay` and not `UseInlineImages`: it doesn't do anything. 621 * Else: If the file is an image, it sets the main picture (right to the log), 622 including layered images. If it is something else, it switches to a windows 623 media player control and tries to open it. If the file doesn't exist, it still 624 switches to WMP, but doesn't load anything into it. 625 626 ### CT_MM_SET_BACKGROUND_MUSIC {#CT_MM_SET_BACKGROUND_MUSIC} 627 * `Part2`: file name (case insensitive) 628 * `Part3`: ignored 629 * `Part4`: ignored 630 * `Text`: ignored 631 * FIXUP: none 632 633 If the file exists and it is an image, it doesn't do anything. Otherwise it sets 634 the background music volume to zero by decreasing it by 1% every 15ms (so it 635 will take 1.5s, and it blocks the execution while doing this (it looks like it 636 blocks the whole fucking GUI thread, but I'm not 100% sure on this). Then it 637 sets the WMP control to the file if it exists, it unloads whatever was loaded if 638 it doesn't exist. 639 640 ### CT_MM_STOP_BACKGROUND_MUSIC 641 * `Part2`: ignored 642 * `Part3`: ignored 643 * `Part4`: ignored 644 * `Text`: ignored 645 * FIXUP: none 646 647 It sets the background music volume to zero in the same way as in 648 [CT_MM_SET_BACKGROUND_MUSIC](#CT_MM_SET_BACKGROUND_MUSIC), then it stops the 649 playback. This doesn't unload anything. 650 651 ### CT_MM_PLAY_SOUNDEFFECT 652 * `Part2`: file name (case insensitive) 653 * `Part3`: ignored 654 * `Part4`: ignored 655 * `Text`: ignored 656 * FIXUP: none 657 658 If the file exists and it is an image, it doesn't do anything. Otherwise it sets 659 the background sound effect to `Part2` if it exists, it unloads it otherwise. 660 There is no fiddling with the volume like with background music and you can't 661 play more than one sound effect at the same time. 662 663 ### CT_MM_SET_MAIN_COMPASS {#CT_MM_SET_MAIN_COMPASS} 664 * `Part2`: file name or `<Default>` 665 * `Part3`: ignored 666 * `Part4`: ignored 667 * `Text`: ignored 668 * FIXUP: none 669 670 If `Part2` is `<Default>` (**case sensitively**), it sets the compass image back 671 to rags' built-in default. Otherwise, it is a (case insensitive) filename. If 672 the file doesn't exists, or if it is not an image, it doesn't do anything. 673 Otherwise it sets the compass image (anim gifs and layered images not 674 supported). 675 676 ### CT_MM_SET_UD_COMPASS 677 Same as [CT_MM_SET_MAIN_COMPASS](#CT_MM_SET_MAIN_COMPASS), except it sets the 678 up-down compass image on success. 679 680 ### CT_LAYEREDIMAGE_ADD 681 * `Part2`: base image file name (case insensitive) 682 * `Part3`: layer file name 683 * `Part4`: ignored 684 * `Text`: ignored 685 * FIXUP: none 686 687 If the base image specified by `Part2` doesn't exist (or it's not an image), it 688 doesn't do anything. Otherwise it adds `Part3` to the end of the layered images 689 list (so it will be on the top) (even if it doesn't exist, it will simply be 690 ignored when drawing). 691 692 ### CT_LAYEREDIMAGE_CLEAR 693 * `Part2`: base image file name (case insensitive) 694 * `Part3`: ignored 695 * `Part4`: ignored 696 * `Text`: ignored 697 * FIXUP: none 698 699 If the base image specified by `Part2` doesn't exist (or it's not an image), it 700 doesn't do anything. Otherwise it clears all layered images. 701 702 ### CT_LAYEREDIMAGE_REMOVE 703 * `Part2`: base image file name (case insensitive) 704 * `Part3`: layer file name (**case sensitive**) 705 * `Part4`: ignored 706 * `Text`: ignored 707 * FIXUP: none 708 709 If the base image specified by `Part2` doesn't exist (or it's not an image), it 710 doesn't do anything. Otherwise, remove the first (i.e. most bottom) occurrence of 711 `Part3` from `Part2`'s layered images. 712 713 ### CT_LAYEREDIMAGE_REPLACE 714 * `Part2`: base image file name (case insensitive) 715 * `Part3`: layer to replace file name (**case sensitive**) 716 * `Part4`: layer to add file name (case insensitive) 717 * `Text`: ignored 718 * FIXUP: none 719 720 If the image specified by `Part2` or `Part4` doesn't exist (or not an image), it 721 doesn't do anything. Otherwise it removes the first occurrence of `Part3` from 722 `Part2`'s layers, and if found, add `Part4` to the end of the layers. 723 724 ### CT_DISPLAYITEMDESC 725 * `Part2`: object UUID or name (case insensitive). Special values: 726 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 727 * `Part3`: ignored 728 * `Part4`: ignored 729 * `Text`: ignored 730 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 731 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 732 UUID, and there is an object with that name, replace `Part2` with the object's 733 UUID. 734 735 When `Part2` is `00000000-0000-0000-0000-000000000004`: if there is a 736 `action_object`, it displays that object's description, otherwise it does 737 nothing. 738 739 When `Part2` is not a special value, try to get an object with than UUID, 740 failing that name. If it exists, it displays the description, otherwise it does 741 nothing. 742 743 Display description: if notifications are turned off, it just prints the 744 object's description. Otherwise, it prints the object's description, then: 745 * `". It is open."` (double space!) if it is `Openable` and `Open`. 746 * `". It is closed."` (double space!) if it is `Openable` and not `Open`. 747 * `". It contains:"` (double space!) if it is a `Container`, and (not 748 `Openable` or `Open`), and it is not a portal. Then it prints every visible 749 subobject's preposition + space + name on a new line, where every line except 750 the first is prefixed with `"and "`. If there are no visible subobjects, it 751 prints `"Nothing"`. 752 753 ### CT_ITEM_LAYERED_REMOVE {#CT_ITEM_LAYERED_REMOVE} 754 * `Part2`: object UUID or name (case insensitive) 755 * `Part3`: ignored 756 * `Part4`: ignored 757 * `Text`: ignored 758 * FIXUP: none 759 760 If the specified object doesn't exist, it doesn't do anything. Otherwise it 761 iterates over all other `Worn` objects in the same location (actually it's 762 buggy: if the object is not in a character/player, it iterates over all objects 763 in the same location *type*). If the two objects share a clothing zone, and if 764 `Part2's level <= other's level`, it fails. 765 766 The action collects all failures, and print a message to the log and returns. If 767 the object is in a character's inventory, the message prefix is: character name 768 (override ignored!) + `" cannot remove "` + `Part2`'s display name + `". "` + 769 character name + `" will need to remove "`, otherwise the message prefix is: 770 `"You cannot remove "` + `Part2`'s display name + `". You need to remove "`. The 771 message continues with the display names of all conflicting objects, separated 772 by `" and "`, and the message suffix is `" first."`. 773 774 If there are no failures, it prints a confirmation message. If the object is in 775 a character, it prints: character name (override ignored) + `" takes off "` + 776 `Part2`'s display name + `"."`, otherwise it prints `"You take off "` + 777 `Part2`'s display name + `"."`. Finally, it set's `Part2`'s `Worn` to false. 778 779 Display name in this case: if preposition not empty, prefix is `preposition` + 780 `" "`. If name override after trim is not empty, name override without trim, 781 otherwise name. 782 783 ### CT_ITEM_LAYERED_WEAR 784 Same as [CT_ITEM_LAYERED_REMOVE](#CT_ITEM_LAYERED_REMOVE), except it sets `Worn` 785 to true and the messages are different. `You/character_name cannot wear` on fail 786 and `You put on/character_name puts on` on failure. 787 788 ### CT_MOVEITEMTOCHAR 789 * `Part2`: object UUID or name (case insensitive). Special values: 790 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 791 * `Part3`: character name 792 * `Part4`: ignored 793 * `Text`: ignored 794 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 795 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 796 UUID, and there is an object with that name, replace `Part2` with the object's 797 UUID. 798 799 It the specified object doesn't exists, or in case of 800 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 801 nothing. Otherwise it sets the character's location to be inside `Part3` without 802 any checking (so it is possible to move the object into a non-existing 803 character). 804 805 ### CT_MOVEITEMTOINV 806 * `Part2`: object UUID or name (case insensitive). Special values: 807 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 808 * `Part3`: ignored 809 * `Part4`: ignored 810 * `Text`: ignored 811 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 812 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 813 UUID, and there is an object with that name, replace `Part2` with the object's 814 UUID. 815 816 It the specified object doesn't exists, or in case of 817 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 818 nothing. Otherwise, if the player has enforce weight limit set, checks if the 819 recursive sum of the carried items + the recursive weight of the new item does 820 not exceed the limit. If it does, it **DISPLAY A FUCKING MESSAGE BOX** with the 821 text `"The "` + object name (override ignored) + <code>" is too heavy to lift at 822 the moment. unload some stuff first."</code> (with two spaces before 823 unload and a lower case character at the beginning of the sentence), then 824 **CANCELS LOOP BREAK** and **ABORTS THE FUCKING COMMAND LIST**. 825 826 Otherwise it sets the object location to the player. 827 828 ### CT_MOVEITEMTOOBJ 829 * `Part2`: object UUID or name (case insensitive). Special values: 830 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 831 * `Part3`: object UUID (**can be parsed**) or name (case insensitive). 832 * `Part4`: ignored 833 * `Text`: ignored 834 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 835 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 836 UUID, and there is an object with that name, replace `Part2` with the object's 837 UUID. Same with `Part3`. 838 839 It the object specified by `Part2` doesn't exists, or in case of 840 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 841 nothing. If `Part3` **can be parsed** as a UUID, set `Part2`s location to 842 `Part3` without any checking (so it is possible to move the object into a 843 non-existing object). Otherwise, it tries to find an object with the name given 844 in `Part3`, if it finds one, it moves `Part2` into it, otherwise it does 845 nothing. 846 847 ### CT_MOVEITEMTOROOM 848 * `Part2`: object UUID or name (case insensitive). Special values: 849 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 850 * `Part3`: room UUID (**can be parsed**) or name (case insensitive). Special 851 values: 852 * `00000000-0000-0000-0000-000000000001`: player's current room 853 * `00000000-0000-0000-0000-000000000002`: "void" room 854 * `Part4`: ignored 855 * `Text`: ignored 856 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 857 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 858 UUID, and there is an object with that name, replace `Part2` with the object's 859 UUID. If `Part3` is `<CurrentRoom>` (**case sensitively**), replace it with 860 `00000000-0000-0000-0000-000000000001`. If `Part3` is `<Void>`, replace it 861 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 862 as an UUID and there is a room with that name, replace it with the room's 863 UUID. 864 865 It the object specified by `Part2` doesn't exists, or in case of 866 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 867 nothing. Get a room depending on `Part3`: 868 * `00000000-0000-0000-0000-000000000001`: get the player's current room. 869 * `00000000-0000-0000-0000-000000000002`: no room (i.e. move the object to 870 nowhere) 871 * **can be parsed** as a UUID: use the specified UUID directly without any 872 checking (so it is possible to move the object to a non-existing room). 873 * anything else: find a room with the specified name. If the room doesn't 874 exists, it **DISPLAY A FUCKING MESSAGE BOX** (<code>"Error in command 875 CT_MoveItemToRoom. Could not locate a room called "</code> + `Part3`) 876 and returns. 877 878 It sets the object location to the room. 879 880 ### CT_SETOBJECTACTION 881 * `Part2`: object UUID or name (case insensitive). Special values: 882 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 883 * `Part3`: `action_name-state` 884 * `Part4`: ignored 885 * `Text`: ignored 886 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 887 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 888 UUID, and there is an object with that name, replace `Part2` with the object's 889 UUID. 890 891 It the object specified by `Part2` doesn't exists, or in case of 892 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 893 nothing. If `Part3` doesn't contain a hyphen, it **throws an exception**. 894 Otherwise everything until the last hyphen is the `action_name`. If the 895 object doesn't have an action with that name (case insensitively), it doesn't 896 do anything. Otherwise it sets its active flag, to true if `state` is `Active` 897 (**case sensitively**), to false otherwise. 898 899 ### CT_ITEM_SET_CUSTOM_PROPERTY {#CT_ITEM_SET_CUSTOM_PROPERTY} 900 * `Part2`: `object_name:property_name` 901 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 902 sensitive**) 903 * `Part4`: value (**double replaced**) 904 * `Text`: ignored 905 * FIXUP: none 906 907 [See for more info on setting custom properties](#set-custom-property). If 908 object name is `<Self>` (**case sensitively**, single `<>`), it operates on 909 `action_object`, otherwise it finds an object with name `object_name` (case 910 insensitively). If it doesn't exist, it doesn't do anything. 911 912 ### CT_ITEM_SET_CUSTOM_PROPERTY_JS 913 Same as [CT_ITEM_SET_CUSTOM_PROPERTY](#CT_ITEM_SET_CUSTOM_PROPERTY), but with 914 JavaScript. 915 916 ### CT_SETITEMDESC 917 * `Part2`: object UUID or name (case insensitive). Special values: 918 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 919 * `Part3`: ignored 920 * `Part4`: ignored 921 * `Text`: new description 922 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 923 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 924 UUID, and there is an object with that name, replace `Part2` with the object's 925 UUID. 926 927 It the object specified by `Part2` doesn't exists, or in case of 928 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 929 nothing. Otherwise it sets the object's description to `Text`. 930 931 ### CT_ITEM_SET_NAME_OVERRIDE 932 * `Part2`: object UUID or name (case insensitive). 933 * `Part3`: ignored 934 * `Part4`: new name override 935 * `Text`: ignored 936 * FIXUP: none 937 938 It the object specified by `Part2` doesn't exists, it does nothing. Otherwise it 939 sets the object's name override to `Part4`. 940 941 ### CT_SETLOCKEDUNLOCKED 942 * `Part2`: object UUID or name (case insensitive). Special values: 943 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 944 * `Part3`: `Locked` / anything else (**case sensitive**) 945 * `Part4`: ignored 946 * `Text`: ignored 947 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 948 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 949 UUID, and there is an object with that name, replace `Part2` with the object's 950 UUID. 951 952 It the object specified by `Part2` doesn't exists, or in case of 953 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 954 nothing. Otherwise if `Part3` is `Locked`, it sets the object's `Locked` 955 property to true, false otherwise. 956 957 ### CT_SETOPENCLOSED 958 * `Part2`: object UUID or name (case insensitive). Special values: 959 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 960 * `Part3`: `Open` / anything else (**case sensitive**) 961 * `Part4`: ignored 962 * `Text`: ignored 963 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 964 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 965 UUID, and there is an object with that name, replace `Part2` with the object's 966 UUID. 967 968 It the object specified by `Part2` doesn't exists, or in case of 969 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 970 nothing. Otherwise if `Part3` is `Open`, it sets the object's `Open` property to 971 true, false otherwise. 972 973 ### CT_SETITEMTOWORN 974 * `Part2`: object UUID or name (case insensitive). Special values: 975 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 976 * `Part3`: `Worn` / anything else (**case sensitive**) 977 * `Part4`: ignored 978 * `Text`: ignored 979 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 980 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 981 UUID, and there is an object with that name, replace `Part2` with the object's 982 UUID. 983 984 It the object specified by `Part2` doesn't exists, or in case of 985 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 986 nothing. Otherwise if `Part3` is `Worn`, it sets the object's `Worn` property to 987 true, false otherwise. 988 989 ### CT_ITEM_SET_VISIBILITY 990 * `Part2`: object UUID or name (case insensitive). Special values: 991 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 992 * `Part3`: `Visible` / anything else (**case sensitive**) 993 * `Part4`: ignored 994 * `Text`: ignored 995 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 996 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 997 UUID, and there is an object with that name, replace `Part2` with the object's 998 UUID. 999 1000 It the object specified by `Part2` doesn't exists, or in case of 1001 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 1002 nothing. Otherwise if `Part3` is `Visible`, it sets the object's `Visible` 1003 property to true, false otherwise. 1004 1005 ### CT_ITEMS_MOVE_CONTAINER_TO_CONTAINER 1006 * `Part2`: object UUID or name (case insensitive). Special values: 1007 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 1008 * `Part3`: object UUID or name (case insensitive). Special values: 1009 * `00000000-0000-0000-0000-000000000004` :: use `action_object` 1010 * `Part4`: ignored 1011 * `Text`: ignored 1012 * FIXUP: none 1013 1014 It the object specified by `Part2` or `Part3` doesn't exists, or in case of 1015 `00000000-0000-0000-0000-000000000004` there is no `action_object`, it does 1016 nothing. Otherwise it moves all objects directly in `Part2` to `Part3`. 1017 1018 ### CT_LOOP_BREAK 1019 * `Part2`: ignored 1020 * `Part3`: ignored 1021 * `Part4`: ignored 1022 * `Text`: ignored 1023 * FIXUP: none 1024 1025 Schedules a loop break and **aborts the current command list**. 1026 1027 ### CT_CANCELMOVE 1028 * `Part2`: ignored 1029 * `Part3`: ignored 1030 * `Part4`: ignored 1031 * `Text`: ignored 1032 * FIXUP: none 1033 1034 Sets a flag to cancel the player's current move. This can be called anywhere, 1035 but only has an effect inside `<<On Player Leave First Time>>` and `<<On Player 1036 Leave>>` player actions when executed as a result of the player (the 3DPD one) 1037 selecting a move target on the GUI. 1038 1039 ### CT_DISPLAYPLAYERDESC 1040 * `Part2`: ignored 1041 * `Part3`: ignored 1042 * `Part4`: ignored 1043 * `Text`: ignored 1044 * FIXUP: none 1045 1046 Prints the player's description after replacement (without `loop_item`) to the 1047 log. 1048 1049 ### CT_SETLAYEREDPLAYERPORTRAIT 1050 * `Part2`: file name or `<None>` 1051 * `Part3`: ignored 1052 * `Part4`: ignored 1053 * `Text`: ignored 1054 * FIXUP: none 1055 1056 If `Part2` is `<None>` (**case sensitively**), it is treated as an empty string. 1057 This sets the player's overlay image to `Part2` without any checking (so it is 1058 possible to set it to a non-existing file, it will be ignored when drawing). 1059 1060 ### CT_MOVEINVENTORYTOCHAR 1061 * `Part2`: character name 1062 * `Part3`: ignored 1063 * `Part4`: ignored 1064 * `Text`: ignored 1065 * FIXUP: if `Part2` is `<<Self>>` (**case sensitively**), replace it with 1066 `00000000-0000-0000-0000-000000000004`. Otherwise, if it can't be parsed as an 1067 UUID, and there is an object with that name, replace `Part2` with the object's 1068 UUID. (This is completely pointless, as `Part2` must be a character name, not 1069 an object UUID). 1070 1071 Moves every item from the player's inventory to `Part2`'s inventory without any 1072 checking (so it is possible to move it into a non-existing character). 1073 1074 ### CT_MOVEINVENTORYTOROOM 1075 * `Part2`: room's UUID. Special values (not parsed, must match exactly): 1076 * `00000000-0000-0000-0000-000000000001` :: player's current room 1077 * `00000000-0000-0000-0000-000000000002` :: "void" room 1078 * `Part3`: ignored 1079 * `Part4`: ignored 1080 * `Text`: ignored 1081 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1082 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1083 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1084 as an UUID and there is a room with that name, replace it with the room's 1085 UUID. 1086 1087 Moves every item from the player's inventory to the specified location: 1088 * `00000000-0000-0000-0000-000000000001`: move to the player's current room. 1089 * `00000000-0000-0000-0000-000000000002`: move to nowhere. 1090 * anything else: move to the room with the specified UUID without any checking 1091 (so it is possible to move items to a not existing room). 1092 1093 ### CT_MOVEPLAYER 1094 * `Part2`: room uuid (**can be parsed**) or name (case insensitive). 1095 * `Part3`: ignored 1096 * `Part4`: ignored 1097 * `Text`: ignored 1098 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1099 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1100 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1101 as an UUID and there is a room with that name, replace it with the room's 1102 UUID. 1103 1104 If `Part2` **can be parsed** as a UUID, find a room with that UUID, else find a 1105 room with that name, and move the player there. If the room doesn't exist, move 1106 the player to nowhere instead (which is a surefire way to fuck up rags and 1107 receive exceptions from everywhere). Otherwise, [execute room enter 1108 procedure](#room-enter-procedure) with actions but without timers. 1109 1110 ### CT_MOVETOCHAR {#CT_MOVETOCHAR} 1111 * `Part2`: character name (case insensitive). 1112 * `Part3`: ignored 1113 * `Part4`: ignored 1114 * `Text`: ignored 1115 * FIXUP: none 1116 1117 If the character doesn't exist, or the character is nowhere or in the same room 1118 as the player, it doesn't do anything. Otherwise it moves the player to the same 1119 room as the character is in, if it exists, it moves the player to nowhere if it 1120 doesn't exist. Finally, if the player is in a room, it [executes room enter 1121 procedure](#room-enter-procedure) with actions but without timers. 1122 1123 *Note*: this action catches any inner exceptions and silently discards them. 1124 1125 ### CT_MOVETOOBJ 1126 * `Part2`: object UUID (case insensitive). 1127 * `Part3`: ignored 1128 * `Part4`: ignored 1129 * `Text`: ignored 1130 * FIXUP: none 1131 1132 If the object doesn't exist, it doesn't do anything. If the object is in a room, 1133 moves the player to that room, or nowhere if it doesn't. Finally, if the player 1134 is in a room, it [executes room enter procedure](#room-enter-procedure) with 1135 actions but without timers. 1136 1137 *Note*: this action catches any inner exceptions and silently discards them. 1138 1139 *Note*: unlike [CT_MOVETOCHAR](#CT_MOVETOCHAR), this executes room enter actions 1140 when the object is not in a room or it is already in the same room as the 1141 player. 1142 1143 ### CT_PLAYER_SET_CUSTOM_PROPERTY {#CT_PLAYER_SET_CUSTOM_PROPERTY} 1144 * `Part2`: `property_name` 1145 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 1146 sensitive**) 1147 * `Part4`: value (**double replaced**) 1148 * `Text`: ignored 1149 * FIXUP: none 1150 1151 [See for more info on setting custom properties](#set-custom-property). Since 1152 there is only one player, `Part2` is treated as `property_name`, even if it 1153 contains colons. 1154 1155 ### CT_PLAYER_SET_CUSTOM_PROPERTY_JS 1156 Same as [CT_PLAYER_SET_CUSTOM_PROPERTY](#CT_PLAYER_SET_CUSTOM_PROPERTY), but 1157 with JavaScript. 1158 1159 ### CT_SETPLAYERACTION 1160 * `Part2`: ignored 1161 * `Part3`: `action_name-state` 1162 * `Part4`: ignored 1163 * `Text`: ignored 1164 * FIXUP: none 1165 1166 If `Part3` doesn't contain a hyphen, it **throws an exception**. Otherwise 1167 everything until the last hyphen is the `action_name`. If the player doesn't 1168 have an action with that name (case insensitively), it doesn't do anything. 1169 Otherwise it sets its active flag, to true if `state` is `Active` (**case 1170 sensitively**), to false otherwise. 1171 1172 ### CT_SETPLAYERDESC 1173 * `Part2`: ignored 1174 * `Part3`: ignored 1175 * `Part4`: ignored 1176 * `Text`: new description 1177 * FIXUP: none 1178 1179 Set the player's description to `Text`. 1180 1181 ### CT_SETPLAYERGENDER 1182 * `Part2`: `Male` / `Female` / `Other` (**case sensitive**) 1183 * `Part3`: ignored 1184 * `Part4`: ignored 1185 * `Text`: ignored 1186 * FIXUP: none 1187 1188 If `Part2` is not one of the allowed values, it **throws an exception**, 1189 otherwise it sets the player's gender. 1190 1191 ### CT_SETPLAYERNAME 1192 * `Part2`: ignored 1193 * `Part3`: ignored 1194 * `Part4`: new player name (**double replaced**) 1195 * `Text`: ignored 1196 * FIXUP: none 1197 1198 Sets the player's name. 1199 1200 ### CT_SETPLAYERPORTRAIT 1201 * `Part2`: file name 1202 * `Part3`: ignored 1203 * `Part4`: ignored 1204 * `Text`: ignored 1205 * FIXUP: none 1206 1207 Sets the player's image to `Part2` without any checking (so it is possible to 1208 set it to a non-existing file). 1209 1210 ### CT_DISPLAYROOMDESCRIPTION 1211 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). Special 1212 values: 1213 * `00000000-0000-0000-0000-000000000001`: player's current room 1214 * `Part3`: ignored 1215 * `Part4`: ignored 1216 * `Text`: ignored 1217 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1218 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1219 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1220 as an UUID and there is a room with that name, replace it with the room's 1221 UUID. 1222 1223 If `Part2` is `00000000-0000-0000-0000-000000000001` it uses the player's 1224 current room, otherwise it finds a room with the given UUID if it **can be 1225 parsed** as a UUID, by a name otherwise. If the room doesn't exists it doesn't 1226 do anything, otherwise it prints its description (and just the description, and 1227 not 28472 unrelated things). 1228 1229 ### CT_DISPLAYROOMPICTURE 1230 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). Special 1231 values: 1232 * `00000000-0000-0000-0000-000000000001`: player's current room 1233 * `Part3`: ignored 1234 * `Part4`: ignored 1235 * `Text`: ignored 1236 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1237 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1238 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1239 as an UUID and there is a room with that name, replace it with the room's 1240 UUID. 1241 1242 If `Part2` is `00000000-0000-0000-0000-000000000001` it uses the player's 1243 current room, otherwise it finds a room with the given UUID if it **can be 1244 parsed** as a UUID, by a name otherwise. If the room doesn't exists it doesn't 1245 do anything. What it does depends on the value of `HideMainPicDisplay` and 1246 `UseInlineImages`: 1247 * `HideMainPicDisplay` and `UseInlineImages`: if the file doesn't exist, **throw 1248 an exception**. If it is an image, paste it into the log (without scaling and 1249 buggily, so sometimes it will just insert an empty line...). If it is an 1250 audio/video, print the full path of the temporary file to the log (where rags 1251 extracted the media file)... 1252 * `HideMainPicDisplay` and not `UseInlineImages`: it doesn't do anything. 1253 * Else: If the file is an image, it sets the main picture (right to the log), 1254 including (single) layered image. If both of them are images, it works 1255 correctly. If any of them missing, they're not set. If one is an image and and 1256 the other is a video, the last one wins (so overlay beats normal image). 1257 1258 ### CT_ROOM_MOVE_ITEMS_TO_PLAYER 1259 * `Part2`: room UUID (case insensitive). Special values (not parsed, must match 1260 exactly): 1261 * `00000000-0000-0000-0000-000000000001`: player's current room 1262 * `Part3`: ignored 1263 * `Part4`: ignored 1264 * `Text`: ignored 1265 * FIXUP: none 1266 1267 If `Part2` is not a valid UUID, it **throws an exception**. Otherwise, if it is 1268 `00000000-0000-0000-0000-000000000001` uses the player's current room, otherwise 1269 it finds the room with the given UUID. If there is no such room, it doesn't do 1270 anything. Otherwise it moves all `Carryable` and `Visible` in the room to the 1271 player's inventory. 1272 1273 ### CT_SETROOMACTION 1274 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). Special 1275 values: 1276 * `00000000-0000-0000-0000-000000000001`: player's current room 1277 * `Part3`: `action_name-state` 1278 * `Part4`: ignored 1279 * `Text`: ignored 1280 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1281 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1282 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1283 as an UUID and there is a room with that name, replace it with the room's 1284 UUID. 1285 1286 It the room specified by `Part2` doesn't exists, it does nothing. If `Part3` 1287 doesn't contain a hyphen, it **throws an exception**. Otherwise everything until 1288 the last hyphen is the `action_name`. If the room doesn't have an action with 1289 that name (case insensitively), it doesn't do anything. Otherwise it sets its 1290 active flag, to true if `state` is `Active` (**case sensitively**), to false 1291 otherwise. 1292 1293 ### CT_ROOM_SET_CUSTOM_PROPERTY {#CT_ROOM_SET_CUSTOM_PROPERTY} 1294 * `Part2`: `room_name:property_name` 1295 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 1296 sensitive**) 1297 * `Part4`: value (**double replaced**) 1298 * `Text`: ignored 1299 * FIXUP: none 1300 1301 [See for more info on setting custom properties](#set-custom-property). If room 1302 name is `<CurrentRoom>` (**case sensitively**), it operates on the player's 1303 current room, otherwise it finds a room with name `room_name` (case 1304 insensitively). If it doesn't exist, it doesn't do anything. 1305 1306 ### CT_ROOM_SET_CUSTOM_PROPERTY_JS 1307 Same as [CT_ROOM_SET_CUSTOM_PROPERTY](#CT_ROOM_SET_CUSTOM_PROPERTY), but with 1308 JavaScript. 1309 1310 ### CT_SETROOMDESCRIPTION 1311 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). Special 1312 values: 1313 * `00000000-0000-0000-0000-000000000001`: player's current room 1314 * `Part3`: ignored 1315 * `Part4`: ignored 1316 * `Text`: new description 1317 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1318 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1319 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1320 as an UUID and there is a room with that name, replace it with the room's 1321 UUID. 1322 1323 If `Part2` is `00000000-0000-0000-0000-000000000001`, it operates on the 1324 player's current room, otherwise if it **can be parsed** as a UUID, it finds a 1325 room with that UUID, else it find a room with that name. If the specified room 1326 doesn't exist, it doesn't do anything, otherwise it sets the room's description. 1327 1328 ### CT_SETEXIT 1329 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). 1330 * `Part3`: `direction-active` (**case sensitive**) 1331 * `Part4`: ignored 1332 * `Text`: ignored 1333 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1334 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1335 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1336 as an UUID and there is a room with that name, replace it with the room's 1337 UUID. (*Note*: the action doesn't hande these values...) 1338 1339 If `Part2` **can be parsed** as a UUID, it finds a room with that UUID, else it 1340 find a room with that name. If the specified room doesn't exist, it doesn't do 1341 anything. If `Part3` doesn't contain a hyphen, it **throws an exception**, 1342 otherwise everything until the first hyphen is the `direction`. If there is a 1343 second hyphen, it and everything after it is ignored. `active` is trimmed of 1344 white-space, but `direction` is not (unlike other enum parsing places...). 1345 1346 If `direction` does not refer to a valid direction (`North`, `South`, 1347 `NorthEast`, ...), it doesn't do anything. Otherwise it sets its `Active` 1348 status, to true if `active` is `Active`, false otherwise. 1349 1350 ### CT_SETEXITDESTINATION 1351 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). 1352 * `Part3`: direction (**case sensitive**) 1353 * `Part4`: `<None>` or destination room UUID (**can be parsed**) or name 1354 * `Text`: ignored 1355 * FIXUP: none 1356 1357 If `Part2` **can be parsed** as a UUID, it finds a room with that UUID, else it 1358 find a room with that name. If the specified room doesn't exist, it doesn't do 1359 anything. If `direction` does not refer to a valid direction (without trimming), 1360 it doesn't do anything. 1361 1362 If `Part4` is `<None>` (**case sensitively**), it sets the exit's destination to 1363 nowhere and sets its `Active` flag to false. Otherwise, if `Part4` **can be 1364 parsed** as a UUID, it finds a room with that UUID, otherwise it finds a room 1365 with that name. If the room does not exists, it does nothing, otherwise it sets 1366 the exit's destination to the room and the exit's `Active` status to true. 1367 1368 ### CT_ROOM_SET_NAME_OVERRIDE 1369 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). 1370 * `Part3`: ignored 1371 * `Part4`: name override 1372 * `Text`: ignored 1373 * FIXUP: none 1374 1375 If `Part2` **can be parsed** as a UUID, it finds a room with that UUID, else it 1376 find a room with that name. If the specified room doesn't exist, it doesn't do 1377 anything, otherwise it sets the room's name override. 1378 1379 ### CT_SETROOMLAYEREDPIC 1380 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). Special 1381 values: 1382 * `00000000-0000-0000-0000-000000000001` :: player's current room 1383 * `Part3`: file name 1384 * `Part4`: ignored 1385 * `Text`: ignored 1386 * FIXUP: none 1387 1388 If `Part2` is `00000000-0000-0000-0000-000000000001`, it operates on the 1389 player's current room, otherwise if it **can be parsed** as a UUID, it finds a 1390 room with that UUID, else it find a room with that name. If the specified room 1391 doesn't exist, it doesn't do anything. 1392 1393 If `Part3` is `<None>`, it is treated as empty string, then it sets the room's 1394 overlay image to `Part3` without any checking (so it can be set to a 1395 non-existing image). 1396 1397 If the room is the current player's room, and `HideMainPicDisplay` is false, it 1398 updates the main image and room image, but without handling videos. 1399 1400 ### CT_SETROOMPIC 1401 * `Part2`: room UUID (**can be parsed**) or name (case insensitive). Special 1402 values: 1403 * `00000000-0000-0000-0000-000000000001` :: player's current room 1404 * `Part3`: file name 1405 * `Part4`: ignored 1406 * `Text`: ignored 1407 * FIXUP: if `Part2` is `<CurrentRoom>` (**case sensitively**), replace it with 1408 `00000000-0000-0000-0000-000000000001`. If `Part2` is `<Void>`, replace it 1409 with `00000000-0000-0000-0000-000000000002`. Otherwise, if it can't be parsed 1410 as an UUID and there is a room with that name, replace it with the room's 1411 UUID. 1412 1413 If `Part2` is `00000000-0000-0000-0000-000000000001`, it operates on the 1414 player's current room, otherwise if it **can be parsed** as a UUID, it finds a 1415 room with that UUID, else it find a room with that name. If the specified room 1416 doesn't exist, it doesn't do anything, otherwise it sets the room's image 1417 without any checking (so it can be set to a non-existing image). 1418 1419 If the room is the current player's room, it tries to redisplay the room image: 1420 * If `HideMainPicDisplay` and `UseInlineImages`: paste the room image into the 1421 log with the usual bugs. 1422 * If `HideMainPicDisplay`: no further actions. 1423 * Else: it updates the main image (but not the room image), without handling 1424 videos. 1425 1426 ### CT_Status_ItemVisibleInvisible 1427 * `Part2`: status bar item name (case insensitive) 1428 * `Part3`: `Visible` / anything else (case insensitive) 1429 * `Part4`: ignored 1430 * `Text`: ignored 1431 * FIXUP: none 1432 1433 If the status bar item specified by `Part2` doesn't exist, it doesn't do 1434 anything. Otherwise it sets its `Visible` flag, to true if `Part3` is `Visible`, 1435 to false otherwise. 1436 1437 ### CT_EXECUTETIMER {#CT_EXECUTETIMER} 1438 * `Part2`: timer name (case insensitive) 1439 * `Part3`: ignored 1440 * `Part4`: ignored 1441 * `Text`: ignored 1442 * FIXUP: none 1443 1444 If the timer with the given name doesn't exist, or it doesn't have an action 1445 with name `<<On Each Turn>>`, it doesn't do anything. Otherwise it executes the 1446 action (without an `action_object`), repeating it as many times as it is reset. 1447 1448 ### CT_RESETTIMER 1449 * `Part2`: timer name (case insensitive) 1450 * `Part3`: ignored 1451 * `Part4`: ignored 1452 * `Text`: ignored 1453 * FIXUP: none 1454 1455 If the timer with the given name doesn't exist, it doesn't do anything. 1456 1457 Otherwise it sets the timer's `TurnNumber` to zero. If `Part2` is the currently 1458 executing timer, it queues a reset (which will cause 1459 [CT_EXECUTETIMER](#CT_EXECUTETIMER) to execute it again, or when normally 1460 executing timers, it will skip subsequent On ... Turn actions), otherwise it 1461 **clears the queued reset**. In any case it **CANCELS LOOP BREAK** and **ABORTS 1462 THE FUCKING COMMAND LIST**. 1463 1464 ### CT_TIMER_SET_CUSTOM_PROPERTY {#CT_TIMER_SET_CUSTOM_PROPERTY} 1465 * `Part2`: `timer_name:property_name` (case insensitive) 1466 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 1467 sensitive**) 1468 * `Part4`: value (**double replaced**) 1469 * `Text`: ignored 1470 * FIXUP: none 1471 1472 [See for more info on setting custom properties](#set-custom-property). It finds 1473 a timer with name `timer_name` (case insensitively). If it doesn't exist, it 1474 doesn't do anything. 1475 1476 ### CT_TIMER_SET_CUSTOM_PROPERTY_JS 1477 Same as [CT_TIMER_SET_CUSTOM_PROPERTY](#CT_TIMER_SET_CUSTOM_PROPERTY), but with 1478 JavaScript. 1479 1480 ### CT_SETTIMER 1481 * `Part2`: timer name (case insensitive) 1482 * `Part3`: `Active` / anything else (case insensitive) 1483 * `Part4`: ignored 1484 * `Text`: ignored 1485 * FIXUP: none 1486 1487 If the timer with the given name doesn't exist, it doesn't do anything. 1488 Otherwise it sets the timer's `Active` flag, to true if `Part3` is `Active`, to 1489 false if it is anything else. 1490 1491 ### CT_DISPLAYVARIABLE 1492 * `Part2`: variable name + optional indices (case insensitive) 1493 * `Part3`: `Display Date & Time` / `Display Date Only` / `Display Time Only` / 1494 `Display Weekday Only` (**case sensitive**) 1495 * `Part4`: ignored 1496 * `Text`: ignored 1497 * FIXUP: none 1498 1499 If the variable specified by `Part2` doesn't exist, it doesn't do anything. Here 1500 is what happens, when the number of indices specified doesn't match the variable 1501 type: 1502 | Var type | 0 idx | 1 idx | 2 idx | 1503 |------------|------------|--------------------------------|---------------| 1504 | Single | single val | single val | single val | 1505 | 1D | nothing | value | **exception** | 1506 | Num/Str 2D | nothing | `System.Collections.ArrayList` | value | 1507 | DT 2D | nothing | **exception** | value | 1508 1509 (Array indices are completely ignored with non-array variables.) Out-of-range 1510 accesses generate exceptions except in "single val" and "nothing" cells. 1511 1512 In case of number or string variables, `Part3` is ignored, they're converted to 1513 string normally and printed. With DateTime variables it specifies a date format: 1514 * `Display Date & Time`: `dddd, MMMM, dd yyyy hh:mm:ss tt`, except in case of 2D 1515 arrays, where it just prints whatever string representation the date is stored 1516 in (so it doesn't throw if you only specify one index, it prints 1517 `System.Collections.ArrayList`). 1518 * `Display Date Only`: `dddd, MMMM, dd yyyy` 1519 * `Display Time Only`: `hh:mm:ss tt` 1520 * `Display Weekday Only`: `dddd` 1521 If `Part3` is not one of the allowed values, it doesn't print anything. 1522 1523 ### CT_SETVARIABLE {#CT_SETVARIABLE} 1524 * `Part2`: variable name + optional indices (case insensitive) 1525 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` / `Add Days` / 1526 `Add Hours` / `Add Minutes` / `Add Seconds` / `Subtract Days` / `Subtract 1527 Hours` / `Subtract Minutes` / `Subtract Seconds` / `Set Day Of Month To` / 1528 `Set Hours To` / `Set Minutes To` / `Set Seconds To` (**case sensitive**) 1529 * `Part4`: value (**double replaced**) 1530 * `Text`: also value (**double replaced**) 1531 * FIXUP: none 1532 1533 If `Part2` contains the string `Array:` (anywhere, **case sensitive**), the 1534 first 6 characters of `Part2` is removed and it will be an array set, otherwise 1535 a normal set. The rest is parsed for variable name and indices as usual, if the 1536 variable doesn't exists, this action does nothing. 1537 1538 In case of a JavaScript set 1539 ([CT_VARIABLE_SET_JAVASCRIPT](#CT_VARIABLE_SET_JAVASCRIPT)), it runs `Part4` 1540 through the JavaScript interpreter (after double replace), and converts the 1541 result to string. 1542 1543 Here is what happens generally when the number of indices doesn't match, but 1544 watch out for exceptions below. 1545 | Var type | 0 idx | 1 idx | 2 idx | 1546 |----------|------------|--------------------------------|----------------------| 1547 | Single | set single | ignore/**exception** | ignore/**exception** | 1548 | 1D | set single | set | **exception** | 1549 | 2D | set single | **fuckup** | set | 1550 * ignore/**exception**: this means that the code will try to overwrite the array 1551 part of the variable, which depending on what leftover garbage is there, might 1552 actually work, but you're more likely to get an exception. 1553 * **fuckup**: in this case you'll end up with an array where a row is replaced 1554 by a string/number/dt, and... you can expect things to break left and right if 1555 you do this. 1556 1557 With number and datetime variables, array set is only working when `Part3` is 1558 `Equals`, but for string variables `Part3` is ignored. An array set is 1559 only working with JS set, without JS it just clears the variable (it will be 0 1560 rows, indefinite columns, it leaves single value alone). 1561 1562 With a normal set, with a number variable, if `Part4` is not a number, it 1563 **throws an exception**, otherwise it just sets the specified cell with the 1564 error handling above. In case of `Add`, `Subtract`, `Multiply` and `Divide` only 1565 normal sets are possible, `Array:` is ignored and **fuckup** changes into 1566 **exception**. If `Part3` has any other value, the value is not updated. 1567 1568 If the variable has `EnforceRestrictions`, the set (or not set in case `Part3` 1569 is invalid...) value is validated. If it is smaller than replaced and converted 1570 to double min, it is set to min, and if after it is larger than max, it is set 1571 to max (if the result of replacement is not a number, it **throws an 1572 exception**). In case of `Equals` and **fuckup**, this will end up in an 1573 exception, but only after fucking up the variable... 1574 1575 In case of string variables, `Part3` is ignored (only simple settings is 1576 supported) and the value is read from `Text` instead of `Part4` (`Text` is also 1577 evaluated with JS, but only with string variables). Array set and normal set is 1578 supported. 1579 1580 DateTime works similarly to numbers, except: every possible value of `Part3` is 1581 supported except `Add`, `Subtract`, `Multiply`, `Divide`, and there are no 1582 min-max checks. `Equals` expects a date time, parsed by .NET's super permissive 1583 parser, everything else an int32, if the conversion fails it **throws an 1584 exception**. 1585 1586 ### CT_ITEM_GETRANDOMGROUP {#CT_ITEM_GETRANDOMGROUP} 1587 * `Part2`: variable name (without indices!) (case insensitive) 1588 * `Part3`: group name (case insensitive) 1589 * `Part4`: ignored 1590 * `Text`: ignored 1591 * FIXUP: none 1592 1593 If the variable specified by `Part2` doesn't exist, it doesn't do anything. 1594 1595 **This operates directly on SQL**, not the in-memory structures (that gets 1596 serialized into savegames). If `Part3` doesn't name a valid object group, it is 1597 treated as being empty. Otherwise it collects all objects recursively in the 1598 object group. 1599 1600 It sets the variable's single string value (even if it is not a string variable) 1601 to the name of a random object in this set, or to the empty string if the object 1602 group is empty. 1603 1604 ### CT_MM_GETRANDOMGROUP 1605 * `Part2`: variable name (without indices!) (case insensitive) 1606 * `Part3`: group name (case insensitive) 1607 * `Part4`: ignored 1608 * `Text`: ignored 1609 * FIXUP: none 1610 1611 Exactly the same as [CT_ITEM_GETRANDOMGROUP](#CT_ITEM_GETRANDOMGROUP), except it 1612 gets a random file name from a file group. 1613 1614 ### CT_VARIABLE_SET_JAVASCRIPT {#CT_VARIABLE_SET_JAVASCRIPT} 1615 See [CT_SETVARIABLE](#CT_SETVARIABLE) for details. I'm only going to document 1616 how JavaScript arrays are turned into rags variable arrays: 1617 1618 If the returned value is not a JavaScript array, the result is an empty array. 1619 Otherwise each item of the array is converted as follows: 1620 * it is not an array and variable type is number: the value is converted to a 1621 string, then to a double. If any of this fails, the value is `-1`. 1622 * it is not an array and variable type is string: the value is converted to a 1623 string. If this fails, the exception is quietly swallowed and it leaves the 1624 array in a half-set state. 1625 * it is not an array and variable type is DateTime: it is converted to a string, 1626 then parsed with .net's super permissive parser. If this fails somehow, it is 1627 set to `0001-01-01 00:00:00` unspecified timezone (can be treated as local). 1628 * it is an array: if it is an empty array, it is skipped, otherwise each item of 1629 this inner array is simply converted to a string. (2D arrays are always stored 1630 as strings in rags.) (If it is not a 2D array, **fuckup**.) 1631 1632 ### CT_SETVARIABLEBYINPUT {#CT_SETVARIABLEBYINPUT} 1633 * `Part2`: input type: `Text` / `Characters` / `Characters And Objects` / 1634 `Objects` / `Custom` / `Inventory` (**case sensitive**) 1635 * `Part3`: variable name + optional indices (case insensitive) 1636 * `Part4`: custom choice title (**double replaced**) 1637 * `Text`: ignored 1638 * `EnhancedData`: one of the few functions that use it 1639 * FIXUP: none 1640 1641 If input type is not one of the allowed values, it is treated as being `None` 1642 (which means an empty selection list. Interestingly, without ovelays, you can 1643 still click on OK even with zero items, overlay forces a cancel button in this 1644 case). In case of a custom choice, custom values are taken from `EnhancedData`, 1645 after replacement, otherwise the same as with normal action input. 1646 1647 When using overlay input, the title is taken from `Part4`, or if it empty after 1648 replace, it is `Please make a selection:`. Without overlay, it is always taken 1649 from `Part4`, even if it is empty. Non overlay query is never cancelable, while 1650 overlay input is cancelable if the list is empty or `EnhancedData.AllowCancel`. 1651 1652 Without overlay, it never uses tags, with overlay it's same as the normal 1653 action. There's no post-processing of the selected tag. If the selection is 1654 canceled, it **CANCELS LOOP BREAK** and **ABORTS THE FUCKING COMMAND LIST**. 1655 1656 If the variable does not exists, or if input type is `None`, it doesn't do 1657 anything (but it only checks this after the user made a selection...). This 1658 action expects the variable to be a string, it sets single and array string 1659 values without checking, ending up in ignore/fuckup cases when done. With `Text` 1660 input, the selection is simply stored. Otherwise, first the single value is set 1661 to an empty string, and if the selection is not an empty string, the normal 1662 value is set. Here is what happens when the number of indices doesn't match the 1663 variable type: 1664 1665 | Var type | 0 idx | 1 idx | 2 idx | 1666 |----------|------------|--------------------------------|----------------------| 1667 | Single | set single | ignore/**exception** | ignore/**exception** | 1668 | 1D | set single | set | **exception** | 1669 | 2D | set single | **fuckup** | set | 1670 1671 ### CT_SETVARIABLE_NUMERIC_BYINPUT 1672 * `Part2`: input type: `Text` / `Custom` (**case sensitive**) 1673 * `Part3`: variable name + optional indices (case insensitive) 1674 * `Part4`: custom choice title (**double replaced**) 1675 * `Text`: ignored 1676 * `EnhancedData`: one of the few functions that use it 1677 * FIXUP: none 1678 1679 If input type is not one of the allowed values, it is treated as being `None`. 1680 Largely the same as [CT_SETVARIABLEBYINPUT](#CT_SETVARIABLEBYINPUT), except it 1681 shows a `Sorry, you must enter a valid number.` message with `Text` input when 1682 you enter an invalid input (custom choices are not checked, you get an 1683 **exception** when the code actually tries to set the value. 1684 1685 It sets the variable's number fields (with a possibility to **fuckup**), except 1686 that it also handles `EnforceRestrictions`. 1687 1688 ### CT_VARIABLE_SET_CUSTOM_PROPERTY {#CT_VARIABLE_SET_CUSTOM_PROPERTY} 1689 * `Part2`: `variable_name:property_name` (case insensitive) 1690 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` (**case 1691 sensitive**) 1692 * `Part4`: value (**double replaced**) 1693 * `Text`: ignored 1694 * FIXUP: none 1695 1696 [See for more info on setting custom properties](#set-custom-property). It finds 1697 a variable with name `variable_name` (case insensitively, without indices). If 1698 it doesn't exist, it doesn't do anything. 1699 1700 ### CT_VARIABLE_SET_CUSTOM_PROPERTY_JS 1701 Same as [CT_VARIABLE_SET_CUSTOM_PROPERTY](#CT_VARIABLE_SET_CUSTOM_PROPERTY), but 1702 with JavaScript. 1703 1704 ### CT_VARIABLE_SET_RANDOMLY 1705 * `Step2`: variable name + optional indices (case insensitive) 1706 * `Step3`: ignored 1707 * `Step4`: ignored 1708 * `Text`: ignored 1709 * FIXUP: none 1710 1711 If the variable doesn't exist, it doesn't do anything. It will always set number 1712 fields, so using this on a non-number variable allows a **fuckup**. It generates 1713 an integer random number between `[int32(double(min)), int32(double(max))+1)` 1714 (string is first converted to double, then converted to int32 by rounding to 1715 zero). Throws an error if the upper bound is smaller than the lower, returns min 1716 when they're equal. Here is what happens when the number of indices doesn't 1717 match the variable type: 1718 1719 | Var type | 0 idx | 1 idx | 2 idx | 1720 |----------|------------|--------------------------------|----------------------| 1721 | Single | set single | ignore/**exception** | ignore/**exception** | 1722 | 1D | set single | set | **exception** | 1723 | 2D | set single | **fuckup** | set | 1724 1725 ### CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE {#CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE} 1726 * `Step2`: variable name + optional indices (case insensitive) 1727 * `Step3`: `character_name:property_name` 1728 * `Step4`: ignored (**double replaced**) 1729 * `Text`: ignored 1730 * FIXUP: none 1731 1732 If the specified variable doesn't exist, it doesn't do anything. If `Step3` 1733 doesn't exactly have one colon, or the character (case insensitively) doesn't 1734 exist, or the custom property (**case sensitively**) doesn't exist, it is 1735 treated as being an empty string. If the variable is a DateTime variable, it 1736 doesn't do anything. 1737 1738 Here is what happens when the number of indices doesn't match the variable type: 1739 | Var type | 0 idx | 1 idx | 2 idx | 1740 |----------|------------|--------------------------------|----------------------| 1741 | Single | set single | ignore/**exception** | ignore/**exception** | 1742 | 1D | set single | set | **exception** | 1743 | 2D | set single | **fuckup** | set | 1744 1745 In case of a number variable, if it is a "set single", the string value is 1746 converted to a double, if it fails it prints some kind of debug log and does 1747 nothing. In case of arrays, it just stores the string into the array (even in 1748 case of an 1D array), for random **fuckup**s in the future. 1749 1750 ### CT_VARIABLE_SET_WITH_ITEMPROPERTYVALUE 1751 * `Step2`: variable name + optional indices (case insensitive) 1752 * `Step3`: `object_name:property_name` 1753 * `Step4`: ignored (**double replaced**) 1754 * `Text`: ignored 1755 * FIXUP: none 1756 1757 Copy-paste of 1758 [CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE](#CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE), 1759 but with objects instead of characters. Additionally, `object_name` can be 1760 `<Self>` (**case sensitive**) to mean `action_object` if it is specified, it 1761 uses empty string if it is not specified. 1762 1763 ### CT_VARIABLE_SET_WITH_PLAYERPROPERTYVALUE 1764 * `Step2`: variable name + optional indices (case insensitive) 1765 * `Step3`: `property_name` (**case sensitive**) 1766 * `Step4`: ignored (**double replaced**) 1767 * `Text`: ignored 1768 * FIXUP: none 1769 1770 Copy-paste of 1771 [CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE](#CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE), 1772 but with player instead of characters. `Step3` is treated as a property name, 1773 even if it contains colons. 1774 1775 ### CT_VARIABLE_SET_WITH_ROOMPROPERTYVALUE 1776 * `Step2`: variable name + optional indices (case insensitive) 1777 * `Step3`: `room_name:property_name` 1778 * `Step4`: ignored (**double replaced**) 1779 * `Text`: ignored 1780 * FIXUP: none 1781 1782 Copy-paste of 1783 [CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE](#CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE), 1784 but with rooms instead of characters. Additionally, `room_name` can be 1785 `<CurrentRoom>` (**case sensitively**) to mean the player's current room. 1786 1787 ### CT_VARIABLE_SET_WITH_TIMERPROPERTYVALUE 1788 * `Step2`: variable name + optional indices (case insensitive) 1789 * `Step3`: `timer_name:property_name` 1790 * `Step4`: ignored (**double replaced**) 1791 * `Text`: ignored 1792 * FIXUP: none 1793 1794 Copy-paste of 1795 [CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE](#CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE), 1796 but with timers instead of characters. 1797 1798 ### CT_VARIABLE_SET_WITH_VARIABLE 1799 * `Step2`: variable to set name + optional indices (case insensitive) 1800 * `Part3`: `Equals` / `Add` / `Subtract` / `Multiply` / `Divide` / `Add Days` / 1801 `Add Hours` / `Add Minutes` / `Add Seconds` / `Subtract Days` / `Subtract 1802 Hours` / `Subtract Minutes` / `Subtract Seconds` / `Set Day Of Month To` / 1803 `Set Hours To` / `Set Minutes To` / `Set Seconds To` (**case sensitive**) 1804 * `Step4`: variable to read name + optional indices (case insensitive) 1805 * `Text`: ignored 1806 1807 If the variable specified by `Step4` doesn't exist, it **throws an exception**. 1808 Here is what happens when the number of indices doesn't match the variable type: 1809 | Var type | 0 idx | 1 idx | 2 idx | 1810 |----------|------------|-----------------------|-----------------------| 1811 | Single | single val | garbage/**exception** | garbage/**exception** | 1812 | 1D | single val | OK | **exception** | 1813 | 2D | single val | **exception** | OK | 1814 1815 The variable is also treated as a number variable, even if not: in case of 1816 single values, this will read garbage, with 1D it **throws an exception**, and 1817 with 2D it tries to parse the stored string as a number and **throws an 1818 excepton** if it fails. 1819 1820 With two indices, if the value is not an integer, it **throws an exception**. If 1821 the value can not be represented as an int32 (after tuncation), it also **throws 1822 an exception**. 1823 1824 If the variable specified by `Step2` doesn't exist, or it is a string variable, 1825 it doesn't do anything. 1826 1827 Here is what happens generally when the number of indices doesn't match with 1828 `Step2`: 1829 | Var type | 0 idx | 1 idx | 2 idx | 1830 |----------|------------|--------------------------------|----------------------| 1831 | Single | set single | ignore/**exception** | ignore/**exception** | 1832 | 1D | set single | set | **exception** | 1833 | 2D | set single | **fuckup** | set | 1834 1835 With number variables, `Step4` is treated as a double, except with 2D arrays and 1836 `Equal`, or single values and `Divide`, where it is treated as an integer. If 1837 `Part2` is not `Equals`, `Add`, `Subtract`, `Multiply` or `Divide`, it doesn't 1838 update the variable. `EnforceRestrictions` are handled on number variables, even 1839 if there was no update. 1840 1841 DateTime with `Equals` is a buggy piece of shit. With not 2D arrays, it just 1842 **throws an exception**, with 2D arrays, it sets the the number converted to 1843 string, which rags later won't be able to parse back (so it's a kind of 1844 **fuckup**). The rest date-related operations actually work though, but they 1845 operate on the truncated integers (so you can't add 1.5 days to a DT). 1846 1847 ### CT_VARIABLE_SET_WITH_VARIABLEPROPERTYVALUE 1848 * `Step2`: variable name + optional indices (case insensitive) 1849 * `Step3`: `timer_name:property_name` 1850 * `Step4`: ignored (**double replaced**) 1851 * `Text`: ignored 1852 * FIXUP: none 1853 1854 Copy-paste of 1855 [CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE](#CT_VARIABLE_SET_WITH_CHARPROPERTYVALUE), 1856 but with variables instead of characters. 1857 1858 ### CT_ENDGAME 1859 * `Step2`: ignored 1860 * `Step3`: ignored 1861 * `Step4`: ignored 1862 * `Text`: display text 1863 * FIXUP: none 1864 1865 Prints `Text` to the log and ends the game. It also displays a dialog box 1866 <code>The game has ended. Would you like to play again?</code> (with two 1867 spaces between the sentences) and buttons `Restart`, `Load` and `No`. Unless 1868 selecting `Restart`, the remaining commands will be run. 1869 1870 # Condition 1871 It has two subtypes, loop and if. 1872 ## Loop 1873 * It has a single check, `CT_Loop_*` 1874 * `PassCommands` will be called for each iteration, setting `loop_item` 1875 * `FailCommands` are ignored 1876 * `CT_LOOP_BREAK` can be used to break out of the innermost loop. However, if 1877 break is inside a Condition's `PassCommands` or `FailCommads`, and there are 1878 subsequent Commands in the same list, some later commands can cancel the 1879 break. See the individual command documentations. 1880 ## If 1881 * Normally it has one or more checks, neither of them being `CT_Loop_*` 1882 * Rags designer doesn't allow you to create a condition with zero checks, but if 1883 you manage to create one it will be regarded as true. 1884 * However you can create an if with loops inside, if you want a bugfest: first, 1885 the loop body will be executed when the check is evaluated. Second, they don't 1886 modify the variable used to store the return value, so their "value" will be 1887 whatever before them produced (or true if they're the first in the list). 1888 Third, since they're not loops, `PassCommands` or `FailCommands` will be 1889 executed after them, but without `loop_item`. For example, having an if with 1890 "loop exits and loop exits", PassCommands will be executed 25 times (unless it 1891 breaks): 12 times with the exits of the first room, 12 times with the exits of 1892 the second room, then one more time without `loop_item`. 1893 * `PassCommands` or `FailCommands` are executed depending on the check 1894 * Handling multiple checks: every check has a `CkType`, but it's ignored for the 1895 first check. For the rest, they kinda also short-circuit: 1896 * `Or` check following a true: result is true, no other checks are executed. 1897 * `And` check following a false: executing the `And` check is skipped, but 1898 subsequent checks are still executed 1899 This (I think) corresponds to `And` having higher precedence, so a list of 1900 `Ignored, A`, `And, B`, `Or, C`, `And, D` is parsed as `(A && B) || (C && D)`. 1901 1902 ## Generic check info 1903 `Step2`, `Step3` and `Step4` values are always run through the [text replacement 1904 function][] before usage. 1905 1906 Note for X in Y checks: rags store the location of the objects/characters by 1907 string, so it's possible for an object/character to be in a 1908 room/object/character that doesn't exists. Unfortunately this information is 1909 lost during import to scraps. 1910 1911 ### CustomPropertyChecks {#CustomPropertyChecks} 1912 * `Step2`: colon separated pair: `name:custom_property_name` 1913 * `Step3`: `Equals` / `Not Equals` / `Contains` / `Greater Than` / `Greater Than 1914 or Equals` / `Less Than` / `Less Than or Equals` (**case sensitive**) 1915 * `Step4`: value to compare with. **Double replaced!** 1916 1917 Interpretation of `name` varies, but generally if `Step2` doesn't have exactly 1918 one colon, or the named entity doesn't exists, or it doesn't have a custom 1919 property named `custom_property_name` (**case insensitively**), the check 1920 **doesn't return anything**. If `Step3` is not one of the allowed values, it 1921 **returns true**. 1922 1923 If both the custom property's value and `Step4` can be converted to a double, 1924 they're treated as numbers otherwise as strings, except `Contains` which is 1925 always string based. (This means that for example `012` equals to `12` but 1926 `012x` doesn't equal to `12x`.) String compare is **REVERSED** and case 1927 insensitive (i.e. `1 < 2` but `1x > 2x`). Returns the result of the comparison. 1928 1929 ## Available ConditionTypes 1930 ### CT_Uninitialized 1931 Not exactly a valid check, Rags will treat it as any other invalid option: 1932 **don't return anything**. 1933 1934 ### CT_AdditionalDataCheck 1935 * `Step2`: object/character name to compare with (case insensitive) 1936 * `Step3`: ignored 1937 * `Step4`: text value to compare with (case insensitive) 1938 1939 Checks the value selected at the beginning of the action. If `InputType` is 1940 `Text`, it compares the entered string with `Step4` (`Step2` is ignored in this 1941 case). Otherwise, `Step4` is ignored and: 1942 1. If selection and `Step2` equals, return true. 1943 2. If there is an object named selection, returns `object.uuid == Step2`. 1944 3. If there is a character named selection, returns `character.ToString() == 1945 Step2`, where `ToString` is `Name` if `NameOverride` is not empty, else 1946 `Name` if we're outside a game (?), else `NameOverride` with text 1947 replacements (!!). 1948 4. If there is an object uuid matching the selection, returns `object.name == 1949 Step2`. 1950 5. Otherwise returns false. 1951 ### CT_Character_CustomPropertyCheck 1952 * `Step2`: `character_name:custom_property_name` (case insensitive) 1953 * `Step3`: comparison type 1954 * `Step4`: value 1955 1956 [See for more info on CustomPropertyChecks](#CustomPropertyChecks). 1957 `character_name` is a name of a character. 1958 1959 ### CT_Character_Gender 1960 * `Step2`: character's name (case insensitive) 1961 * `Step3`: `Male` / `Female` / anything else (**case sensitive**) 1962 * `Step4`: ignored 1963 1964 If the character doesn't exist, **doesn't return anything**. If `Step3` is 1965 neither `Male` nor `Female`, it's treated as `Other`. Returns whether the 1966 character's gender equals to `Step3`. 1967 1968 ### CT_Character_In_Room 1969 * `Step2`: character's name (case insensitive) 1970 * `Step3`: room's uuid (**case sensitive**). Special values (not parsed, must 1971 match exactly): 1972 * `00000000-0000-0000-0000-000000000001` :: player's current room 1973 * `00000000-0000-0000-0000-000000000002` :: "void" room 1974 * `Step4`: ignored 1975 1976 If the character doesn't exists, **throws an exception**. Returns whether the 1977 character is currently in the specified room (see special values above). 1978 1979 ### CT_Character_In_RoomGroup {#CT_Character_In_RoomGroup} 1980 * `Step2`: character's name (case insensitive) 1981 * `Step3`: room group or `None` (**case sensitive**) 1982 * `Step4`: ignored 1983 1984 If the character doesn't exists, **throws an exception**. If the character is 1985 not in a room, returns false. Returns whether character's current room's group's 1986 name equals to `Step3` (use `None` to check if the room is not in any group). 1987 1988 _Note_: in rags, room groups are not recursive, but they are in scraps. To be 1989 consistent with `CT_Item_InGroup` and `CT_MultiMedia_InGroup`, scraps does a 1990 recursive check here aswell. 1991 1992 ### CT_Item_CustomPropertyCheck 1993 * `Step2`: `object_name:custom_property_name` (case insensitive) 1994 * `Step3`: comparison type 1995 * `Step4`: value 1996 1997 [See for more info on CustomPropertyChecks](#CustomPropertyChecks). If 1998 `object_name` is `<Self>` (**case sensitively**), it refers to the hidden object 1999 parameter, otherwise it is an object's name (case insensitively). 2000 2001 ### CT_Item_InGroup {#CT_Item_InGroup} 2002 * `Step2`: name of the object (case insensitive) 2003 * `Step3`: group name (**case sensitive**) 2004 * `Step4`: ignored 2005 2006 **This is buggy in designer!** Rags designer puts the object's uuid into 2007 `Step2`, but the player expects an object name. To fix it, you have to *type* 2008 the object name into the `Choose Item` box, and not select from the drop-down 2009 menu. 2010 2011 **This operates directly on SQL**, not the in-memory structures (that gets 2012 serialized into savegames). If there is no such object in the SQL, it is treated 2013 as having an empty group name. If the object's group name equals to `Step3`, 2014 returns true. Otherwise it gets the children of the specified group and 2015 recursively checks all of them (same as getting the parent of the object group, 2016 and recursively checking the parent groups, except much more inefficient). 2017 Returns false if not found. 2018 2019 ### CT_Item_Held_By_Character 2020 * `Step2`: character name (**case sensitive**) 2021 * `Step3`: object UUID or name (case insensitive) 2022 * `Step4`: ignored 2023 * FIXUP: if `Step3` can't be parsed as an UUID, and there is an object with that 2024 name, replace `Step3` with the object's UUID. 2025 2026 If the object doesn't exist, returns false. Otherwise returns whether the 2027 object's location is directly a character with the specified name (not 2028 subobject). 2029 2030 ### CT_Item_Held_By_Player 2031 * `Step2`: object UUID or name (case insensitive) 2032 * `Step3`: ignored 2033 * `Step4`: ignored 2034 * FIXUP: if `Step2` can't be parsed as an UUID, and there is an object with that 2035 name, replace `Step2` with the object's UUID. 2036 2037 If the object doesn't exist, returns false. Otherwise returns whether the 2038 object's location is a player with the specified name **or it is recursively 2039 inside an object held by the player**. 2040 2041 ### CT_Item_In_Object 2042 * `Step2`: object UUID or name (case insensitive) 2043 * `Step3`: other object's UUID (**case sensitive**) 2044 * `Step4`: ignored 2045 * FIXUP: if `Step2` can't be parsed as an UUID, and there is an object with that 2046 name, replace `Step2` with the object's UUID. 2047 2048 If the object specified by `Step2` doesn't exists, return false. Otherwise 2049 returns whether `Step2` is directly inside `Step3`. 2050 2051 ### CT_Item_In_Room 2052 * `Step2`: object UUID or name (case insensitive) 2053 * `Step3`: room UUID (**case sensitive**). Special values (not parsed, must 2054 match exactly): 2055 * `00000000-0000-0000-0000-000000000001` :: player's current room 2056 * `Step4`: ignored 2057 * FIXUP: if `Step2` can't be parsed as an UUID, and there is an object with that 2058 name, replace `Step2` with the object's UUID. Same with `Step3` and room. 2059 2060 If the object doesn't exists, returns false. Otherwise returns whether the 2061 object is currently in the specified room (see special values above) directly 2062 (i.e. not subobjects). 2063 2064 ### CT_Item_In_RoomGroup 2065 * `Step2`: object UUID (case insensitive) 2066 * `Step3`: room group or `None` (**case sensitive**) 2067 * `Step4`: ignored 2068 2069 If the object doesn't exists or it is not in a room, returns false. Returns 2070 whether the object's current room's group's name equals to `Step3`. 2071 2072 [See also CT_Character_In_RoomGroup](#CT_Character_In_RoomGroup). 2073 2074 ### CT_Item_Not_Held_By_Player 2075 * `Step2`: object UUID or name (case insensitive) 2076 * `Step3`: ignored 2077 * `Step4`: ignored 2078 * FIXUP: if `Step2` can't be parsed as an UUID, and there is an object with that 2079 name, replace `Step2` with the object's UUID. 2080 2081 If the object doesn't exists, return false. Otherwise returns whether the object 2082 is not in the player's inventory **DIRECTLY**. Consistency, where the fuck are 2083 you!? 2084 2085 ### CT_Item_Not_In_Object 2086 * `Step2`: object UUID or name (case insensitive) 2087 * `Step3`: other object's UUID (**case sensitive**) 2088 * `Step4`: ignored 2089 2090 If the object specified by `Step2` doesn't exists, return false. Otherwise 2091 returns whether `Step2` is not directly inside `Step3`. 2092 2093 ### CT_Item_State_Check 2094 * `Step2`: object UUID or name (case insensitive) 2095 * `Step3`: `Open` / `Closed` / `Locked` / `Unlocked` / `Worn` / `Removed` / 2096 `Read` / `Unread` / `Visible` / `Invisible` (**case sensitive**) 2097 * `Step4`: ignored 2098 * FIXUP: if `Step2` can't be parsed as an UUID, and there is an object with that 2099 name, replace `Step2` with the object's UUID. 2100 2101 If the object doesn't exists, it returns false. Otherwise, if `Step3` is not one 2102 of the allowed values, it **doesn't return anything**. Otherwise, returns 2103 whether the object is in the specified state. (Every second item in the list is 2104 the negation of the previous one, so `Closed` is checking for not `Open`, 2105 `Removed` is checking for not `Worn`, etc.) 2106 2107 ### CT_Loop_While 2108 Parameters are the same as [CT_Variable_Comparison](#CT_Variable_Comparison), 2109 execute `PassCommands` until variable comparison is true. `loop_item` is passed 2110 through unmodified. 2111 2112 ### CT_Loop_Characters 2113 * `Step2`: ignored 2114 * `Step3`: ignored 2115 * `Step4`: ignored 2116 2117 Iterate through each character in the game (skipping player, since player is not 2118 a character in rags), setting `loop_item`. 2119 2120 ### CT_Loop_Items 2121 * `Step2`: ignored 2122 * `Step3`: ignored 2123 * `Step4`: ignored 2124 2125 Iterate through each object in the game, setting `loop_item`. 2126 2127 ### CT_Loop_Item_Group 2128 * `Step2`: object group name (**case sensitive**) 2129 * `Step3`: ignored 2130 * `Step4`: ignored 2131 2132 Iterate through each object in the game. If the object is in the specified 2133 object group directly, call `PassCommands` setting `loop_item`. 2134 2135 ### CT_Loop_Item_Container 2136 * `Step2`: object UUID (**case sensitive**) 2137 * `Step3`: ignored 2138 * `Step4`: ignored 2139 2140 Iterate through each object in the game. If the object is in the specified 2141 object, call `PassCommands` setting `loop_item`. 2142 2143 ### CT_Loop_Item_Room 2144 * `Step2`: room UUID (**case sensitive**) 2145 * `Step3`: ignored 2146 * `Step4`: ignored 2147 2148 Iterate through each object in the game. If the object is in the specified 2149 room, call `PassCommands` setting `loop_item`. 2150 2151 ### CT_Loop_Item_Inventory 2152 * `Step2`: ignored 2153 * `Step3`: ignored 2154 * `Step4`: ignored 2155 2156 Iterate through each object in the game. If the object is in the player's 2157 inventory, call `PassCommands` setting `loop_item`. 2158 2159 ### CT_Loop_Item_Char_Inventory 2160 * `Step2`: character name (**case sensitive**) 2161 * `Step3`: ignored 2162 * `Step4`: ignored 2163 2164 Iterate through each object in the game. If the object is in the specified 2165 character's inventory, call `PassCommands` setting `loop_item`. 2166 2167 ### CT_Loop_Rooms 2168 * `Step2`: ignored 2169 * `Step3`: ignored 2170 * `Step4`: ignored 2171 2172 Iterate through each room in the game, setting `loop_item`. 2173 2174 ### CT_Loop_Exits 2175 * `Step2`: room UUID (**can be parsed**) or name (case insensitive) 2176 * `Step3`: ignored 2177 * `Step4`: ignored 2178 2179 If the specified room doesn't exists, doesn't do anything. Otherwise call 2180 `PassCommands` settings `loop_item` with each exit of the room (including 2181 disabled ones, i.e. the loop will be executed exactly 12 times, unless broken). 2182 2183 ### CT_MultiMedia_InGroup 2184 * `Step2`: name of the object (case insensitive) 2185 * `Step3`: group name (**case sensitive**) 2186 * `Step4`: ignored 2187 2188 Works similarly to [CT_Item_InGroup](#CT_Item_InGroup). 2189 2190 ### CT_Player_CustomPropertyCheck 2191 * `Step2`: `custom_property_name` 2192 * `Step3`: comparison type 2193 * `Step4`: value 2194 2195 [See for more info on CustomPropertyChecks](#CustomPropertyChecks). Since there 2196 is only one player in rags, `Step2` only contains a custom property name. Colons 2197 are treated as part of the property name! 2198 2199 ### CT_Player_Gender 2200 * `Step2`: `Male` / `Female` / anything else (**case sensitive**) 2201 * `Step3`: ignored 2202 * `Step4`: ignored 2203 2204 If `Step2` is neither `Male` nor `Female`, it's treated as `Other`. Returns 2205 whether the player's gender equals to `Step2`. 2206 2207 ### CT_Player_In_Room 2208 * `Step2`: room's UUID (**case sensitive**). Special values (not parsed, must 2209 match exactly): 2210 * `00000000-0000-0000-0000-000000000002` :: "void" room (crashes rags if you 2211 actually manage to put the player here, so probably can be ignored) 2212 * `Step3`: ignored 2213 * `Step4`: ignored 2214 * FIXUP: if `Step2` can't be parsed as an UUID, and there is a room with that 2215 name, replace `Step2` with the room's UUID. 2216 2217 Returns whether the player is currently in the specified room. 2218 2219 ### CT_Player_In_RoomGroup 2220 * `Step2`: room group or `None` (**case sensitive**) 2221 * `Step3`: ignored 2222 * `Step4`: ignored 2223 2224 (If the player is not in a room, throws an exception.) Returns whether player's 2225 current room's group's name equals to `Step2`. 2226 2227 [See also CT_Character_In_RoomGroup](#CT_Character_In_RoomGroup). 2228 2229 ### CT_Player_In_Same_Room_As 2230 * `Step2`: character name (case insensitive) 2231 * `Step3`: ignored 2232 * `Step4`: ignored 2233 2234 If the character doesn't exists, **throws an exception**. Returns whether the 2235 player and the specified character is in the same room. 2236 2237 ### CT_Player_Moving 2238 * `Step2`: `Empty` / `North` / `South` / ... (**case sensitive**) 2239 * `Step3`: ignored 2240 * `Step4`: ignored 2241 2242 If `Step2` is not a valid direction, **throws an exception**. Otherwise checks 2243 whether the player currently moves in the specified direction (only in room's on 2244 player leave events, otherwise the player is moving in the `Empty` direction). 2245 2246 ### CT_Room_CustomPropertyCheck 2247 * `Step2`: `room_uuid:custom_property_name` (case insensitive) 2248 * `Step3`: comparison type 2249 * `Step4`: value 2250 2251 [See for more info on CustomPropertyChecks](#CustomPropertyChecks). `room_uuid` 2252 can be `<CurrentRoom>` (**case sensitively!**) to mean the player's room, 2253 otherwise it is a room uuid. 2254 2255 ### CT_Timer_CustomPropertyCheck 2256 * `Step2`: `timer_name:custom_property_name` (case insensitive) 2257 * `Step3`: comparison type 2258 * `Step4`: value 2259 2260 [See for more info on CustomPropertyChecks](#CustomPropertyChecks). `timer_name` 2261 is a name of a timer. 2262 2263 ### CT_Variable_Comparison {#CT_Variable_Comparison} 2264 * `Step2`: variable name + optional indices (case insensitive) 2265 * `Step3`: `Equals` / `Not Equals` / `Greater Than` / `Greater Than or Equals` / 2266 `Less Than` / `Less Than or Equals` / `Contains` / `DayOfWeek Is` / `Hour 2267 Equals` / `Hour Is Greater Than` / `Hour Is Less Than` / `Minute Equals` / 2268 `Minute Is Greater Than` / `Minute Is Less Than` / `Seconds Equals` / `Seconds 2269 Is Greater Than` / `Seconds Is Less Than` (**case sensitive**) 2270 * `Step4`: value to compare with. **Double replaced!** 2271 2272 Optional index handling: everything before the first `(` is the variable name, 2273 without trimming. (No `(` -> full string is the variable name.) There is a `(` 2274 and a `)` afterwards: everything between them is converted to int32, if fails 2275 treat as no index. There is a second `(` and a `)` afterwards: do the same 2276 conversion. Examples: `foo(0)(0)` -> name `"foo"`, indices 0 and 0. `foo((2)` -> 2277 name `"foo "`, indices invalid and 2. 2278 2279 If the variable does not exists, **returns true**. Here is what happens when the 2280 number of indices specified doesn't match the variable type (invalid first and 2281 valid second indices: 2 idx, but later exception due to out of range): 2282 | Var type | 0 idx | 1 idx | 2 idx | 2283 |------------|---------|--------------------------------|-----------------------| 2284 | Num single | OK | garbage/**exception** | garbage/**exception** | 2285 | Num 1D | garbage | OK | **exception** | 2286 | Num 2D | garbage | **exception** | OK | 2287 | Str single | OK | garbage/**exception** | garbage/**exception** | 2288 | Str 1D | garbage | OK | **exception** | 2289 | Str 2D | garbage | `System.Collections.ArrayList` | OK | 2290 | DT single | OK | garbage/**exception** | garbage/**exception** | 2291 | DT 1D | garbage | OK | **exception** | 2292 | DT 2D | garbage | **exception** | OK | 2293 2294 Explanations: 2295 * Garbage in 0 idx column: rags doesn't store variable values in a union, and 2296 selecting a different type in rags designer doesn't clear out the values 2297 stored for other types. It also treats array and single as separate types. 2298 Thus when not specifying any index, rags will use this single type's value 2299 with whatever garbage value left behind. Scraps emulates this behavior. 2300 * garbage/exception: similar to the previous. If the variable happen to have a 2301 garbage array data, it will use it, otherwise exception. Scraps always throws 2302 an exception in this case. 2303 * `System.Collections.ArrayList`: rags will treat the variable as having this 2304 value. 2305 2306 If the index is out of range, it **throws an exception**. If `Step2` has an 2307 index, the value used to store single number values (refer to garbage in 0 idx 2308 column above) is **overwritten** with the current array cell value. (Unlike 2309 `CT_Variable_To_Variable_Comparison`, this is done for every type, not just 2310 numbers). 2311 2312 If an unknown/invalid comparison type is specified, it **returns true**. 2313 Different variable types support an arbitrary subset of the possible comparisons 2314 (O=supported, X=not supported): 2315 | Comparison | Num | Str | DT | 2316 |---------------------------|-----|-----|----| 2317 | `Equals` | O | O | O | 2318 | `Not Equals` | O | O | O | 2319 | `Greater Than` | O | O | O | 2320 | `Greater Than or Equals` | O | X | O | 2321 | `Less Than` | O | O | O | 2322 | `Less Than or Equals` | O | X | O | 2323 | `Contains` | X | O | X | 2324 | `DayOfWeek Is` | X | X | O | 2325 | `Hour Equals` | X | X | O | 2326 | `Hour Is Greater Than` | X | X | O | 2327 | `Hour Is Less Than` | X | X | O | 2328 | `Minute Equals` | X | X | O | 2329 | `Minute Is Greater Than` | X | X | O | 2330 | `Minute Is Less Than` | X | X | O | 2331 | `Seconds Equals` | X | X | O | 2332 | `Seconds Is Greater Than` | X | X | O | 2333 | `Seconds Is Less Than` | X | X | O | 2334 2335 In case of a number variable, `Step4` is converted to a double, if it fails it 2336 **throws an exception**. In case of a string variable, formatting macros are 2337 stripped from the variable's value ([see text.md for 2338 details](text.md#strip-format)), but not from `Step4`. Comparison is case 2339 insensitive. In case of a DateTime variable, normal comparisons are parsed with 2340 .NET's super permissive parser, hour/minute/second checks are parsed as int32, 2341 if it fails it **throws an exception**. DayOfWeek check is special, `Step4` is 2342 one of `Sunday` / `Monday` / `Tuesday` / `Wednesday` / `Thursday` / `Friday` / 2343 `Saturday` (checked case insensitively). 2344 2345 Finally it returns the result of the comparison. 2346 2347 ### CT_Variable_To_Variable_Comparison 2348 * `Step2`: variable name + optional indices (case insensitive) 2349 * `Step3`: `Equals` / `Not Equals` / `Greater Than` / `Greater Than or Equals` / 2350 `Less Than` / `Less Than or Equals` (**case sensitive**) 2351 * `Step4`: variable name + optional indices (case insensitive) 2352 2353 If the variable specified by `Step4` doesn't exists, **throws an exception**. If 2354 `Step4` is a DateTime variable, it is treated as having an empty string as 2355 value. Here is what happens, when the number of indices specified in `Step4` 2356 doesn't match the variable type: 2357 | Var type | 0 idx | 1 idx | 2 idx | 2358 |----------|---------|--------------------------------|-----------------------| 2359 | Single | OK | garbage/**exception** | garbage/**exception** | 2360 | 1D | garbage | OK | **exception** | 2361 | 2D | garbage | `System.Collections.ArrayList` | OK | 2362 2363 See [the previous section for explanations](#CT_Variable_Comparison). Rags 2364 converts number values to string, this is why you have 2365 `System.Collections.ArrayList` even with numbers. 2366 2367 If the variable specified by `Step2` doesn't exists or it is a DateTime 2368 variable, it **doesn't return anything**. Here is what happens when the indices 2369 don't match, this time for `Step2`: 2370 2371 | Var type | 0 idx | 1 idx | 2 idx | 2372 |------------|---------|--------------------------------|-----------------------| 2373 | Num single | OK | garbage/**exception** | garbage/**exception** | 2374 | Num 1D | garbage | OK | **exception** | 2375 | Num 2D | garbage | **exception** | OK | 2376 | Str single | OK | garbage/**exception** | garbage/**exception** | 2377 | Str 1D | garbage | OK | **exception** | 2378 | Str 2D | garbage | `System.Collections.ArrayList` | OK | 2379 2380 If `Step3` does not have an allowed value (string variables only support 2381 `Equals` and `Not Equals`), it **doesn't return anything**. 2382 2383 In case of a number variable, the string representation of whatever that was 2384 read from `Step4` is converted to a double, if this fails it is treated as 2385 `0.0`. If `Step2` has an index, the value used to store single number values 2386 (refer to garbage in 0 idx column above) is **overwritten** with the current 2387 array cell value. (This does not happen with string vars!) Afterward, it returns 2388 the value of the comparison. 2389 2390 In case of a string variable, formatting macros are stripped from `Step2`'s 2391 value ([see text.md for details](text.md#strip-format)), but not from `Step4`, 2392 then they're compared for equality. (Less than, greater than not supported). 2393 2394 ### CT_Variable_CustomPropertyCheck 2395 * `Step2`: `variable_name:custom_property_name` (case insensitive) 2396 * `Step3`: comparison type 2397 * `Step4`: value 2398 2399 [See for more info on CustomPropertyChecks](#CustomPropertyChecks). 2400 `variable_name` is a name of a variable and can contain indices, but they're 2401 ignored. 2402 2403 [text replacement function]: text.md#replacement