Advanced Script Handling
Context: HL Kit … Advanced Authoring Concepts
The Kit provides a variety of more specialized control mechanisms for managing scripts. These mechanisms provide the ability to handle special-case situations that may arise when writing data files for some game systems. The topics below cover these various mechanisms.
Sequencing of Scripts with Identical Timing
When multiple tasks are assigned a common phase and priority, those tasks will be scheduled for evaluation at the exact same time. However, the task scheduler only invokes one task at a time. This means that there would be no guarantee about the order in which two tasks are evaluated that are assigned the same timing. From an authoring standpoint, it's much easier to assign a group of tasks the same phase and priority instead of having to micro-manage which ones occur before each other.
To address this, the Kit provides a set of rules for task scheduling. These rules govern the sequence in which tasks are evaluation that have the same phase and priority. The table below details the order that the Kit uses, with tasks being evaluated in the sequence given, based on their type.
- Existence tests that are assigned by tables or choosers
- Bootstrap condition tests that are assigned by root picks
- Secondary tests that are assigned by tables or choosers
- Condition tests that are assigned by components
- Condition tests that are defined explicitly on a thing
- Calculate scripts defined on any fields
- Bound scripts defined on any fields
- Eval scripts that are defined on components or things
- Evalrule scripts that are defined on components or things
- Gear scripts that are defined on components
The above rules don't handle a common situation that arises when component scripts are employed. The following topic address how this situation is handled.
Sequencing of Component Scripts
When an eval script or evalrule script is defined for a component, all things derived from that component possess the same script. Since the script is assigned a common phase and priority, all instances of that script will be scheduled for evaluation at the exact same time. As discussed above, the task scheduler only invokes one task at a time, so this means that there is no guarantee about the order in which these scripts are evaluated.
In most cases, there is no need to worry about the respective timing of these scripts, since they don't depend on each other. However, there are situations where it's important that all the scripts being evaluated in a guaranteed order. For example, consider the d20 System data files, where attribute bonuses are chosen every four character levels. It's critical that those bonuses be applied in the exact order that they are selected by the user, since those bonuses have ripple effects elsewhere on the character. Consequently, the Kit imposes rules on the evaluation sequencing of component scripts.
Every component must be assigned a "sequence" attribute. This attribute governs how picks are sequenced to the user by default. It also governs how their tasks will be sequenced during evaluation. Consequently, during task scheduling of eval scripts/rules that are associated with the same component, the corresponding tasks are sorted using the sequence assigned to the component. This ensures that tasks are always scheduled in a consistent fashion. It also extends to the case where things are added multiple times to a container.
In rare situations, you'll need to specify an alternate behavior for task scheduling. To accommodate these cases, individual scripts and rules have an attribute that overrides their behavior. This attribute allows to you specify a different component whose sorting rules must be used when scheduling all tasks for that script.
Limiting Evaluation and Reporting
By default, every eval script/rule is scheduled and processed separately for every pick added to an actor. That behavior is exactly what you'll want 99% of the time, but there are some situations where special handling is needed.
Consider the case where you only want a script to be invoked if a thing is added to the actor. However, what if you also only want the script to be invoked a single time, even if multiples of the thing are added to the actor? There may also be times when you want a rule to be tested for all picks, but you only want the error message to be reported a single time if any of them fail. To deal with these situations, you can specify limits on the evaluation and reporting of scripts and rules.
To limit the evaluation, you can use the "runlimit" attribute to specify the maximum number of times to evaluate the task. This limit is then imposed separately for each container into which the thing is added. If the thing is added ten times to a container, a "runlimit" of one will ensure that the task is only ever invoked a single time. If the thing is added ten times to one container and four times to another container, the task will be invoked once within each container.
To limit the reporting of a rule, you can use the "reportlimit" attribute, which dictates the maximum number of times the rule reports an error. This is critically different from the "runlimit" attribute, which controls the actual invocation. With a report limit, the rule is invoked for every pick, as normal. Only the error message is limited. For example, consider the d20 System data files. If you add multiple class levels to a character, you need to assign hit points for each. You only want to report the error once, but you want all of the picks to be processed so that they will be properly highlighted in red if invalid.
For both evaluation and reporting limits, each thing is normally treated as distinct, with its own limit tracking. Once in awhile, you may want to have all things derived from a given component all contribute to the same limit. In other words, the same limit is imposed whether the user adds the same thing multiple times or different things. This behavior is controlled via the "iseach" attribute on components.