Armor (Savage)

From HLKitWiki
Revision as of 07:51, 17 December 2008 by Rob (talk | contribs) (New page: {{context|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==...)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
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 look like below.

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

<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 files always show 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="static"
  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 all 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". There are instances of this in the file "procedures.dat", as well as in the character sheet ("sheet_standard1.dat") and statblock output ("out_statblock.dat").

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 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.pri". 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 value="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>

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 that 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 value="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 need to put the new field on the "Defense" component as well. 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 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 that task. If a shield is equipped, it needs to add its Parry bonus to the Parry trait, resulting in the script below.

<eval value="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 & "\n"
    ~identify the areas covered by the armor
    iteminfo &= "Coverage: " & tagnames[ArmorLoc.?,", "] & "\n"
  ~otherwise, this is a shield, so output the appropriate details
  else
    ~add the parry bonus
    iteminfo &= "Parry Bonus: " & signed(field[defParry].value) & "\n"
    ~add the ranged attack defense (if any)
    if (field[defDefense].value > 0) then
      iteminfo &= "Ranged Defense: " & field[defDefense].text & "\n"
      endif
    endif
  ~report the minimum strength requirement (omitting if there is none)
  if (field[defStrReq].value > 0) then
    iteminfo &= "Minimum Strength: " & field[defStrReq].value & "\n"
    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 earlier ("Defense").