Tag expressions are built upon individual tag terms. Within a tag expression, every term is evaluated against the tags assigned to an object. Terms can either be "simple" terms or "arithmetic comparison" terms. Every individual term yields a result of either "true" or "false", allowing the terms to be combined into more complex Boolean expressions.
Simple terms are just that – simple. A simple term must be one of the following:
TRUE The literal value "TRUE" can be specified (case is important). FALSE The literal value "FALSE" can be specified (case is important). tag template A tag template can be specified, with the term returning a Boolean result of true or false. When a tag template is evaluated as a simple term, the result is true if the object contains one or more tags that match the tag template and false if no tags match the template.
Arithmetic Comparison Terms
There are times when you will need to identify objects which satisfy criteria that don't simply yield a "match" or "no match" result. In these instances, the object is required to comply with numeric constraints associated with the tags assigned. For example, a rule might pertain only to spells with a level of 5 or more. In situations like this, it’s not appropriate to test for all the possible values (e.g. for the proposed sample rule, you would not want to test separately for spells of levels 5, 6, 7, 8, etc.). To handle special relationships like this, tag terms can be defined using arithmetic comparisons.
Arithmetic comparison terms always take the traditional form "left oper right", where "oper" is one of the arithmetic comparison operators: '<', '<=', '>', '>=', '=', or '<>' (the last one indicating "not equal"). Both "left" and "right" must evaluate to numeric values, with the comparison term yielding a "true" or "false" result based on the relationship between the values for "left" and "right". In this type of usage, the left and right sides of the comparison must be one of the following:
integer A literal integer value can be specified (e.g. "1234"). Use of a literal integer value is restricted to the right side of an arithmetic comparison term only. An error occurs if a literal integer value is used on the left side. The integer value may not be negative. count:template A standard tag template of the form "group.template", subscribing to the syntax set forth previously (e.g. "color.bl?"). The value generated is the number of tags that have been assigned to the object which match the template (wildcards are allowed per standard tag template syntax). low:template All tags that match the specified template are identified, and an integer value is extracted from the end of each tag. The lowest (i.e. minimum) value of all tags is the value yielded. Integer value extraction is described below. Example: "low:cost.gold?". high:template Identical to "low:template", except that the highest (i.e. maximum) value of all matching tags is yielded. Integer extraction is described below. Example: "high:cost.gold?". val:template The first tag that matches the specified template is identified, and an integer value is extracted from that tag and returned. Integer value extraction is described below. Example: "val:cost.gold?".
NOTE! If the template matches multiple tags, there is no guarantee which tag will be retrieved, so this form should only be used when you know there is a single tag matching the template assigned to the object. Using this form is much more efficient that the "low" and "high" forms, so make use of it whenever it is safe to do so.
NOTE! The prefix may optionally be omitted from a tag term within an arithmetic expression. If no prefix is specified, then the default prefix of "count" is assumed. Therefore, the term "color.blue" is equivalent to "count:color.blue" within an arithmetic comparison term, such as below.
(color.blue >= 1)
Tag Value Extraction
Every tag can be converted to an integer value. Extracting an integer value from a tag utilizes the unique id of the tag and not its name. Starting at the end of the tag’s unique id, all digits are extracted until a non-digit is encountered. Those digits are then converted to an integer value. For example, integer value extraction from the tag "blue1234" would yield the value "1234". Similarly, the tag "blue5x123" would yield the value "123".
If a tag does not end in any digits, then the tag will yield "no" value, which will be treated as a value of zero when an integer value is required for the tag. For example, the tag "blue567xyz" would yield no value, since only the trailing digits are used and all earlier digits are ignored.
NOTE! Depending on the situation, there may be a critical distinction between a tag with "no" value and a tag with a "zero" value.
Be sure to keep the following issues in mind when dealing with tag values:
- Negative values are never extracted from tags, since only digits are considered when extracting the value.
- Under some circumstances, a tag will have no value and be referenced as if it has a value. Tags with "no" value are ignored in those situations (e.g. within summations and averages), while a tag with a "zero" value is processed with that value.
- An object that does not have any tags matching the specified template and for which a value is referenced will never satisfy an arithmetic comparison under any circumstances. For example, the comparison "val:attack.? < 3" will always fail for any object that has no tag from the "attack" group. Similarly, the comparison "val:attack.? >= 3" will also fail for those same objects. If an object has no value, it can never be compliant with a value-based comparison.
Field Value Tests
There are times when tracking a piece of information for an object really needs to be handled via a field value, yet you still need to test that state within tag expressions. One option would be to maintain both the field value and a corresponding tag that can be tested in a tag expression, but that approach quickly becomes difficult to maintain. To alleviate this, the Kit allows direct access to field values from within tag expressions.
You can reference field values using the syntax "fieldval:fieldid", where "fieldid" is replaced by the unique id of the field to access. Other than that, you can utilize field references just like any other arithmetic comparison term within the tag expression. For example, the tag expression "fieldval:strength < 10" would be satisfied if the object possesses a "strength" field and that field has a value of less than 10.
Only the fields for the appropriate object context can be referenced via tag expressions. Consequently, if the tag expression is being applied to a pick, only the fields defined for that pick can be referenced. If a field reference is used within a tag expression that either is not a pick/thing context or does not contain the specified field, a run-time error is reported to the user. If a tag expression will be used against things/picks that derive from different component sets and may not all possess a particular field, the author can include a test for the needed component within the tag expression to verify the presence of the field before accessing it. For example, if the field "foo" is defined within the "compid" component, the following syntax can be used to safely test the field value:
(component.compid & (fieldval:foo >= 1))
Be sure to keep the following important issues in mind when utilizing field values via tag expressions:
- Since only things and picks possess fields, access to field values will only work when the target object is a thing or a pick. There are times this might not be immediately obvious. For example, you might assume that field values can be referenced within a thing "condition" tag expression, except that the "condition" test is applied against the tags of the container, which means there is no thing/pick context to retrieve any fields from.
- In addition to a variety of standard tag expressions within a thing/pick context, access to field values can also be utilized from within most scripts. Any time that a pick target context is accessed from within a script, the "tagexpr[...]" target reference can safely utilize a field value references against the fields within that context.
- Do NOT use field value access as a crutch within tag expressions. Field value access from tag expressions is significantly more expensive to process than testing a tag, so use tags whenever possible and only use field values when absolutely necessary.
Explicit Scope Restrictions
While not common, situations will arise where you'll want to test the tags of a thing/pick and the tags of the hero to determine whether a tag expression should be satisfied. For example, consider a situation where an assortment of things is only available if the actor belongs to a particular group of classes. Assuming those classes assign an identifiable tag to the actor, you could filter the things displayed based on the presence of appropriate tags on the things and the tag on the actor.
To support these situations, any tag template used within a tag expression can specify a prefix of "hero#" on the template. When this prefix is assigned, the tag template is compared directly against the tags of the containing hero context, even if a pick or a child container is the established context. For this to work, the prefix must appear immediately prior to the tag template, such as in the examples below.
group.tag & hero#group.tag
NOTE! Unlike with scripts, you cannot transition through the hierarchy within the tag terms. You can test tags on the current context or directly on the hero. Those are the only options, and they will be all you need 99.9% of the time.