Creature Specification (Savage)

From HLKitWiki
Revision as of 19:03, 6 February 2009 by Rob (Talk | contribs) (New page: {{context|Authoring Examples|Savage Worlds Walk-Through}} ===Overview=== ===Directly Defining Traits=== It's now time to figure out how we're going to be handling the details of creatur...)

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

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

Directly Defining Traits

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 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

  • fields to specify the values
  • script to apply the Pace value
  • script to apply the other deltas
  • change history behavior to "changes" to skip changes of zero

Assigning Gear

  • bootstrap the equipment
  • designate the "initequip" tag for default weapons and armor

Selecting Abilities

  • race-specific abilities
  • creature-specific abilities
    • shared abilities with races
    • separate file to hold all of the general/shared abilities
  • default abilities that can be added to any creature
    • identifying the abilities distinctly from other abilities via tag

Defining Complete Creatures

  • walk-through of three different creatures