Armor (Savage)

From HLKitWiki
(Redirected from Armor (Skeleton))
Jump to navigationJump to search

Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through 

Overview

Now that weapons are operational, we might as well switch our focus to armor and shields.

Armor Categories

We need to setup the handling for appropriate armor categories, just like we did for weapons. We'll define a new tag group for this purpose called "ArmorType", complete with tags for each category. As before, we designate the tag group as having "explicit" sequencing behavior, with each tag specifying its respective order. The net result is a tag group that looks like below, which is added to the file "tags.1st".

<group
  id="ArmorType"
  dynamic="yes"
  sequence="explicit">
  <value id="MedArmor" name="Medieval Armor" order="1"/>
  <value id="MedShield" name="Medieval Shields" order="2"/>
  <value id="ModArmor" name="Modern Armor" order="3"/>
  <value id="FutArmor" name="Futuristic Armor" order="4"/>
  </group>

We can now define a new sort set that relies upon our new tag group, and we'll call it "Defense". As before, the sort set first sorts on the armor type and then on the name of the thing, which results in the following definition in the file "control.1st".

<sortset
  id="Defense"
  name="Defensive Gear By Type and Name">
  <sortkey isfield="no" id="ArmorType"/>
  <sortkey isfield="no" id="_Name_"/>
  </sortset> 

Defensive Bonus

Both armor and shields confer a defensive bonus to the character, although their use is distinct in Savage Worlds. However, we can define a single field on the "Defense" component to track this value and simply use it in different ways for armor and shields. The Skeleton files provide a "defDefense" field for us already, so we'll use that field for our purposes.

The Savage Worlds rulebook always shows the armor rating in the format "+X", so we should make sure that we do that same. This can be achieved by adding a Finalize script to the "defDefense" field that prefixes it with a "+" character for display. The revised field should look like the one shown below.

<field
  id="defDefense"
  name="Defense Adjustment"
  type="derived"
  maxfinal="10">
  <finalize><![CDATA[
    @text = "+" & @value
    ]]></finalize>
  </field>

With the conversion of this field to possess a Finalize script, we need to realize that any references to this field within scripts that access its "value" will not get the newly synthesize text. So we need to do a scan of our data files and review any references to the "defDefense" field. Any reference to the field by value that is used in output for the user to see should be revised to access the field as text. This amounts to changing references from "field[defDefense].value" to "field[defDefense].text". Fortunately, after a quick scan, there don't appear to be any such instances we need to worry about.

The defensive bonus for armor needs to be added to the character's Toughness trait. However, we still need to handle the non-stacking nature of armor and the multiple zones of coverage before we can do this correctly. So we'll deal with this detail in a little bit.

Four Zones of Coverage

Armor protection within Savage Worlds is tracked separately for four different zones on a character: torso, head, arms, and legs. However, the armor protection conferred by a piece of equipment always has the same value, so it makes more sense to track a single value for the armor and then designate which zones are protected by the equipment. To support this, we'll define a new tag group called "ArmorLoc" that possesses four tags - one for each zone of protection. Each piece of armor can then be assigned one or more tags to indicate which zones are actually protected. This yields a new tag group that looks like below.

<group
  id="ArmorLoc">
  <value id="Torso"/>
  <value id="Head"/>
  <value id="Arms"/>
  <value id="Legs"/>
  </group> 

Non-Stacking Protection

Armor protection does not stack in Savage Worlds, so we need to identify the best protection in each zone of coverage for the character. The easiest way of resolving this is to create four new fields directly on the actor pick. These fields will track the best protection in each location. The new fields must be added to the "Actor" component, which is defined in the file "actor.str". They should look similar to those shown below.

<field
  id="acDefTorso"
  name="Armor on Torso"
  type="derived">
  </field>

<field
  id="acDefHead"
  name="Armor on Head"
  type="derived">
  </field>

<field
  id="acDefArms"
  name="Armor on Arms"
  type="derived">
  </field>

<field
  id="acDefLegs"
  name="Armor on Legs"
  type="derived">
  </field>

