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.

18 KiB

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

  • [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 (archive) and long (archive) 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

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