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

309 lines
18 KiB
Markdown

# Generic
* Macro names are case insensitive
* There's a hidden input called `loopobject`, called `loop_item` in scraps
because it's not necessarily an object: it can be a room, exit, object or
character.
* A second hidden input is called `newvalue`, an optional string. If it is a
single space, it is treated as an empty string. This is only used by some of
the colon macros.
* Replacement is done right-to-left (probably because of `[a/an]`)
* Replacement is repeated if the leftmost macro with a colon in it has a valid
name (i.e. starts with `[V:`, `[JSA:`, `[RP:`, `[IP:`, `[PP:`, `[CP:`, `[VP:`,
`[TP:`, `[IA:`, `[TA:`, `[CA:`, `[RA:`, `[PA:`).
So for example, with a player name `<[playername][v:]>`, the following results
in an infinite loop:
* `[playername][v:]`
The following ones does not result in an infinite loop:
* `[a:b][playername][v:]` (expands to `[a:b]<[playername][v:]>`)
* `[playername]` (expands to `<[playername][v:]>`)
* If the replaced text forms a valid macro with the text before it, it is
replaced. So with playername `name]`, `[player[playername]` is expanded to
`name]`.
* This is also buggy a bit, since macro names without colons are checked with
contains instead of ==, but then a fixed number of characters are removed. As
a consequence, with playername `[playername]`, `[[[[[playername]` expands to
`[playername]]]]]`. But macros with colon work differently, with a variable
`tex` set to `[v:tex]`, `[[[[v:tex]` expands to `[[[[v:tex]`. (If you somehow
solve the problem of Rags Designer freezing when you set that variable...)
* Some actions call the replacement function multiple times, so they will repeat
the replacement even without a colon macro, but without a `loop_item`.
* When adding text to the log, the replacement function is called (without
`loop_item`) until the output changes. So with playername `[playername]`,
`[playername]` is expanded to `[playername]`, but if the playername is
`[playername]x`, it's infinite loop.
* Rags author, I hope you fucking die.
# Replacements {#replacement}
* `[V:` var `]`: `var` or `var(0)` or `var(0)(0)`, but indices are substituted
again. (Note: this means that due to macros being replaced from right-to-left,
indices are essentially expanded twice. So for example having a player name
`[v:foo]` and a variable `foo` set to some valid index in `bar`,
`[v:bar([playername])]` will work correctly, even though normally
`[playername]` would be just replaced with `[v:foo]`.) Num and string works
the same way. Non existing var: empty replacement. In case of date variable, a
date format can be specified after a colon: `[v:dat:yyyy]`, default if not
specified: `dddd, MMMM, dd yyyy hh:mm:ss tt`. Invalid format exceptions are
not caught. See
[short](https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings)
[(archive)](https://archive.md/jnRFV) and
[long](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings)
[(archive)](https://archive.md/s9yIZ) format strings.
Some undocumented features of .NET: escape works inside quotes, unmatched
quotes or an ending a format string with an escape character results in an
exception. `%` only works when it is the first character, and whatever follows
it will be treated as a single character, exception otherwise. (A single `%`
is also invalid, `%dd` is interpreted as `d''d`.)
inval1st: the first index is invalid, second doesn't care
inval2nd: first index is valid, second is not (or 1D array)
| var type | 0idx | 1idx val | 1idx inval | 2idx val | 2idx inval1st | 2idx inval2nd |
|------------|------|----------------------------------|------------|----------|---------------|----------------------------------|
| num | val | N/A | `""` | N/A | `""` | N/A |
| num ary 1D | `""` | value | `""` | N/A | `""` | `"<invalid variable reference>"` |
| num ary 2D | `""` | `"System.Collections.ArrayList"` | `""` | value | `""` | `"<invalid variable reference>"` |
| dt | val | N/A | `""` | N/A | `""` | N/A |
| dt ary 1D | `""` | value | `""` | N/A | `""` | **exception** |
| dt ary 2D | `""` | **exception** | `""` | valid | `""` | **exception** |
Here **exception** means that the replacing function throws an exception, in
most cases this means Rags player pops up that usual .net uncaught exception
window, but they're caugh in some cases (for example, with actions). Scraps
duplicates the behaviour of num/str arrays instead of throwing.
Invalid array handling: 1D invalid parentheses: `[v:foo(]` => `foo(]`. 2D is
more complicated, if the variable doesn't exists: `[v:foo(0)(]` => `foo(0)(]`,
but if it exists it's replaced to value with `]` appended. If you have an
array named `asd)`, `[v:asd)(0)]` results in `asd)(0)]`. The second `(` is not
checked, `[v:asd(0)(0)]` and `[v:asd(0)x0)]` is the same. Extra characters
after the `)` are not handled, so `[v:foo(0)xyz]` or `[v:foo(0)(0)xyz]` with
value `foo` will be replaced with value `fooyz]`. A negative index is treated
the same way as an invalid index.
Whitespace handling: spaces after `[v:` are allowed and before the first `(`
(or `]` when there is no array indexing), but not around the `:` separating
the variable name and date format. It's also allowed inside the parentheses
but not outside (so `[v:foo( 0 )( 0 )]` is ok, `[v:foo(0) (0)]` is not.
`newval` handling: the macro expands to nothing. In case of `<invalid variable
reference>` it doesn't do anything, exceptions are propagated and variable not
set. In case of a 2D array it just sets the value to the string (which can
cause parse errors later, i.e. fuckup), with 1D array: invalid number **throws
an exception**, invalid dt: store the `"bad value"` string as a DateTime value
(major **fuckup**?). With single values, invalid numbers are replaced with -1,
invalid DateTimes are replaced with the current time.
* `[JSA:` varname `]`: "export" to JS array. Non array vars expand to empty
string. 1D array => `[1,2,3];` (yes, with a fucking semicolon at the end), 2D
=> `[[1,2],[3,4]];`. Strings are double quoted (`["ab","cd"];`), quotes are
escaped (`a"b` => `["a\"b"];`), carriage returns/newlines are replaced with
`\r` and `\n` but other characters are not (so `\` remains `\`). Dates are
exported as strings, `month/day/year hour:minute:second PM` in 1D array,
whatever string representation they're stored in in 2D case...
Whitespace handling: spaces around varname are trimmed. `newvalue` is ignored.
* `[RP:` name `:` propname `]`: Room Property. If room name contains a `(`, that
character and everything after is stripped away and the remaining is an UUID.
Otherwise it's a room name. Second name is the custom property name. If the
macro has a third `:`, it an everything after it is ignored. Non-existing
room/property => empty string. `newvalue` is handled if the property is found.
Whitespace handling: spaces around name and propname are allowed.
* `[IP:` name `:` propname `]`: object (Item) Property. Works as `RP`.
* `[PP:` propname `]`: Get property from player. Empty if missing. `:` is not
special in this case, it's treated as a part of propname.
* `[CP:` name `:` propname `]`: Character Property. Same as `RP` except no UUID
handling.
* `[VP:` name `:` propname `]`: Variable Property. Works as `CP`.
* `[TP:` name `:` propname `]`: Timer Property. Works as `CP`.
* `[IA:` name `:` attrname `]`: object (Item) Attribute. Name is an UUID,
failing that a name. An extra `:` and everything after is ignored. Attr can
be:
| attrname | value | `newvalue` |
|----------------|---------------------------------|------------|
| NAME | object name (override ignored!) | set |
| NAMEOVERRIDE | name override | set |
| CLOTHINGLAYERS | `[["name","level"], ...]` | ignored |
| ID | UUID | ignored |
| PREPOSITION | preposition | set |
| DESCRIPTION | description | set |
| IMPORTANT | True/False | ToBoolean |
| CARRYABLE | True/False | ToBoolean |
| WEARABLE | True/False | ToBoolean |
| WORN | True/False | ToBoolean |
| CONTAINER | True/False | ToBoolean |
| OPENABLE | True/False | ToBoolean |
| OPEN | True/False | ToBoolean |
| LOCKABLE | True/False | ToBoolean |
| LOCKED | True/False | ToBoolean |
| VISIBLE | True/False | ToBoolean |
| ENTERABLE | True/False | ToBoolean |
| READABLE | True/False | ToBoolean |
| WEIGHT | object weight (double) | ToDouble |
| ACTION | Special, see below | buggy |
ToBoolean: after whitespace trimming, it must be case-insensitively `true` or
`false`, otherwise it throws an exception.
Conversion errors are catched and have no effect.
Whitespace handling: spaces around name allowed, but not around attrname.
* `[TA:` name `:` attrname `]`: Timer Attribute. Attr can be:
| attrname | value | `newvalue` |
|--------------|------------------------|------------|
| ACTIVE | True/False | ToBoolean |
| NAME | timer name | set |
| TIMERTYPE | TT_RUNALWAYS/TT_LENGTH | enum parse |
| LENGTH | integer length | ToInt32 |
| TURNNUMBER | integer turn | ToInt32 |
| TIMERSECONDS | integer seconds | ToInt32 |
| LIVETIMER | True/False | ToBool |
| ACTION | Special, see below | buggy |
Note that setting LIVETIMER won't start/stop existing background thread! So
setting a normal timer to live timer effectively disables it, while setting a
live timer to a normal means that it will continue to be a live timer, but
also a normal timer that is executed normally after actions. But after saving
and loading the game, the threads are recreated normally.
* `[CA:` name `:` attrname `]`: Character Attribute. Attr can be:
| attrname | value | `newvalue` |
|---------------------|-------------------------------|----------------|
| NAME | char name (override ignored!) | set |
| OVERRIDENAME | name override | set |
| GENDER | Male/Female/Other | enum parse |
| CURRENTROOM | current room's UUID | set (no check) |
| ALLOWINVINTERACTION | True/False | ToBoolean |
| ACTION | Special, see below | buggy |
* `[RA:` name `:` attrname `]`: Room Attribute. Name is an UUID, failing that a
name. Attr can be:
| attrname | value | `newvalue` |
|------------------|-------------------------------|----------------|
| NAME | room name (override ignored!) | set |
| DESCRIPTION | room description | set |
| SDESC | name override | set |
| ROOMPIC | picture filename or `None` | set (no check) |
| ENTEREDFIRSTTIME | True/False | ToBoolean |
| LEFTFIRSTTIME | True/False | ToBoolean |
| ID | UUID | ignored |
| ACTION | Special, see below | buggy |
| EXIT | Special, see below | |
`[RA:` name `:EXIT:` dir `:` attrname `]`: dir is `North`, `SouthEast`, etc.
dir is case sensitive! Spaces around dir are allowed. Invalid dir or not
specifying dir at all (e.g. `[ra:foo:exit]`) results in an empty string, but
specifying dir and not specifying attrname results in an exception...
| exit attrname | value | `newvalue` |
|-----------------|----------------------------------------------------|----------------|
| ACTIVE | True/False | ToBoolean |
| DESTINATIONID | destination room UUID or empty | set (no check) |
| DESTINATIONNAME | destination room name (override ignored!) or empty | set (if exist) |
| PORTAL | portal object's UUID or `<None>` | set (no check) |
* `[PA:` attrname `]`: Player Attribute. Attr can be:
| attrname | value | `newvalue` |
|-------------|---------------------------------|----------------|
| NAME | player name | set |
| GENDER | Male/Female/Other | enum parse |
| CURROOM | current room's UUID or `<Null>` | set (no check) |
| CURPORTRAIT | player image or empty | set (no check) |
| ACTION | Special, see below | buggy |
* `[PLAYERNAME]`: Player's NameOverride
* `[INPUTDATA]`: aka AdditionalData aka last selected shit
* `[MAXCARRY]`: Player's WeightLimit
* `[TURNS]`: TurnCount
* `[CURRENTCARRY]`: sum of weight in Player's inventory
* `[MAN/WOMAN]`: Player's gender: man, woman, thing
* `[MALE/FEMALE]`: Player's gender: male, female, other
* `[BOY/GIRL]`: Player's gender: boy, girl, thing
* `[LEN(` varname `)]`: String var: string len, array var: number of rows. Only
works at the beginning of the replacement string! If there are any characters
before the `[LEN(` part, it breaks.
Error behavior: if variable is a number of datetime (and not an array),
expands to empty. If the variable doesn't exists, five characters after the
`(` are removed, e.g. `[len(nosuch)]` => `[len(h)]`. If there are less than 5
characters after the `(`, Rags throws an exception. If the closing `)` is
missing, Rags removes the `[len(` part.
`[len(` not at the beginning at the string: see the source for exact details,
but unless you do that multiple eval trick with `[[`, Rags will assume that
`[len(` is at the beginning of the full string, and the variable name will be
taken from the 5th character until the `(`. So for example, if you have an
array variable called `len` with length 3, `....[len(foo)]>a` will be replaced
with `3>a` (if you have one less extra character after the macro, Rags
throws an exception).
* `[A/AN]`: expands to `an` if the text following `[A/AN]` after expansion
(skipping any whitespace characters) starts with any of `aiueo`, `a` if
something else, nothing if there's no text after it.
These operate on `loop_item` if it is a room, player's room otherwise
(`loop_item` is unset or it is not a room).
* `[ROOM.NAME]`: room's name (override ignored!)
* `[ROOM.ID]`: room's UUID
These operate on `loop_item` if it is a room exit, expand to nothing otherwise.
* `[EXIT.ACTIVE]`: Y/N
* `[EXIT.DIRECTION]`: North, South, NorthEast, etc
* `[EXIT.DESTNAME]`: destination room (name override || name) if it exists,
empty otherwise
* `[EXIT.DESTID]`: destination room's UUID if it exists, empty otherwise
These operate on `loop_item` if it is an object, expand to nothing otherwise.
* `[ITEM.NAME]`: object's name (override ignored!)
* `[ITEM.ID]`: object's UUID
These operate on `loop_item` if it is a character, expand to nothing otherwise.
* `[CHAR.NAME]`: character's name (override ignored!)
## Action attributes
Various `[.A:.*]` can take `ACTION` as an attrname. For example:
`[CA:` name `:ACTION:` actname `:` attrname `]`: actname is an action name,
attrname can be:
| attrname | value | `newvalue` |
|--------------|---------------------------------------------------------------|---------------=|
| ACTIVE | True/False | ToBoolean |
| OVERRIDENAME | override name | set |
| ACTIONPARENT | action's parent name or `None` | set (no check) |
| INPUTTYPE | None/Object/Character/ObjectOrCharacter/Text/Custom/Inventory | enum parse |
Not specifying enough colons result in an exception.
`newvalue`: because Rags author lacked brain cells, an empty (and as an
extension, originally just a single space) `newvalues` are treated as if it
weren't specified.
# Rich text
Note: where you see a comma separated list, you can always pass more elements,
extra elements are ignored.
* `[c` num `,` num `,` num `]` ... `[/c]`: change color to RGB
* `[c` string `]` ... `[/c]`: change to named color
* `[f` string [ `,` double ] `]` ... `[/f]`: change to font, optionally size.
* `[b]` ... `[/b]`: bold
* `[i]` ... `[/i]`: italic
* `[u]` ... `[/u]`: underline
* `[Middle]` ... `[/Middle]`: centered
## Stripping formatting macros {#strip-format}
Sometimes formatting macros are stripped from the text. The algorithm is the
following (string comparisons are case insensitive):
1. Find `[f`. If found, find the next `]`. If there is no `]` and `[f` is at
the beginning of the string, *infinite loop*. If there is no `]` and `[f` is
not at the beginning of the string, *throw an exception*. Otherwise remove
`[f` `]` and everything between.
If there is a `[/f]` after this, remove it too.
Repeat 1. while there are any `[f` in the string.
2. Do the same with `[c` `]` and `[/c]`.
3. Remove any instance of `[middle]`, `[/middle]`, `[i]`, `[/i]`, `[b]`, `[/b]`,
`[u]`, `[/u]`. Unlike in step 1-2, the end tags don't have to follow an open
tag.
Order matters! `[[i]b]` after stripping is an empty string, while `[[b]i]` is
`[i]`.