Tags and Mappings

Tags are names that expand into a set of values when defining objects and applying rules. Mappings are a conversion from one set of tag values to another. Together they allow the creation of "families" of related objects and shorter code to work with them.

Declaring tags

You declare them in a TAGS section. So something like:

TAGS
Activity = Active Inactive

will declare a tag set named Activity with two possible values Active and Inactive.

You can reuse tags and tag sets in the declaration of other tag sets, so:

Polarity = Positive Negative
Charge = Polarity Neutral
is equivalent to:
Polarity = Positive Negative
Charge = Positive Negative Neutral

The directions tag set is predefined. It provides the tags up, right, down, left as you would expect, but also has horizontal and vertical as sub-sets.

Tags in object names

You use tags in objects names. So we can have Player:Neutral, Player:Negative, Player:Positive as objects. Internally, that's EXACTLY the same as writing PlayerNeutral, PlayerNegative, PlayerPositive without the : and without declaring the tags. The main benefit is that you can use tags as rule parameters and in mappings, as we will see below. But it is also useful as you can now declare directly Player:Charge to declare the three types of players at the same time. And Player:Charge gets automatically declared as a property too, i.e., it's as if it automatically added this for you in the LEGEND section:

Player:Charge = Player:Positive or Player:Negative or Player:Neutral
Remember that directions is a predefined tag set, so if you want to have an object that has an associated direction, just write something like this:
Rocket:directions
red

Instead of:

Rocket:up
red
Rocket:right
red
...

If you add a sprite in this declaration, all four variants of the rocket will use that sprite, but we will see soon the amazing technique of transforming that base sprite in different ways for each object (for instance, rotating the sprite so that the rocket points in the right direction).

There are a few subtleties. First, Player:Charge declares all three types of players but marks them as implicitly declared, so that you can redeclare them explicitly after that. The goal is that you can provide a default palette and sprite for Player:Charge, then change the palette and sprite for a specific player object. You can also do the opposite: declare first an object like Player:up then use the builtin directions tag set to declare Player:directions. In an implicit declaration like that, the objects that have already been declared explicitly will not be redeclared. It is useful if you want to create the sprites for some direction of the player by copying and rotating the sprite for Player:up.

Tags and collision layers

If you use something like Player:Charge in a collision layer, it will do as if Player:Charge was defined as the following property:

Player:Charge = Player:Positive or Player:Negative or Player:Neutral.

And it will thus put all three types of players in the same collision layer. But sometimes you want things to go in different layers, and you can use another kind of parameter expansion for that. If you write:

Charge -> Player:Charge Crate:Charge

It will generate three collisions layers (one for each value of Charge), so that it's equivalent to:

Player:Positive Crate:Positive
Player:Negative Crate:Negative
Player:Neutral  Crate:Neutral

Declaring mappings

You declare mappings in a MAPPINGS section with a syntax like this.

Polarity => OpposedPolarity
Positive Negative -> Negative Positive

Here, we declare a mapping named OpposedPolarity that maps the tags in the Polarity tag set to their opposites. The second line gives the mapping value by value.

Note that mappings can map tags to tag sets, and this is a very useful advanced technique. For instance, the predefined mappings perpendicular could be defined manually like that:

directions => parallel
up down right left -> vertical vertical horizontal horizontal
  
directions => perpendicular
up down right left -> horizontal horizontal vertical vertical

Using mappings

You use mappings in rules (and a few other places) like this.

Polarity [Crate:Polarity] -> [Crate:OpposedPolarity]

Here, Polarity is used as a (tag set) rule parameter. It means that the rule will be expanded for each value in the tag set. So if you have a rule like this:

Polarity [ Crate:Polarity | Crate:Polarity ] -> [ < Crate:Polarity | > Crate:Polarity ]

It will be expanded into the two rules:

[ Crate:Positive | Crate:Positive ] -> [ < Crate:Positive | > Crate:Positive ]
+ [ Crate:Negative | Crate:Negative ] -> [ < Crate:Negative | > Crate:Negative ]

The expansion of rules with rule parameters writes a new rule for every value of the parameter and replaces all occurrences of the parameter name in the rule by its value. Mappings build on top of this expansion mechanism: the name of a mapping can be used instead of the name of the parameter, and it will be replaced by the value associated to the parameter's value in the mapping. So the rule:

Polarity [Crate:Polarity] -> [Crate:OpposedPolarity]
gets expanded as:
[Crate:Positive] -> [Crate:Negative]
+ [Crate:Negative] -> [Crate:Positive]

Because the mapping OpposedPolarity is defined as:

Polarity => OpposedPolarity
Positive Negative -> Negative Positive

Mappings of rule direction

Remember that the rule direction is an expansion parameter like others, and that the relative directions > < v ^ perpendicular parallel orthogonal are predefined mappings for directions. So you can do things like:

(the player faces the direction of the key pressed)
[ > Player:directions ] -> [ > Player:> ]

(players can push wheelchairs only when they face one, standing behind it)
[ > Player:> | WheelChair:> ] -> [ > Player:> | > WheelChair:> ]

Sprite transforms

Tags and mappings let you allow create "families" of objects. You can define the sprite of an object as a copy of another, applying transforms to the sprite as you copy it, and generate names for those objects using tags and mappings.

So for instance, a Lava sprite could be defined like this:

Lava:Anim5
Red Orange #CF4020
copy: Lava:PrevAnim5 shift:left
Where Anim5 is the tag defined as:
TAGS
Anim5 = Step1 Step2 Step3 Step4 Step5

And there is a mapping giving the step n-1 from the step n:

MAPPINGS
Anim5 => PrevAnim5
Step1 Step2 Step3 Step4 Step5 -> Step5 Step1 Step2 Step3 Step4

The last step of the animation is defined with a sprite (Lava:Step5), and then the other steps are defined by shifting left the previous one. That's what the transform line does. It works because the objects are created in the order of the definition of the tags.

You can use

You can write multiple transforms and replace parts of the name and transform directions by tags or mappings, and they will be applied in the order you list them.

Transforms can also be given after a sprite matrix specification, not only after a copy: instruction. This is typically used to avoid providing the N bottom lines of the sprite, using a translate: transform instead.

This document was adapted from an original authored by Clement Sparrow.