scraps

Abandon all hope, ye who enter here.
git clone https://git.neptards.moe/neptards/scraps.git
Log | Files | Refs | Submodules | README | LICENSE

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]`.