Difference between revisions of "Weapons (Savage)"

From HLKitWiki
Jump to: navigation, search
Line 22: Line 22:
 
~report the armor piercing rating of the weapon (if any)
 
~report the armor piercing rating of the weapon (if any)
 
if (field[wpPiercing].value > 0) then
 
if (field[wpPiercing].value > 0) then
   iteminfo &= "Armor Piercing: " & field[wpPiercing].value & "\n"
+
   iteminfo &= "Armor Piercing: " & field[wpPiercing].value & "{br}"
 
   endif
 
   endif
 
</pre>
 
</pre>
Line 132: Line 132:
 
~report the parry adjustment of the weapon (if any)
 
~report the parry adjustment of the weapon (if any)
 
if (field[wpParry].value <> 0) then
 
if (field[wpParry].value <> 0) then
   iteminfo &= "Parry Adjustment: " & signed(field[wpPiercing].value) & "\n"
+
   iteminfo &= "Parry Adjustment: " & signed(field[wpPiercing].value) & "{br}"
 
   endif  
 
   endif  
  
 
~report the reach of the weapon (if any)
 
~report the reach of the weapon (if any)
 
if (field[wpReach].value > 0) then
 
if (field[wpReach].value > 0) then
   iteminfo &= "Reach: " & field[wpReach].value & "\n"
+
   iteminfo &= "Reach: " & field[wpReach].value & "{br}"
 
   endif
 
   endif
 
</pre>
 
</pre>

Revision as of 00:10, 23 December 2008

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

Now that basic gear is in, this section will examine a more complex type of equipment: weapons. We'll continue with the same process we began in the previous chapter, assessing the differences between the Savage Worlds game system and the default mechanisms provided by the Skeleton data files.

Weapons in General

There is only one facet of weapons in general that is distinct from the default behaviors of the Skeleton files. Both hand weapons and ranged weapons can possess the "armor piercing" attribute, with a corresponding rating being specified. Since there is a rating involved, we need to add a new field to the "BaseWeapon" component in order to track the rating. You'll find the component in the file "equipment.str", and the new field should look similar to the one shown below.

<field
  id="wpPiercing
  name="Armor Piercing"
  type="static">
  </field>

Once the new field is added, we then need to include it in the description text for the weapon. All descriptions are routed through the assorted procedures in the file "procedures.dat", so open that file. Locate the "InfoWeapon" procedure, which synthesizes the generic details of all weapons. Insert the necessary lines of script code to include the armor piercing rating within the details. It's probably best to only include the info for weapons that possess a non-zero armor piercing rating, so the new code should look similar to the lines below.

~report the armor piercing rating of the weapon (if any)
if (field[wpPiercing].value > 0) then
  iteminfo &= "Armor Piercing: " & field[wpPiercing].value & "{br}"
  endif

The final thing we need to do is add the armor piercing rating to the special notes that are shown for weapons on the Armory tab. The notes to be displayed are synthesized into the "wpNotes" field for weapons via an Eval script within the "WeaponBase" component. Locate this script and add the appropriate new code to include the armor piercing rating at the start. The new code should look similar to the lines below.

~report the armor piercing rating of the weapon (if any)
if (field[wpPiercing].value > 0) then
  if (empty(special) = 0) then
    special &= ", "
    endif
  special &= "AP " & field[wpPiercing].value
  endif 

Minimum Strength Requirement

Weapons may be specified as having a minimum strength requirement. If a weapon is equipped when that requirement is not meant, using the weapon incurs a penalty of -1 for each step of shortfall. The Skeleton files provide some basic mechanisms for managing the strength requirement, but we must apply the penalty appropriately ourselves. Open the file "equipment.str" and locate the "WeaponBase" component. The first Eval script handles the minimum strength requirement logic, applying a penalty to the weapon and having a place to apply a more general penalty. Within Savage Worlds, there is no generalized penalty, so those lines can be deleted from the script. The penalty adjustment applied is currently a flat "-1", so we must change it to represent the shortfall. The net result is a revised Eval script that looks similar to the following script.

