text.md (18499B)
1 # Generic 2 * Macro names are case insensitive 3 * There's a hidden input called `loopobject`, called `loop_item` in scraps 4 because it's not necessarily an object: it can be a room, exit, object or 5 character. 6 * A second hidden input is called `newvalue`, an optional string. If it is a 7 single space, it is treated as an empty string. This is only used by some of 8 the colon macros. 9 * Replacement is done right-to-left (probably because of `[a/an]`) 10 * Replacement is repeated if the leftmost macro with a colon in it has a valid 11 name (i.e. starts with `[V:`, `[JSA:`, `[RP:`, `[IP:`, `[PP:`, `[CP:`, `[VP:`, 12 `[TP:`, `[IA:`, `[TA:`, `[CA:`, `[RA:`, `[PA:`). 13 14 So for example, with a player name `<[playername][v:]>`, the following results 15 in an infinite loop: 16 17 * `[playername][v:]` 18 19 The following ones does not result in an infinite loop: 20 21 * `[a:b][playername][v:]` (expands to `[a:b]<[playername][v:]>`) 22 * `[playername]` (expands to `<[playername][v:]>`) 23 * If the replaced text forms a valid macro with the text before it, it is 24 replaced. So with playername `name]`, `[player[playername]` is expanded to 25 `name]`. 26 * This is also buggy a bit, since macro names without colons are checked with 27 contains instead of ==, but then a fixed number of characters are removed. As 28 a consequence, with playername `[playername]`, `[[[[[playername]` expands to 29 `[playername]]]]]`. But macros with colon work differently, with a variable 30 `tex` set to `[v:tex]`, `[[[[v:tex]` expands to `[[[[v:tex]`. (If you somehow 31 solve the problem of Rags Designer freezing when you set that variable...) 32 * Some actions call the replacement function multiple times, so they will repeat 33 the replacement even without a colon macro, but without a `loop_item`. 34 * When adding text to the log, the replacement function is called (without 35 `loop_item`) until the output changes. So with playername `[playername]`, 36 `[playername]` is expanded to `[playername]`, but if the playername is 37 `[playername]x`, it's infinite loop. 38 * Rags author, I hope you fucking die. 39 40 # Replacements {#replacement} 41 * `[V:` var `]`: `var` or `var(0)` or `var(0)(0)`, but indices are substituted 42 again. (Note: this means that due to macros being replaced from right-to-left, 43 indices are essentially expanded twice. So for example having a player name 44 `[v:foo]` and a variable `foo` set to some valid index in `bar`, 45 `[v:bar([playername])]` will work correctly, even though normally 46 `[playername]` would be just replaced with `[v:foo]`.) Num and string works 47 the same way. Non existing var: empty replacement. In case of date variable, a 48 date format can be specified after a colon: `[v:dat:yyyy]`, default if not 49 specified: `dddd, MMMM, dd yyyy hh:mm:ss tt`. Invalid format exceptions are 50 not caught. See 51 [short](https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) 52 [(archive)](https://archive.md/jnRFV) and 53 [long](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings) 54 [(archive)](https://archive.md/s9yIZ) format strings. 55 56 Some undocumented features of .NET: escape works inside quotes, unmatched 57 quotes or an ending a format string with an escape character results in an 58 exception. `%` only works when it is the first character, and whatever follows 59 it will be treated as a single character, exception otherwise. (A single `%` 60 is also invalid, `%dd` is interpreted as `d''d`.) 61 62 inval1st: the first index is invalid, second doesn't care 63 64 inval2nd: first index is valid, second is not (or 1D array) 65 66 | var type | 0idx | 1idx val | 1idx inval | 2idx val | 2idx inval1st | 2idx inval2nd | 67 |------------|------|----------------------------------|------------|----------|---------------|----------------------------------| 68 | num | val | N/A | `""` | N/A | `""` | N/A | 69 | num ary 1D | `""` | value | `""` | N/A | `""` | `"<invalid variable reference>"` | 70 | num ary 2D | `""` | `"System.Collections.ArrayList"` | `""` | value | `""` | `"<invalid variable reference>"` | 71 | dt | val | N/A | `""` | N/A | `""` | N/A | 72 | dt ary 1D | `""` | value | `""` | N/A | `""` | **exception** | 73 | dt ary 2D | `""` | **exception** | `""` | valid | `""` | **exception** | 74 75 Here **exception** means that the replacing function throws an exception, in 76 most cases this means Rags player pops up that usual .net uncaught exception 77 window, but they're caugh in some cases (for example, with actions). Scraps 78 duplicates the behaviour of num/str arrays instead of throwing. 79 80 Invalid array handling: 1D invalid parentheses: `[v:foo(]` => `foo(]`. 2D is 81 more complicated, if the variable doesn't exists: `[v:foo(0)(]` => `foo(0)(]`, 82 but if it exists it's replaced to value with `]` appended. If you have an 83 array named `asd)`, `[v:asd)(0)]` results in `asd)(0)]`. The second `(` is not 84 checked, `[v:asd(0)(0)]` and `[v:asd(0)x0)]` is the same. Extra characters 85 after the `)` are not handled, so `[v:foo(0)xyz]` or `[v:foo(0)(0)xyz]` with 86 value `foo` will be replaced with value `fooyz]`. A negative index is treated 87 the same way as an invalid index. 88 89 Whitespace handling: spaces after `[v:` are allowed and before the first `(` 90 (or `]` when there is no array indexing), but not around the `:` separating 91 the variable name and date format. It's also allowed inside the parentheses 92 but not outside (so `[v:foo( 0 )( 0 )]` is ok, `[v:foo(0) (0)]` is not. 93 94 `newval` handling: the macro expands to nothing. In case of `<invalid variable 95 reference>` it doesn't do anything, exceptions are propagated and variable not 96 set. In case of a 2D array it just sets the value to the string (which can 97 cause parse errors later, i.e. fuckup), with 1D array: invalid number **throws 98 an exception**, invalid dt: store the `"bad value"` string as a DateTime value 99 (major **fuckup**?). With single values, invalid numbers are replaced with -1, 100 invalid DateTimes are replaced with the current time. 101 * `[JSA:` varname `]`: "export" to JS array. Non array vars expand to empty 102 string. 1D array => `[1,2,3];` (yes, with a fucking semicolon at the end), 2D 103 => `[[1,2],[3,4]];`. Strings are double quoted (`["ab","cd"];`), quotes are 104 escaped (`a"b` => `["a\"b"];`), carriage returns/newlines are replaced with 105 `\r` and `\n` but other characters are not (so `\` remains `\`). Dates are 106 exported as strings, `month/day/year hour:minute:second PM` in 1D array, 107 whatever string representation they're stored in in 2D case... 108 109 Whitespace handling: spaces around varname are trimmed. `newvalue` is ignored. 110 * `[RP:` name `:` propname `]`: Room Property. If room name contains a `(`, that 111 character and everything after is stripped away and the remaining is an UUID. 112 Otherwise it's a room name. Second name is the custom property name. If the 113 macro has a third `:`, it an everything after it is ignored. Non-existing 114 room/property => empty string. `newvalue` is handled if the property is found. 115 116 Whitespace handling: spaces around name and propname are allowed. 117 * `[IP:` name `:` propname `]`: object (Item) Property. Works as `RP`. 118 * `[PP:` propname `]`: Get property from player. Empty if missing. `:` is not 119 special in this case, it's treated as a part of propname. 120 * `[CP:` name `:` propname `]`: Character Property. Same as `RP` except no UUID 121 handling. 122 * `[VP:` name `:` propname `]`: Variable Property. Works as `CP`. 123 * `[TP:` name `:` propname `]`: Timer Property. Works as `CP`. 124 * `[IA:` name `:` attrname `]`: object (Item) Attribute. Name is an UUID, 125 failing that a name. An extra `:` and everything after is ignored. Attr can 126 be: 127 | attrname | value | `newvalue` | 128 |----------------|---------------------------------|------------| 129 | NAME | object name (override ignored!) | set | 130 | NAMEOVERRIDE | name override | set | 131 | CLOTHINGLAYERS | `[["name","level"], ...]` | ignored | 132 | ID | UUID | ignored | 133 | PREPOSITION | preposition | set | 134 | DESCRIPTION | description | set | 135 | IMPORTANT | True/False | ToBoolean | 136 | CARRYABLE | True/False | ToBoolean | 137 | WEARABLE | True/False | ToBoolean | 138 | WORN | True/False | ToBoolean | 139 | CONTAINER | True/False | ToBoolean | 140 | OPENABLE | True/False | ToBoolean | 141 | OPEN | True/False | ToBoolean | 142 | LOCKABLE | True/False | ToBoolean | 143 | LOCKED | True/False | ToBoolean | 144 | VISIBLE | True/False | ToBoolean | 145 | ENTERABLE | True/False | ToBoolean | 146 | READABLE | True/False | ToBoolean | 147 | WEIGHT | object weight (double) | ToDouble | 148 | ACTION | Special, see below | buggy | 149 150 ToBoolean: after whitespace trimming, it must be case-insensitively `true` or 151 `false`, otherwise it throws an exception. 152 153 Conversion errors are catched and have no effect. 154 155 Whitespace handling: spaces around name allowed, but not around attrname. 156 * `[TA:` name `:` attrname `]`: Timer Attribute. Attr can be: 157 | attrname | value | `newvalue` | 158 |--------------|------------------------|------------| 159 | ACTIVE | True/False | ToBoolean | 160 | NAME | timer name | set | 161 | TIMERTYPE | TT_RUNALWAYS/TT_LENGTH | enum parse | 162 | LENGTH | integer length | ToInt32 | 163 | TURNNUMBER | integer turn | ToInt32 | 164 | TIMERSECONDS | integer seconds | ToInt32 | 165 | LIVETIMER | True/False | ToBool | 166 | ACTION | Special, see below | buggy | 167 168 Note that setting LIVETIMER won't start/stop existing background thread! So 169 setting a normal timer to live timer effectively disables it, while setting a 170 live timer to a normal means that it will continue to be a live timer, but 171 also a normal timer that is executed normally after actions. But after saving 172 and loading the game, the threads are recreated normally. 173 * `[CA:` name `:` attrname `]`: Character Attribute. Attr can be: 174 | attrname | value | `newvalue` | 175 |---------------------|-------------------------------|----------------| 176 | NAME | char name (override ignored!) | set | 177 | OVERRIDENAME | name override | set | 178 | GENDER | Male/Female/Other | enum parse | 179 | CURRENTROOM | current room's UUID | set (no check) | 180 | ALLOWINVINTERACTION | True/False | ToBoolean | 181 | ACTION | Special, see below | buggy | 182 * `[RA:` name `:` attrname `]`: Room Attribute. Name is an UUID, failing that a 183 name. Attr can be: 184 | attrname | value | `newvalue` | 185 |------------------|-------------------------------|----------------| 186 | NAME | room name (override ignored!) | set | 187 | DESCRIPTION | room description | set | 188 | SDESC | name override | set | 189 | ROOMPIC | picture filename or `None` | set (no check) | 190 | ENTEREDFIRSTTIME | True/False | ToBoolean | 191 | LEFTFIRSTTIME | True/False | ToBoolean | 192 | ID | UUID | ignored | 193 | ACTION | Special, see below | buggy | 194 | EXIT | Special, see below | | 195 196 `[RA:` name `:EXIT:` dir `:` attrname `]`: dir is `North`, `SouthEast`, etc. 197 dir is case sensitive! Spaces around dir are allowed. Invalid dir or not 198 specifying dir at all (e.g. `[ra:foo:exit]`) results in an empty string, but 199 specifying dir and not specifying attrname results in an exception... 200 | exit attrname | value | `newvalue` | 201 |-----------------|----------------------------------------------------|----------------| 202 | ACTIVE | True/False | ToBoolean | 203 | DESTINATIONID | destination room UUID or empty | set (no check) | 204 | DESTINATIONNAME | destination room name (override ignored!) or empty | set (if exist) | 205 | PORTAL | portal object's UUID or `<None>` | set (no check) | 206 * `[PA:` attrname `]`: Player Attribute. Attr can be: 207 | attrname | value | `newvalue` | 208 |-------------|---------------------------------|----------------| 209 | NAME | player name | set | 210 | GENDER | Male/Female/Other | enum parse | 211 | CURROOM | current room's UUID or `<Null>` | set (no check) | 212 | CURPORTRAIT | player image or empty | set (no check) | 213 | ACTION | Special, see below | buggy | 214 * `[PLAYERNAME]`: Player's NameOverride 215 * `[INPUTDATA]`: aka AdditionalData aka last selected shit 216 * `[MAXCARRY]`: Player's WeightLimit 217 * `[TURNS]`: TurnCount 218 * `[CURRENTCARRY]`: sum of weight in Player's inventory 219 * `[MAN/WOMAN]`: Player's gender: man, woman, thing 220 * `[MALE/FEMALE]`: Player's gender: male, female, other 221 * `[BOY/GIRL]`: Player's gender: boy, girl, thing 222 * `[LEN(` varname `)]`: String var: string len, array var: number of rows. Only 223 works at the beginning of the replacement string! If there are any characters 224 before the `[LEN(` part, it breaks. 225 226 Error behavior: if variable is a number of datetime (and not an array), 227 expands to empty. If the variable doesn't exists, five characters after the 228 `(` are removed, e.g. `[len(nosuch)]` => `[len(h)]`. If there are less than 5 229 characters after the `(`, Rags throws an exception. If the closing `)` is 230 missing, Rags removes the `[len(` part. 231 232 `[len(` not at the beginning at the string: see the source for exact details, 233 but unless you do that multiple eval trick with `[[`, Rags will assume that 234 `[len(` is at the beginning of the full string, and the variable name will be 235 taken from the 5th character until the `(`. So for example, if you have an 236 array variable called `len` with length 3, `....[len(foo)]>a` will be replaced 237 with `3>a` (if you have one less extra character after the macro, Rags 238 throws an exception). 239 * `[A/AN]`: expands to `an` if the text following `[A/AN]` after expansion 240 (skipping any whitespace characters) starts with any of `aiueo`, `a` if 241 something else, nothing if there's no text after it. 242 243 These operate on `loop_item` if it is a room, player's room otherwise 244 (`loop_item` is unset or it is not a room). 245 * `[ROOM.NAME]`: room's name (override ignored!) 246 * `[ROOM.ID]`: room's UUID 247 248 These operate on `loop_item` if it is a room exit, expand to nothing otherwise. 249 * `[EXIT.ACTIVE]`: Y/N 250 * `[EXIT.DIRECTION]`: North, South, NorthEast, etc 251 * `[EXIT.DESTNAME]`: destination room (name override || name) if it exists, 252 empty otherwise 253 * `[EXIT.DESTID]`: destination room's UUID if it exists, empty otherwise 254 255 These operate on `loop_item` if it is an object, expand to nothing otherwise. 256 * `[ITEM.NAME]`: object's name (override ignored!) 257 * `[ITEM.ID]`: object's UUID 258 259 These operate on `loop_item` if it is a character, expand to nothing otherwise. 260 * `[CHAR.NAME]`: character's name (override ignored!) 261 262 ## Action attributes 263 Various `[.A:.*]` can take `ACTION` as an attrname. For example: 264 `[CA:` name `:ACTION:` actname `:` attrname `]`: actname is an action name, 265 attrname can be: 266 | attrname | value | `newvalue` | 267 |--------------|---------------------------------------------------------------|---------------=| 268 | ACTIVE | True/False | ToBoolean | 269 | OVERRIDENAME | override name | set | 270 | ACTIONPARENT | action's parent name or `None` | set (no check) | 271 | INPUTTYPE | None/Object/Character/ObjectOrCharacter/Text/Custom/Inventory | enum parse | 272 273 Not specifying enough colons result in an exception. 274 275 `newvalue`: because Rags author lacked brain cells, an empty (and as an 276 extension, originally just a single space) `newvalues` are treated as if it 277 weren't specified. 278 279 # Rich text 280 Note: where you see a comma separated list, you can always pass more elements, 281 extra elements are ignored. 282 283 * `[c` num `,` num `,` num `]` ... `[/c]`: change color to RGB 284 * `[c` string `]` ... `[/c]`: change to named color 285 * `[f` string [ `,` double ] `]` ... `[/f]`: change to font, optionally size. 286 * `[b]` ... `[/b]`: bold 287 * `[i]` ... `[/i]`: italic 288 * `[u]` ... `[/u]`: underline 289 * `[Middle]` ... `[/Middle]`: centered 290 291 ## Stripping formatting macros {#strip-format} 292 Sometimes formatting macros are stripped from the text. The algorithm is the 293 following (string comparisons are case insensitive): 294 1. Find `[f`. If found, find the next `]`. If there is no `]` and `[f` is at 295 the beginning of the string, *infinite loop*. If there is no `]` and `[f` is 296 not at the beginning of the string, *throw an exception*. Otherwise remove 297 `[f` `]` and everything between. 298 299 If there is a `[/f]` after this, remove it too. 300 301 Repeat 1. while there are any `[f` in the string. 302 2. Do the same with `[c` `]` and `[/c]`. 303 3. Remove any instance of `[middle]`, `[/middle]`, `[i]`, `[/i]`, `[b]`, `[/b]`, 304 `[u]`, `[/u]`. Unlike in step 1-2, the end tags don't have to follow an open 305 tag. 306 307 Order matters! `[[i]b]` after stripping is an empty string, while `[[b]i]` is 308 `[i]`.