Once the new fields are defined, the next thing we need to do is put the appropriate values into them. An Eval script on the "Armor" component applies the effects of equipped armor, so we need to revise that script. The script currently adds the defensive bonus of the armor to the character's Toughness. Instead of doing that, the script should assign the defensive bonus to the new actor field(s), doing so only if the new value is better than the current value and only for the locations covered by the armor. The new script should look similar to the one below.

<eval index="2" phase="PreTraits" priority="5000">
  <before name="Calc trtFinal"/>
  <after name="Equipped"/><![CDATA[
  ~if this gear is not equipped, skip it
  if (field[grIsEquip].value = 0) then
    done
    endif

  ~apply the appropriate trait adjustments for the equipment
  var defense as number
  defense = field[defDefense].value
  if (tagis[ArmorLoc.Torso] <> 0) then
    if (defense > herofield[acDefTorso].value) then
      herofield[acDefTorso].value = defense
      endif
    endif
  if (tagis[ArmorLoc.Head] <> 0) then
    if (defense > herofield[acDefHead].value) then
      herofield[acDefHead].value = defense
      endif
    endif
  if (tagis[ArmorLoc.Arms] <> 0) then
    if (defense > herofield[acDefArms].value) then
      herofield[acDefArms].value = defense
      endif
    endif
  if (tagis[ArmorLoc.Legs] <> 0) then
    if (defense > herofield[acDefLegs].value) then
      herofield[acDefLegs].value = defense
      endif
    endif
  ]]></eval>

As we test our changes, we'll find that we inherited a validation test from the Skeleton data files. This test verifies that the character doesn't try wearing more than one piece of armor at a time. Since Savage Worlds allows for multiple armor pieces with the non-stacking logic we just implemented, this validation test is not appropriate for us. The validation is performed via the "valArmor" thing, which resides in the file "thing_validate.dat". All we need to do is delete the thing and the erroneous validation is gone.

Unfortunately, that didn't fix the problem entirely. Although the validation error is no longer appearing, multiple pieces of equipped armor are still showing up in red. This is due to an Eval Rule script on the "Armor" component. Once we delete the script, the user is free to equip armor in whatever way he chooses.

Torso Armor Boosts Toughness

At this point, we should now be tracking the best armor protection in each location. So we can now determine the protection afforded to the torso and add that to the character's Toughness trait. Since the best protection is tracked on the "Actor" component, we can define a new Eval script on that component to confer the appropriate protection to the Toughness trait. The new script must occur after we've accrued the best protection information, and it should end up looking like the following.

<eval index="4" phase="PreTraits" priority="6000">
  <before name="Calc trtFinal"/><![CDATA[
  #traitbonus[trTough] += field[acDefTorso].value
  ]]></eval> 

Ballistic Defense

Some kinds of armor (e.g. Kevlar Vest) possess a separate defense against bullets. This defense applies only against bullets and also negates some portion of the AP rating of the bullet. We should probably track this separate defensive rating on the "Armor" component. However, we're ultimately going to want to emulate the Savage Worlds rulebook for how to display this information. Since the rulebook incorporates this ballistic defense into the actual armor protection value shown (e.g. "+2/+4"), we want to incorporate it into the net value shown. The value shown is synthesized via a Finalize script for the "defDefense" field, which resides on the "Defense" component, so we must also put the new field on the "Defense" component. The new field should look similar to the following.

<field
  id="defBullets"
  name="Bullets Defense Adjustment"
  type="static">
  </field>

Now that the field is defined, we need to incorporate the value into the display of the "defDefense" field. This means revising the Finalize script to add any "defBullets" field that may exist, which means we also may need to allow a little more storage for the result. The revised field definition should look similar to what's shown below.

<field
  id="defDefense"
  name="Defense Adjustment"
  type="static"
  maxfinal="20">
  <finalize><![CDATA[
    @text = "+" & @value
    if (field[defBullets].value > 0) then
      @text &= "/+" & field[defBullets].value
      endif
    ]]></finalize>
  </field> 

Shields

There are two facets of shields that need to be managed by the data files. First of all, some shields have an armor bonus, which is applied to ranged attacks that hit the character. We'll use the "defDefense" field for this purpose. 

