Creature Specification (Savage)

From HLKitWiki
Revision as of 21:22, 10 February 2009 by Rob (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

It's now time to figure out how we're going to be handling the details of creatures. Each creature in the rulebook has die-types specified for each attribute. Creatures also have a list of skills, with the appropriate die-type for each. Derived traits are specified, incorporating any special adjustments beyond the standard derived value. Lastly, a list of gear and/or special abilities is given.

Our solution for creatures must enable all of these different facets to be readily assigned to each creature. We want a design that is both easy to use and also easy to utilize via the integrated Editor. For the latter, it's always easiest when tags are utilized, since users can simply pick from a controlled list that the Editor presents. The next easiest mechanism is simple fields, wherein the user can enter text or a value. After that, whatever works is going to be pretty much the same.

Given the list of requirements and goals above, we'll see about devising a good solution. As an added user benefit, we'll also strive to allow the user to customize the default values for each creature if he wants.

Internal Approach

There are two different facets of traits that we'll need to handle. The first is the basic die-type for the trait, ranging from "d4" to "d12". The second is the roll bonus in situations where the creature has a trait that exceeds a "d12" rating.

These two facets are managed internally via two separate fields for each trait. As such, our first thought will likely be to do the same. We could have two separate fields that specify the starting die-type and bonus for each trait. An Eval script could be written that assigns the die-type value to the "trtBonus" field and the bonus value to the "trtRoll" field.

This would give us a workable solution, but it would be far from ideal. Since we're applying the values as bonuses, we run into a number of limitations. The most important limitation is that we are unable to allow the user to adjust the trait values. Since the attributes start at the minimum and the bonus is applied, users can increase the die-type but not decrease it. Consequently, we can't allow adjustment, since users will expect full range of adjustment to be possible. Another issue is that users would be unable to adjust the roll bonus. If the user wanted to change a trait from "d12+2" to "d12+1", there would be no way to do it, other than applying a permanent adjustment to the creature.

Giving the problem a bit more thought, a better approach would be to actually set the "trtUser" field of the trait to the proper die-type. This would make it possible for the user to then edit the die-type as he saw fit. Unfortunately, there are two liabilities with this technique. There would still be no way for the user to easily adjust any roll bonus, plus there would be nothing to prevent the user from modifying the die-type independently from the roll bonus. For example, a trait of "d12+1" could have only the die-type modified downwards one notch, which would result in a trait of "d10+1" instead of "d12".

Based on this analysis, we need a single unified value that represents both the die-type and the roll bonus. Once the value reaches a "d12" rating, increasing it one step would become "d12+1". The increase would steadily adjust the roll bonus as soon as the maximum die-type was reached. How can we do this?

The trick is to modify the behavior of the "trtUser" field when dealing with a creature. For a normal character, the "trtUser" field has a fixed range of 2-6, corresponding to the "d4" through "d12" die-types. However, we already have to handle the case of the die-type exceeding "d12" when determining what to display, so having a value of 8 will still result in a "d12" being displayed. This means we can change the maximum limit to higher than 6 and the die-type display will continue to work fine.

Armed with this knowledge, we can consider values larger than 6 to indicate progressive increases in the roll bonus. A value of 7 would translate to "d12+1", while 8 would translate to "d12+2", etc. We can write an Eval script to detect a value greater than 6 and automatically apply the excess as a roll bonus by modifying the "trtRoll" field.

Since the "trtUser" field is directly modifiable by the user via the incrementer, this approach affords the user with a simple, intuitive means of adjusting traits. They start out at "d4", progress upwards through "d12", and then continue to increase as "d12+N". Decrementing the trait works seamlessly in the other direction.

We've got our strategy mapped out, so let's put the basic mechanisms into place. There are two changes we need to make internally to support this approach. The first is to setup a different maximum value for traits when creating a creature. We'll support up to "d12+10", so we'll set out maximum accordingly. The Eval script where the ranges are specified can be changed to the following.

<eval index="3" phase="Initialize" priority="3000"><![CDATA[
  ~since die types range from d4 to d12, in multiples of 2, we have a range of
  ~2-6 for traits - we'll convert to the die type for display in a separate script
  ~Note: Creatures treat excess as a roll bonus, so let the maximum go to 16 (+10).
  field[trtMinimum].value = 2
  if (hero.tagis[Hero.Creature] <> 0) then
    field[trtMaximum].value = 16
  else
    field[trtMaximum].value = 6
    endif
  ]]></eval>

With the maximum relaxed, we can add the handling for a value that exceeds 6. We'll define a new Eval script that only does something when we have a creature with a trait that exceeds 6. If we do, then the excess is added to the "trtRoll" field and becomes a roll bonus. The new Eval script should look like the one below.

<eval index="5" phase="Traits" priority="8000">
  <before name="Calc trtNetRoll"/><![CDATA[
    ~for creatures with a die-type greater than 6, the excess is a roll bonus
    if (hero.tagis[Hero.Creature] <> 0) then
      if (field[trtUser].value > 6) then
        field[trtRoll].value = field[trtUser].value - 6
        endif
      endif
  ]]></eval>

External Approach

We also have to figure out a convenient way to let authors specify the die-type and roll bonus values for each trait. Whatever we come up with should be readily adaptable for use within the integrated Editor, thereby allowing users to add their own custom creatures easily.

As above, the first idea is probably going to be two separate fields that can be specified. Since we're only assigning starting values, we don't have to worry about having a smooth progression. However, two separate values does make it possible for the user to inadvertently specify a roll bonus with a die-type less than "d12", such as "d8+2". So a single value would be ideal.

If we only have a single value, we then need to assess what that value will be and how the user will specify it. We currently use a value of 2-6 for the die-types "d4" through "d12". Our internal solution calls for using values of 7+ to indicate the roll bonus beyond a "d12". While all this makes perfect sense for internal use, it's going to seem like nonsense to a user with no knowledge about the inner workings of our data files. An most users don't want to understand the inner workings - they just want to knock out a quick creature for use in the upcoming game and be done with it. That means field values are a poor solution.

The best solution is to use an assortment of tags. We could define a new tag group that contains tags corresponding to each internal value we want to support. Each tag could be defined with a suitable publicly visible name that presents something intuitive to the user. For example, the tag for the value "7" would have a name of "d12+1", so the user would select "d12+1" within the Editor and the proper value of 7 would be assigned internally. We can use the "tagvalue" target reference within the scripting language to extract a value from the unique id of each tag, so we simply need to ensure that the unique id of the tags is the value we want to use internally.

An example of what the tag group should look like is provided below. We define tags that extend up to a "+10" bonus on the trait, which will be the maximum we support.

<group
  id="DieType"
  name="Die-Type">
  <value id="2" name="d4"/>
  <value id="3" name="d6"/>
  <value id="4" name="d8"/>
  <value id="5" name="d10"/>
  <value id="6" name="d12"/>
  <value id="7" name="d12+1"/>
  <value id="8" name="d12+2"/>
  <value id="9" name="d12+3"/>
  <value id="10" name="d12+4"/>
  <value id="11" name="d12+5"/>
  <value id="12" name="d12+6"/>
  <value id="13" name="d12+7"/>
  <value id="14" name="d12+8"/>
  <value id="15" name="d12+9"/>
  <value id="16" name="d12+10"/>
  </group>

At this point, we now have both our internal and external approaches mapped out. We can now begin implementing our solution.

Assigning Attributes

We'll first setup the means for specifying the attribute values to be used when defining creatures. We need to use a group of tags to specify both the die-type and roll bonus for each attribute. However, if we only define one set of tags and re-use them, we'll run into the problem of determining which tag is associated with which attribute. This means we need a separate set of tags for each attribute. Each of the tag groups is shown below, and the full set of tags defined in the previous section must be specified for each tag group.

<group
  id="AgiDie"
  name="Agility Die-Type">
  ...
  </group>

<group
  id="SmaDie"
  name="Smarts Die-Type">
  ...
  </group>

<group
  id="SpiDie"
  name="Spirit Die-Type">
  ...
  </group>

<group
  id="StrDie"
  name="Strength Die-Type">
  ...
  </group>

<group
  id="VigDie"
  name="Vigor Die-Type">
  ...
  </group>

We now have five tag groups, each with its own set of tags. Each creature can be assigned a single tag from each tag group, with each tag dictating the appropriate die-type and roll bonus for its corresponding attribute. For example, the creature below would be assigned die-types of "d4" through "d12" for the various attributes.

<thing
  id="creSample"
  name="Sample"
  compset="Creature"
  isunique="yes"
  description="Description goes here">
  <tag group="AgiDie" tag="2"/>
  <tag group="SmaDie" tag="3"/>
  <tag group="SpiDie" tag="4"/>
  <tag group="StrDie" tag="5"/>
  <tag group="VigDie" tag="6"/>
  </thing>

The final piece we need to handle is taking the values dictated by the tags and assigning them to the appropriate attributes. Our first instinct will be to create a new Eval script. In that script, we can assign the value of each tag to the "trtUser" field of the trait. Unfortunately, that won't work.

The problem is that using an Eval script will result in the value being applied to the field during every evaluation cycle. If we do that, the user can modify the attribute all he wants, but we'll always keep resetting the value back to the initial value for the creature. That's not what we had in mind.

Fields of type "user" work differently from "derived" fields. A "derived" field is reset at the start of each evaluation cycle and must be set to an appropriate value within each cycle. In contrast, a "user" field is only ever changed when the user makes a change or a script assigns a new value. Consequently, what we need to do is assign the proper values to the "user" fields when the creature is created and never touch them again. That way, we initialize them and hand control to the user thereafter.

The Kit provides a mechanism to handle exactly this type of situation. Every component can define a Creation script. This script is invoked exactly once for every pick derived from that component - at the time the pick is first created. If we assign the values within this script, they will be setup once and never modified again. The appropriate Creation script for setting up the various attributes is shown below.

<creation><![CDATA[
  ~assign the appropriate die-type ratings to attributes
  #traituser[attrAgi] = tagvalue[AgiDie.?]
  #traituser[attrSma] = tagvalue[SmaDie.?]
  #traituser[attrSpi] = tagvalue[SpiDie.?]
  #traituser[attrStr] = tagvalue[StrDie.?]
  #traituser[attrVig] = tagvalue[VigDie.?]
  ]]></creation>

Reload the data files, create a creature, and then select the sample creature above. If everything is working, each of the attributes should be initialized to the proper die-type. Modifying the various attributes up and down via the incrementer will properly adjust the attributes, allowing the user to fully control everything after the creature is first added.

The one limitation of this approach is that changing the creature will reset any adjustments made by the user. In general, that behavior is a good thing, since a new creature should always start out with the default values assigned in the rulebook.

Assigning Skills

The overall technique for assigning skills is very similar to the one used for attributes. However, there is a very important wrinkle in dealing with skills. In Savage Worlds, skills are not added to every character, so that means creatures don't begin with any skills. The technique we used for attributes assumes that the attribute is already on the character so that it can be properly adjusted.

The only way to add a skill to a character is by bootstrapping it. This means that each creature must bootstrap each of the skills that it possesses. Once we do that, though, we need a way to initialize the "trtUser" field of the skill appropriately. If we try to do anything within the Creation script of the "Creature" component, it will fail, since the script is invoked immediately upon creation, which is before any bootstrapped picks are added to the creature.

The solution is to leverage the Creation script on the skill itself. We can assign the appropriate die-type tag to the skill as part of the bootstrap process. That tag is considered part of the skill, so it can be utilized within the Creation script of the skill.

This means that we only need a single tag group for all skills. Since the proper tag will be assigned to each separate skill, no skill should have more than one such tag. So we can define a new tag group like the one below that contains all of the various tags for the different die-types.

<group
  id="SkillDie"
  name="Skill Die-Type">
  ...
  </group>

With the tag group in place, we can assign a few skills to our sample creature for testing. Each skill is bootstrapped and assigned the proper tag for the die-type it should possess. The revised sample creature below demonstrates three skills be added to the creature. These skills are then assigned die-types of "d6", "d8", and "d10".

<thing
  id="creSample"
  name="Sample"
  compset="Creature"
  isunique="yes"
  description="Description goes here">
  <tag group="AgiDie" tag="2"/>
  <tag group="SmaDie" tag="3"/>
  <tag group="SpiDie" tag="4"/>
  <tag group="StrDie" tag="5"/>
  <tag group="VigDie" tag="6"/>
  <bootstrap thing="skFighting">
    <autotag group="SkillDie" tag="3"/>
    </bootstrap>
  <bootstrap thing="skGuts">
    <autotag group="SkillDie" tag="4"/>
    </bootstrap>
  <bootstrap thing="skNotice">
    <autotag group="SkillDie" tag="5"/>
    </bootstrap>
  </thing>

The skills are automatically added to the creature and assigned their default rating of "d4". We now need to do something with the tags to initialize the die-type ratings. We'll define a Creation script for the "Skill" component. In this script, we'll first check to make sure that we have a creature and a suitable tag with which to initialize the skill. We can then use the same approach as with attributes, assigning the value of the tag as the initial value of the "trtUser" field. The Creation script below shows the logic in action.

<creation><![CDATA[
  ~if this is a creature with a skill rating, assign the die-type rating to the skill
  if (hero.tagis[Hero.Creature] + tagis[SkillDie.?] >= 2) then
    field[trtUser].value = tagvalue[SkillDie.?]
    endif
  ]]></creation>

At this point, skills should be working smoothly for creatures.

Assigning Derived Traits

Derived traits work quite differently from attributes and skills for normal characters. That's because they are 100% derived from facets of the character. For creatures, though, we need to handle them differently. Various creatures have values assigned for certain derived traits that are not purely derived. The most common trait is "Pace", but other traits vary as well.

The problem is that derived traits are always calculated in the end. We need to handle them in a way that will smoothly integrate with their calculated nature. Unfortunately, there is no clean way of doing that by having the derived trait value be specified explicitly for a creature. The only way to reasonably accomplish it is to utilize an adjustment that gets incorporated into the calculated final value.

This means that we need to define four new fields for the "Creature" component. We need one field apiece for the four derived traits, where each field specifies an adjustment from the standard calculated value. If a field is not specified, then no extra adjustment will be applied for that trait.

From a usage standpoint, the problem with this approach centers on Pace. Virtually all creatures have a custom Pace, so we should really assume that Pace has a starting value of zero instead of the current six. That way, the actual Pace can be specified instead of an adjustment. Since the other derived traits rarely differ from the calculated value, requiring an adjustment to the calculated value is perfectly reasonable.

In order to specify the Pace as an explicit value, we need to modify the trait itself. The "trPace" trait currently assumes a default value of six for the "trtBonus" field. We need to override that behavior when we have a creature. So we'll define the following Eval script on the trait to apply the special handling.

<eval index="2" phase="Initialize" priority="3000"><![CDATA[
  if (hero.tagis[Hero.Creature] <> 0) then
    field[trtBonus].value = 0
    endif
  ]]></eval>

With the change in place, we can shift back to the fields on the "Creature" component. We'll end up with four fields, where the Pace indicates the actual starting value to use and the others indicate an adjustment. Our new fields should looks like the following.

<field
  id="crePace"
  name="Base Pace"
  type="static">
  </field>

<field
  id="creParry"
  name="Parry Adjustment"
  type="static">
  </field>

<field
  id="creTough"
  name="Toughness Adjustment"
  type="static">
  </field>

<field
  id="creCharis"
  name="Charisma Adjustment"
  type="static">
  </field>

We now need to communicate the adjustment value for each derived trait from the creature. Each derived trait needs to know what the particular adjustment value is. We'll define a new field on the "Derived" component for this purpose, which should look like below.

<field
  id="trtCreatur"
  name="Creature Start"
  type="derived">
  </field>

Each creature can tell its derived traits the value to be used via an Eval script on the "Creature" component. This script must be invoked early in the evaluation cycle, but it is otherwise very simple. The result script is shown below.

<eval index="3" phase="Setup" priority="5000"><![CDATA[
  ~setup the proper starting values for each trait
  hero.child[trPace].field[trtCreatur].value = field[crePace].value
  hero.child[trParry].field[trtCreatur].value = field[creParry].value
  hero.child[trTough].field[trtCreatur].value = field[creTough].value
  hero.child[trCharisma].field[trtCreatur].value = field[creCharis].value
  ]]></eval>

At this point, each derived trait should know the adjustment value specified by any selected creature. Integrating that value into the final calculation for the derived trait is relatively simple. All we need to do is revise the Eval script that calculates the value of "trtFinal" to add in the adjustment for creatures. We can achieve this by adding the lines of code below to the Eval script.

~if this is a creature, we need to add the user value as a custom adjustment
if (hero.tagis[Hero.Creature] <> 0) then
  field[trtFinal].value += field[trtCreatur].value
  endif

That's all there is to it. We're now ready to give our changes a try. When we select a creature from the chooser, the appropriate values should be applied as adjustments to the final derived trait values. Let's put our changes to the test with an actual creature that applies adjustments.

We'll modify our sample creature for this purpose. We can assign a few field values on the creature to apply our adjustments. We'll assign a "Pace" of "5" and a "Parry" adjustment of "1". To do this, we'll add the following XML elements to the creature's definition.

<fieldval field="crePace" value="5"/>
<fieldval field="creParry" value="1"/>

Reload the data files and create a creature. Take a look at the default values shown for the derived traits. From the chooser, select our test creature. The "Pace" value should immediately change to "5", and the "Parry" value should change from "2" to "3". Our derived traits are working smoothly now.

Assigning Gear

The assignment of standard gear to creatures is incredibly simple. All that's needed is to bootstrap the desired gear onto the creature. If the gear is a weapon or armor, then you may also want to have the gear start out in the "equipped" state, which can be accomplished by assigning the "Equipment.StartEquip" tag to the gear. For example, a "Dagger" can be automatically assigned to a creature and pre-equipped via the XML element below.

<bootstrap thing="wpDagger">
  <autotag group="Equipment" tag="StartEquip"/>
  </bootstrap>

Many creatures also possess a natural attack of some sort and/or natural armor. You can add these as weapons in the same way as other gear. However, you're much better off using the customizable abilities that we defined earlier for this purpose.

Assigning Abilities

The assignment of abilities to a creature is comparable to assigning gear. Once the ability is defined, a simple "bootstrap" element is all that's needed. The definition of abilities has already been outlined previously. For example, the "Rollover" special ability of an Alligator can be defined as shown below and assigned to a creature via the bootstrap beneath.

<thing
  id="abRollover"
  name="Rollover"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  </thing>
<bootstrap thing="abRollover"/>

If a creature has a natural weapon, it's easiest to use the customizable weapon we defined earlier. No new ability needs to be defined, as the existing "Unarmed Attack" weapon can be tailored to the particulars necessary for the creature. For example, the "Bite" attack of the Alligator can be assigned to the creature via the following.

<bootstrap thing="abWeapon">
  <assignval field="livename" value="Bite"/>
  <assignval field="abilText" value="+d6"/>
  </bootstrap>

A similar mechanism was defined for use with natural armor and can be readily employed. All that's needed is the defensive bonus conferred. For example, the thick skins of the Alligator confer a "+2" armor bonus, which can be assigned via the following.

<bootstrap thing="abArmor">
  <assignval field="abilValue" value="2"/>
  </bootstrap>