Context: HL Kit Advanced Authoring Concepts
There will be many situations where you want to add a pick to a container automatically. For example, you'll need to make sure that every actor starts out with all of the attributes possessed by characters. You'll also want to re-use abilities, so you'll need to have one thing automatically add another thing.
Within the Kit, this process is referred to as "bootstrapping". Correspondingly, when you add a thing to a container automatically, the resulting pick is referred to as a "bootstrapped" pick. There are a number of important implications associated with bootstrapping, which are discussed in the topics below.
Things Bootstrapping Other Things
You can bootstrap picks from a variety of situations. However, the most common situation will be when you have one thing bootstrap another. This will regularly occur when the game system has an assortment of abilities that are conferred from a variety of sources. For example, consider the "low-light vision" ability within the d20 System. This ability is conferred by multiple different races, certain magic weapons, etc. You're only going to want to define the ability once, after which you can have each race and weapon simply bootstrap the ability.
When a pick is bootstrapped by another pick, the pick that does the bootstrapping is referred to as the "root" pick. The root pick has complete control control of the bootstrapped pick. If the root pick is deleted, the bootstrapped pick is also deleted. If the root pick goes non-live, so does the bootstrapped pick. The bootstrapped pick still has its own independent existence, but that state is wholly dependent upon the root pick as well.
Situations Where You Can Bootstrap
In addition to things bootstrapping each other, there are a number of additional situations in which bootstrapping of things can be performed. These situations are outlined below. In each of these cases, the dependency relationships outlined above for things bootstrapping things do not apply.
- Things can be bootstrapped onto all actors when the actor is initially created.
- Things can be bootstrapped onto entities as part of the entity's definition. The bootstrapped picks are part of every gizmo created from that entity.
- Things can be bootstrapped onto entities when they are attached. The bootstrapped picks are only included on gizmos created by that thing.
- Things can be bootstrapped onto minions when they are attached. The bootstrapped picks are only included on minions created by that thing.
Bootstrapped Picks are Not Deletable
The topic name pretty much sums it. When a pick is bootstrapped into a container, only the source that performs the bootstrap has control over the pick's existence. Consequently, all bootstrapped picks of fundamentally not deletable. Any attempt to delete a bootstrapped pick will fail.
If you want to pre-select a pick into a table or chooser, and you want the user to be able to delete that selection, you need to use a different technique. Please refer to the section "Automatically Adding Picks to Actors".
Bootstrapping the Same Thing Multiple Times
At this point, you may be wondering what happens when the same thing is bootstrapped multiple times into a container. The answer depends on whether the thing is designated as being unique. If not, then separate, independent picks are always created, and their behaviors are not linked in any way. However, if the thing is designated as unique, then only one pick is ever added to a given container. This means that all of the bootstrap actions will actually reference the identical pick.
When the first bootstrap occurs, the new pick is created. Each subsequent bootstrap simply increases the reference count for the pick. As long as at least one of the sources for the bootstrap remains in existence, so will the bootstrapped pick. This is important when things bootstrap over things and those things are user-added. Consider the situation where both ThingA and ThingB bootstrap ThingZ. When the user added ThingA, ThingZ is bootstrapped. When the user adds ThingB, the reference count is increased. If ThingA is deleted, the reference count is decreased, but ThingZ still exists. When ThingB is deleted, the reference count goes to zero and ThingZ is finally deleted as well.
When multiple things bootstrap the same, unique thing, their effects are cumulative upon the new pick. If ThingA specifies an auto-tag for ThingZ, and ThingB specifies a different auto-tag for ThingZ, then both auto-tags are assigned to ThingZ. If only ThingA is added to the container, only its auto-tags are assigned, and the same holds for ThingB. However, if both things are added to the container, both auto-tags are assigned.
If multiple things bootstrap the same, unique thing, all of the conditions associated with the root picks must be handled in accordance with some sort of rules. For example, we'll assume ThingA and ThingB both bootstrap ThingZ, and both ThingA and ThingB have been added to the same container. Now we'll further assume that ThingA has a Live test that is currently failed. This means that ThingA is treated as not existing within the container, although ThingB has no dependency and therefore fully exists. So what happens with ThingZ?
To resolve situations like this, the Kit treats each root pick independently. If any one of the root picks for a bootstrapped pick is considered live, then the bootstrapped pick is also considered live. In the example above, this means that ThingZ would be treated as being live due to ThingB being live. The fact that ThingA is not live is irrelevant.
The Mechanics of Bootstrapping
The process of bootstrapping a thing is typically accomplished via the "bootstrap" element. Since there are a variety of places where bootstrapping can be performed, this element is re-used throughout the Kit.
You can also bootstrap things via the "autoadd" element and the "enmasse" element. Both of those elements are exclusively used within structural files.
The vast majority of bootstraps are simple. The bootstrap is always performed for the source context it is defined within. However, bootstraps on components are sometimes an exception. A bootstrap definition on a component will be inherited by all things derived from that component. However, you may only want the bootstrap to be applied to most of those things, with some not having the bootstrap.
In these situations, a Match tag expression can be specified with the bootstrap. This tag expression is applied to each thing derived from the component. Only the things that satisfy the tag expression are assigned the bootstrap. Note that the tag expression is tested against the tags possessed by each thing, so each thing uniquely controls whether it does or does not receive the bootstrap. This also means that only the initial tags each thing possesses are tested.
IMPORTANT! If a component bootstrap does not specify a Match tag expression, the bootstrap is automatically considered a match to all things derived from the component and assigned to all of them.
There is another potential wrinkle with bootstraps. Some things can be added to different containers or under different circumstances. Depending on the situation, you may want the bootstrap to be added in some cases but omitted in others. For example, a special ability that is selected directly by the user for an actor may behave one way, while that same ability being added as a power within a magic item may behave a bit differently.
To accommodate special cases like this, a Condition tag expression may be specified. This tag expression is applied to the prospective container for the new pick. If the container satisfies the condition test, then the bootstrap is added normally. However, if the container does not satisfy the test, no bootstrap is added.
The Condition test on bootstraps works differently from the Match tag expression. In addition to focusing on the container, the test is applied against tags that are dynamically assigned to that container. This means that the Condition test must be scheduled at a specific phase and priority during the evaluation cycle. Any tags that you wish to test for within the container must be handled prior to the timing of the Condition test.
IMPORTANT! All tasks that operate upon a pick must occur after any bootstrap Condition tests for the pick. This includes all component scripts. If a task is determined to be scheduled prior to the Condition test, an error will be reported.
IMPORTANT! Within the Condition test, it is valid to test field values, but the source from which these field values are retrieved will vary. The handling of each possible situation is outlined below.
- If the bootstrap is being added by another pick, all field value tests are applied to the root pick that is performing the bootstrap.
- If the bootstrap is being added by a gizmo, then all field value tests are applied to the anchor pick that attaches the gizmo.
- If the bootstrap is added by a minion, the field value tests are applied to the master pick that attaches the minion.
- For global bootstraps that are applied to every actor, no field value tests may be utilized, as there is nothing to compare against.
- In all cases, the author is responsible for ensuring that only field values that properly exist are tested, since attempts to access missing fields will result in an error being reported.