<eval index="1" phase="Final" priority="5000"><![CDATA[
  ~if the minimum strength is satisfied, there's nothing to do
    if (field[wpStrReq].value <= #trait[attrStr]) then
      done
      endif

  ~assign a tag to indicate the requirement isn't met
  perform assign[Helper.BadStrReq]

  ~apply any penalty required with the specific weapon
  field[wpPenalty].value = #trait[attrStr] - field[wpStrReq].value

  ~if not equipped, there's nothing more to do
  if (field[grIsEquip].value = 0) then
    done
    endif
  ]]></eval> 

Weapon Categories

In the same way we encountered for basic gear, weapons in Savage Worlds are grouped into categories or types. If we want our weapons to be displayed to the user in appropriate groupings to match the rulebook, we'll need to manage these groupings. This entails the creation of a new tag group that we'll call "WeaponType" and for which we'll define tags for each grouping. This gets added to the file "tags.1st". We can then assign the appropriate tag to each weapon. We could potentially create separate tag groups for hand weapons and ranged weapons, but there is no real benefit in doing it, so we'll just use a single tag group for all weapons. As we did for basic gear, the tag group needs to be assigned the "explicit" sequencing behavior, and each tag needs to be assigned an appropriate "order" attribute to designate the proper sort order to match the rulebook. The net result is a tag group that looks like the following.

<group
  id="WeaponType"
  sequence="explicit">
  <value id="MedBlades" name="Medieval Blades" order="1"/>
  <value id="MedAxes" name="Medieval Axes and Mauls" order="2"/>
  <value id="MedPoles" name="Medieval Pole Arms" order="3"/>
  <value id="ModMelee" name="Modern Melee" order="4"/>
  <value id="FutMelee" name="Futuristic Melee" order="5"/>
  <value id="MedRange" name="Medieval Ranged" order="10"/>
  <value id="Black" name="Black Powder" order="11"/>
  <value id="ModPistol" name="Modern Pistol" order="12"/>
  <value id="ModSMG" name="Modern Submachine Gun" order="13"/>
  <value id="ModShotgun" name="Modern Shotgun" order="14"/>
  <value id="ModRifle" name="Modern Rifle" order="15"/>
  <value id="ModAssault" name="Modern Assault Rifle" order="16"/>
  <value id="ModMachine" name="Modern Machine Gun" order="17"/>
  <value id="FutRange" name="Futuristic Ranged" order="18"/>
  </group>

With the weapon type available, we need to put it to use. This will be done through a new sort set that parallels what we did for basic gear, and we'll call it "Weapon". The sort set first sorts on the weapon type and then on the name of the thing, which results in the following definition to be added to the file "control.1st".

<sortset
  id="Weapon"
  name="Weapon By Type and Name">
  <sortkey isfield="no" id="WeaponType"/>
  <sortkey isfield="no" id="_Name_"/>
  </sortset> 

Hand Weapons

There are two aspects of hand weapons that differ from what's provided by the Skeleton files. The first is the parry adjustment, and the second is the weapon's reach. As with how we handled armor piercing, both of these aspects are numeric ratings, so we'll add new fields to track both of them. Since these aspects only apply to hand weapons, we'll add them to the "WeapMelee" component, which is in the file "equipment.str". The new fields should look similar to the ones shown below.

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

<field
  id="wpReach"
  name="Reach Distance"
  type="static">
  </field> 

With the tracking of the Parry adjustment, we need to apply that adjustment appropriately when the weapon is equipped. That entails defining a new Eval script for the component. All it needs to do is apply the proper adjustment to Parry, which is achieved via the script below.

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

Again as we did for armor piercing, we need to add these ratings to the weapon description details. This can be done within the "InfoMelee" procedure within the file "procedures.dat". Since the rulebook omits these ratings unless they are non-zero, we'll do the same within the description text. This yields code that should look similar to the following.

~report the parry adjustment of the weapon (if any)
if (field[wpParry].value <> 0) then
  iteminfo &= "Parry Adjustment: " & signed(field[wpPiercing].value) & "{br}"
  endif 

~report the reach of the weapon (if any)
if (field[wpReach].value > 0) then
  iteminfo &= "Reach: " & field[wpReach].value & "{br}"
  endif

Our last task is to add these two ratings to the special notes shown for weapons on the Armory tab. This can be done via the Eval script within the "WeapMelee" component that performs this function. Since the logic is basically the same, we can use the code from adding the armor piercing as a template. The new lines of code should look like below.

~report the parry adjustment of the weapon (if any)
if (field[wpParry].value <> 0) then
  if (empty(special) = 0) then
    special &= ", "
    endif
  special &= "Parry " & signed(field[wpParry].value)
  endif

~report the reach rating of the weapon (if any)
if (field[wpReach].value <> 0) then
  if (empty(special) = 0) then
    special &= ", "
    endif
  special &= "Reach " & field[wpReach].value
  endif 

Add Hand Weapons

Everything is now in place for us to start adding all of the various hand weapons to the data files. All weapons 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="wpGreatswd"
  name="Greatsword"
  compset="Melee"
  description="Description goes here">
  <fieldval field="wpDamage" value="Str+4"/>
  <fieldval field="wpStrReq" value="5"/>
  <fieldval field="wpParry" value="-1"/>
  <fieldval field="grCost" value="400"/>
  <fieldval field="grWeight" value="12"/>
  <usesource id="TimeMedi"/>
  <usesource id="TimePowder"/>
  <tag group="WeaponType" tag="MedBlades"/>
  <tag group="Equipment" tag="TwoHand"/>
  </thing>

<thing
  id="wpSpear"
  name="Spear"
  compset="Melee"
  description="Description goes here">
  <fieldval field="wpDamage" value="Str+2"/>
  <fieldval field="wpStrReq" value="3"/>
  <fieldval field="wpParry" value="1"/>
  <fieldval field="wpReach" value="1"/>
  <fieldval field="grCost" value="250"/>
  <fieldval field="grWeight" value="5"/>
  <usesource id="TimeMedi"/>
  <usesource id="TimePowder"/>
  <tag group="WeaponType" tag="MedPoles"/>
  <tag group="Equipment" tag="TwoHand"/>
  </thing>

<thing
  id="wpSurvKnif"
  name="Survival Knife"
  compset="Melee"
  description="Description goes here">
  <fieldval field="wpDamage" value="Str+1"/>
  <fieldval field="wpSpecial" value="Contains supplies that add +1 to Survival rolls"/>
  <fieldval field="grCost" value="50"/>
  <fieldval field="grWeight" value="3"/>
  <usesource id="TimeModern"/>
  <tag group="WeaponType" tag="ModMelee"/>
  <eval index="1" phase="PreTraits" priority="5000">
    <before name="Calc trtFinal"/><![CDATA[
    #traitroll[skSurvival] += 1
    ]]></eval>
  </thing>

While we're adding all of the hand weapons, we also need to revamp the provided "Unarmed Strike" weapon so that it behaves properly as a Savage Worlds weapon. The revised version should look similar to the following.

<thing
  id="wpUnarmed"
  name="Unarmed Strike"
  compset="Melee"
  description="Description goes here"
  isunique="yes"
  holdable="no">
  <fieldval field="wpDamage" value="Str"/>
  <fieldval field="wpSpecial" value=""/>
  <fieldval field="grCost" value="0"/>
  <fieldval field="grWeight" value="0"/>
  <tag group="Equipment" tag="Natural"/>
  </thing> 

Revise Table for Hand Weapons

All of the hand weapons are in place, so we can now revise how the weapons are managed visually within the table. The entirety of the "Amory" tab is defined within the file "tab_armory.dat", so that's where we'll be making our next changes. The table portal for hand weapons is named "arMelee". 

We'll start with the same two changes we made to the table for showing basic gear. First, the width of the area for showing weapons descriptions is too narrow, so add the "descwidth" attribute to the "table_dynamic" element with a value of either 275 or 300. Second, we need to sort the weapons using the sort set we just defined for weapons. So add the "choosesortset" attribute to the same "table_dynamic" element and assign it the unique id of our new sort set: "Weapon".

The next thing we need to adjust is that the Skeleton files we inherited refer to "Melee Weapons", while Savage Worlds uses the term "Hand Weapons". Internally, this distinction is not important, but we want everything to look familiar to users. So we need to modify the scripts within the "headertitle" and "additem" elements to change the terms used.

After making the above changes, we should end up with a revised "arMelee" portal that looks similar to the one below.

<portal
  id="arMelee"
  style="tblNormal">
  <table_dynamic
    component="Gear"
    showtemplate="arWpnPick"
    choosetemplate="arWpnThing"
    choosesortset="Weapon"
    buytemplate="BuyCash"
    selltemplate="SellCash"
    descwidth="275">
    <list>component.WeapMelee</list>
    <candidate inheritlist="yes">!Equipment.Natural</candidate>
    <titlebar><![CDATA[
      @text = "Select Hand Weapons to Purchase from the List Below"
      ]]></titlebar>
    <headertitle><![CDATA[
      @text = "Hand Weapons"
      ]]></headertitle>
    <additem><![CDATA[
      @text = "Add New Hand Weapons"
      ]]></additem>
    </table_dynamic>
  </portal>

We should now ask ourselves if there are any of the new fields that we'd like to see shown for weapons that have been added to the character. The two solid candidates are the Parry adjustment and the AP rating for the weapon. Whenever a weapon is equipped, any Parry adjustment is automatically applied, so it's not something that the user can forget to add, which means it's really not important to remind the user about it. The AP rating, though, it quite different. It's very important that the user not forget about an AP rating for a wielded weapon, so we really should make a point of showing it. 

Showing the AP rating within the template entails a number of additions and revisions. It all centers upon the template, which is named "arWpnPick" and will be found in the file "tab_armory.dat". Before we proceed, note that this one template is used for both hand weapons and ranged weapons, so our additions will appear for both weapon types unless we takes steps to only show it for one type. Since the AP rating is highly applicable for both weapon types, we'll just add it so that it's visible for all weapons.

The first thing we need to do is add a new portal to the template. Since we want the new portal to appear between the damage rating and any range information for the weapon, we'll position the new portal between those two portals within the template. If we use a field-based label portal, we'll only be able to display the raw value, which won't be very clear to the user. So we'll use a script-based label portal instead, allowing us to include a useful prefix and display the AP rating in the form "AP2". This means our new portal should look similar to the one below.

<portal
  id="piercing"
  style="lblDisable">
  <label>
    <labeltext><![CDATA[
      if (field[wpPiercing].value > 0) then
        @text = "AP" & field[wpPiercing].value
      else
        @text = ""
        endif
      ]]></labeltext>
    </label>
  </portal>

Once the portal has been added, we now need to position it properly. In order to make room for it, we'll also need to nudge around some of the other portals. After a little bit of experimenting, a suitable result can be achieved, yielding the following new Position script.

~set up our height based on our tallest portal
height = portal[info].height

~if this is a "sizing" calculation, we're done
if (issizing <> 0) then
  done
  endif

~center the portals vertically
perform portal[info].centervert
perform portal[name].centervert
perform portal[attack].centervert
perform portal[damage].centervert
perform portal[piercing].centervert
perform portal[range].centervert
perform portal[special].centervert
perform portal[gearmanage].centervert
perform portal[delete].centervert
perform portal[strreq].centervert
perform portal[heldby].centervert

~position the delete portal on the far right
perform portal[delete].alignedge[right,0]

~position the info portal to the left of the delete button
perform portal[info].alignrel[rtol,delete,-6]
position the gear portal to the left of the info button
perform portal[gearmanage].alignrel[rtol,info,-6]

~position the special portal to the left of the gear button
perform portal[special].alignrel[rtol,gearmanage,-6]

~position the range portal to the left of the delete button; we want the
~damage to be centered in its own column relative to a centerpoint position
perform portal[range].centerpoint[horz,335]

~position the AP portal to the left of the range column; we want the AP to
~be centered in its own column relative to a centerpoint position
perform portal[piercing].centerpoint[horz,280]

~position the damage portal to the left of the AP column; we want the damage
~to be centered in its own column relative to a centerpoint position
perform portal[damage].centerpoint[horz,240]

~position the attack portal to the left of damage column; we want the
~attack to be centered in its own column relative to a centerpoint position
perform portal[attack].centerpoint[horz,195]

~position the name on the left and let it use all available space
var limit as number
limit = portal[attack].left - 8 - portal[heldby].width - 5 - portal[strreq].width - 2
portal[name].left = 0
portal[name].width = minimum(portal[name].width,limit)

~show the 'strength requirement' icon to the right of the name
perform portal[strreq].alignrel[ltor,name,2]
portal[strreq].visible = tagis[Helper.BadStrReq]

~show the 'held by' icon to the right of the strenght requirement if appropriate
if (portal[strreq].visible = 0) then
  portal[heldby].left = portal[strreq].left
else
  perform portal[heldby].alignrel[ltor,strreq,5]
  endif
portal[heldby].visible = isgearheld

~if this is not a ranged weapon, hide the range portal
if (tagis[component.WeapRange] = 0) then
  portal[range].visible = 0
  endif

~only show the special portal if there are special abilities/notes to view
portal[special].visible = 1 - field[wpNotes].isempty