Shields also have a Parry bonus that must be applied when the shield is equipped. To track the Parry bonus, we need to add a new field to the "Shield" component. The field is a simple, static value, and it should look like the following.

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

Once the field is defined, we need to apply the Parry bonus to the trait appropriately. This requires that we define a new Eval script within the "Shield" component that performs the task. If a shield is equipped, it needs to add its Parry bonus to the Parry trait, resulting in the script below.

<eval index="2" phase="PreTraits" priority="5000">
  <before name="Calc trtFinal"/><![CDATA[
  if (field[grIsEquip].value > 0) then
    #traitbonus[trParry] += field[defParry].value
    endif
  ]]></eval> 

Updated Description

Armor and shields both introduce a few facets that need to be included in the description text that is synthesized for display to the user. This text is generated within the "InfoDef" procedure that is found in the file "procedures.dat". For armor, we need to report what areas are covered. For shields, we need to report both the armor bonus that is applied to ranged attacks and the Parry bonus. We can differentiate these two object types via the "component" tag each possesses. This yields the following revised procedure for showing the new information.

<procedure id="InfoDef" context="info"><![CDATA[
  ~declare variables that are used to communicate with our caller
  var iteminfo as string

  ~if this is armor, output the appropriate details
  if (tagis[component.Armor] <> 0) then
    ~add the armor rating
    iteminfo &= "Armor: " & field[defDefense].text & "{br}"
    ~identify the areas covered by the armor
    iteminfo &= "Coverage: " & tagnames[ArmorLoc.?,", "] & "{br}"

  ~otherwise, this is a shield, so output the appropriate details
  else
    ~add the parry bonus
    iteminfo &= "Parry Bonus: " & signed(field[defParry].value) & "{br}"
    ~add the ranged attack defense (if any)
    if (field[defDefense].value > 0) then
      iteminfo &= "Ranged Defense: " & field[defDefense].text & "{br}"
      endif
    endif

  ~report the minimum strength requirement (omitting if there is none)
  if (field[defStrReq].value > 0) then
    iteminfo &= "Minimum Strength: " & field[defStrReq].value & "{br}"
    endif
  ]]></procedure> 

Add Armor and Shields

All of the mechanics for armor and shields is now in place, so we can begin adding all of the various items to the data files. Just like weapons, they should be added to the file "thing_armory.dat". A few examples are provided below. You can either add the rest or pull them out of the complete Savage Worlds data files that are provided.

<thing
  id="armLeather"
  name="Leather Armor"
  compset="Armor"
  description="Description goes here">
  <fieldval field="defDefense" value="1"/>
  <fieldval field="grCost" value="50"/>
  <fieldval field="gearweight" value="15"/>
  <usesource id="TimeMedi"/>
  <tag group="ArmorType" tag="MedArmor"/>
  <tag group="ArmorLoc" tag="Torso"/>
  <tag group="ArmorLoc" tag="Arms"/>
  <tag group="ArmorLoc" tag="Legs"/>
  </thing> 

<thing
  id="shdMedium"
  name="Medium Shield"
  compset="Shield"
  description="Description goes here">
  <fieldval field="defDefense" value="2"/>
  <fieldval field="defParry" value="1"/>
  <fieldval field="grCost" value="50"/>
  <fieldval field="gearweight" value="12"/>
  <usesource id="TimeMedi"/>
  <tag group="ArmorType" tag="MedShield"/>
  </thing> 

Revise Table for Armor and Shields

The final task is to revise the way that armor and shields are visually managed within the UI. There is a table portal for defensive equipment that is named "arDefense" within the file "tab_armory.dat". After reviewing how everything is behaving, there are only two details that need to be changed, and they are the same two things that we needed to change for weapons. First, we need to increase the width of the area for showing armor descriptions, which entails adding the "descwidth" attribute to the "table_dynamic" element with a value of either 275 to 300. Second, we add the "choosesortset" attribute to the same "table_dynamic" element and assign it the unique id of the custom sort set that we created above ("Defense").