Finishing Off Weapons (Savage)
Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through
Overview
We now need to reconcile the remaining facets of weapons, such as the proper calculation of attack values and the handling of ammunition.
Weapon Attack Calculations
Early in the overall development process for these data files, we determined that Savage Worlds calculated the attack for weapons very differently from the default logic provided by the Skeleton files, so we disabled it. Now that we have all the various weapons in place, it's a perfect time to re-visit that and get it working correctly. The "wpNetAtk" field within the "BaseWeapon" component is provided for exactly this purpose. However, that field is value-based and it needs to be text-based so that it can display something like "d8+2". This means our first task is to change the "wpNetAtk" field to be text-based, which we can do by assigning it a "maxlength" attribute of "50". The revised field should look similar to the following.
<field id="wpNetAtk" name="Net Attack" type="derived" maxlength="50"> </field>
One Field to Harness Three
While we're mucking with fields, we might as well address another need. In order to independently synthesize an appropriately adjusted trait roll for the weapon attacks, those scripts will need to access the net bonus/penalty for the particular trait. Currently, that information is tracked across three separate fields, so we need to introduce a new field that calculates the net adjustment and can be readily accessed. We'll call our new field "trtNetRoll", and it must be generated after any penalties are applied due to the load limit, so we'll base our timing on the script we defined earlier that performs that role. In the end, our new field should look a lot like the definition below.
<field id="trtNetRoll" name="Net Roll Bonus" type="derived"> <calculate name="Calc trtNetRoll" phase="Traits" priority="9000"> <after name="Apply LoadLimit"/><![CDATA[ @value = field[trtRoll].value + field[trtProf].value + herofield[acNetPenal].value ]]></calculate> </field>
Re-Usable Procedure for Display
The logic we'll want to use when synthesizing the net attack for display will be very similar to the logic used to generate the final trait rolls for display within the "trtDisplay" field. Consequently, our next task should be to carve out that logic for re-use by putting it into a procedure. Once it's in a procedure, it can be used from both the current script that synthesizes "trtDisplay" and any other script that synthesizes "wpNetAtk".
Looking closely at the logic, there are two distinct values that must be passed into the procedure, with a string being returned for use by the caller. The two inbound values are the die type for the trait and any bonus/penalty to the roll. Based on these two inputs, our procedure can interpret the values correctly and synthesize the appropriate string for display to the user. The only thing special we need to do is ensure that the die type is properly bounded, just like is already done within the logic that synthesizes "trtDisplay". In the end, we should end up with a procedure that looks very similar to the one provided below.
<procedure id="FinalRoll" scripttype="none"><![CDATA[ ~declare variables that are used to communicate with our caller var finaldie as number var finalbonus as number var finaltext as string ~bound our final die type appropriately var final as number final = finaldie if (final < 2) then final = 2 elseif (final > 6) then final = 6 endif ~convert the final value for the trait to the proper die type for display var dietype as number dietype = final * 2 finaltext = "d" & dietype ~if there are any bonuses or penalties on the roll, append those the final result if (finalbonus <> 0) then finaltext &= signed(finalbonus) endif ]]></procedure>
Once the procedure is in place, we need to revise the Eval script that synthesizes the "trtDisplay" field so that it uses the new procedure. The net result should closely parallel the revised script below.
<eval index="4" phase="Render" priority="5000" name="Calc trtDisplay"> <after name="Calc trtNetRoll"/> <after name="Calc trtFinal"/><![CDATA[ ~if this is a derived trait, our display text is the final value if (tagis[component.Derived] <> 0) then field[trtDisplay].text = field[trtFinal].value done endif ~calculate the net bonuses and penalties for the roll var finalbonus as number finalbonus = field[trtNetRoll].value ~generate the appropriate results for display var finaldie as number var finaltext as string finaldie = field[trtFinal].value call FinalRoll ~put the final result into the proper field field[trtDisplay].text = finaltext ]]></eval>
Calculating the Net Attack
We've now got all of the pieces in place to be able to properly synthesize the "wpNetAtk" field for display to the user. There are two separate scripts in the file "equipment.str" that synthesize the "wpNetAtk" field - one for ranged weapons and another for hand weapons. We need two separate scripts because one is based on the "Shooting" skill, while the other is based on the "Fighting" skill.
We'll start by modifying the Eval script within the "WeapRange" component. The timing does not need to be touched, but we otherwise need to replace the script with revised logic that is similar to the synthesis of "trtDisplay" above and uses the "Shooting" skill. The key differences are two-fold. First, the bonus needs to incorporate both any bonus/penalty from the trait and any separate bonus/penalty from the weapon itself. Second, we need to handle the case where the character does not possess the needed skill. If not, then the die type will be zero due to the use of the "childfound." transition, which the procedure will bound as if it's a "d4". We must also apply the appropriate "-2" adjustment to the roll for being unskilled. The net result should yield a script that looks similar to the one below.
<eval index="1" phase="Final" priority="7000" name="Calc wpNetAtk"> <after name="Calc trtNetRoll"/> <after name="Calc trtFinal"/><![CDATA[ ~start with the bonuses and penalties associated with the weapon var finalbonus as number finalbonus = field[wpBonus].value + field[wpPenalty].value ~apply the appropriate adjustment for the associated skill if (hero.childexists[skShooting] = 0) then finalbonus -= 2 else finalbonus += hero.child[skShooting].field[trtNetRoll].value endif ~get the proper die type for the associated skill var finaldie as number finaldie = hero.childfound[skShooting].field[trtFinal].value ~generate the appropriate results for display var finaltext as string call FinalRoll ~put the final result into the proper field field[wpNetAtk].text = finaltext ]]></eval>
We can now define a very similar Eval script for the "WeapMelee" component that synthesizes the net attack based on the "Fighting" skill. The existing script can have the comments removed from around it and can then have its contents replaced with the revised logic shown below.
<eval value="2" phase="Final" priority="7000" name="Calc wpNetAtk"> <after name="Calc trtNetRoll"/> <after name="Calc trtFinal"/><![CDATA[ ~start with the bonuses and penalties associated with the weapon var finalbonus as number finalbonus = field[wpBonus].value + field[wpPenalty].value ~apply the appropriate adjustment for the associated skill if (hero.childexists[skFighting] = 0) then finalbonus -= 2 else finalbonus += hero.child[skFighting].field[trtNetRoll].value endif ~get the proper die type for the associated skill var finaldie as number finaldie = hero.childfound[skFighting].field[trtFinal].value ~generate the appropriate results for display var finaltext as string call FinalRoll ~put the final result into the proper field field[wpNetAtk].text = finaltext ]]></eval>
Ammunition
The final aspect of weapons that we need to address is ammunition. All ammunition is actually purchased and managed as gear via the Gear tab, but we'll address it as part of the weapons. There really isn't anything to deal with for ammunition, except that we need to add a new tag for the new type of gear. Once that's done, it's just a matter of adding all of the appropriate entries for each type of ammunition. A couple of 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="eqBulletSm" name="Bullets (Small)" compset="Ammunition" lotsize="50" stacking="merge" description="Includes .22 to .32 caliber weapons"> <fieldval field="grCost" value=".2"/> <fieldval field="grWeight" value=".06"/> <tag group="GearType" tag="Ammo"/> </thing> <thing id="eqLsrBatt" name="Laser Battery" compset="Ammunition" lotsize="1" stacking="merge" description="Provides one full load of shots for the laser pistol, rifle, or MG"> <fieldval field="grCost" value="25"/> <fieldval field="grWeight" value="1"/> <usesource id="TimeFuture"/> <tag group="GearType" tag="Ammo"/> </thing>
Thrown Weapons
After doing a bit of testing of our changes, there is something we've overlooked. Savage Worlds has separate skills for "Shooting" and "Throwing". However, we neglected to distinguish that detail when calculating the net attack for ranged weapons.
The first thing we need to do is differentiate between thrown weapons and fired weapons. We can accomplish that by defining a new tag in the "Weapon" tag group in the file "tags.1st". This new tag will identify a weapon that uses the "Throwing" skill, with any weapon lacking the tag using the "Shooting" skill. The new tag definition is as simple as below.
<value id="Thrown"/>
With the tag in place, we can assign it to the appropriate weapons, such as a throwing knife. We can then modify the Eval script that calculates the "wpNetAtk" field within the "WeapRange" component. The overall logic remains the same, but we need to use the different skill when a weapon is thrown. The revised Eval script is shown below.
~start with the bonuses and penalties associated with the weapon var finalbonus as number finalbonus = field[wpBonus].value + field[wpPenalty].value ~apply the appropriate adjustment for the associated skill if (tagis[Weapon.Thrown] <> 0) then if (hero.childexists[skThrowing] = 0) then finalbonus -= 2 else finalbonus += hero.child[skThrowing].field[trtNetRoll].value endif else if (hero.childexists[skShooting] = 0) then finalbonus -= 2 else finalbonus += hero.child[skShooting].field[trtNetRoll].value endif endif ~get the proper die type for the associated skill var finaldie as number if (tagis[Weapon.Thrown] <> 0) then finaldie = hero.childfound[skThrowing].field[trtFinal].value else finaldie = hero.childfound[skShooting].field[trtFinal].value endif ~generate the appropriate results for display var finaltext as string call FinalRoll ~put the final result into the proper field field[wpNetAtk].text = finaltext