Damage (Savage): Difference between revisions

From HLKitWiki
Jump to navigationJump to search
No edit summary
 
(4 intermediate revisions by the same user not shown)
Line 50: Line 50:
   type="derived"
   type="derived"
   maxfinal="50">
   maxfinal="50">
   <calculate phase="Render" priority="1000">
   <calculate phase="Render" priority="1000"><![CDATA[
    <after name="Calc acHPNow"/><![CDATA[
     ~make sure this value consists of the elements that could cause the summary to change
     ~make sure this value consists of the elements that could cause the summary to change
     @value = field[acShaken].value * 10000 + field[acWounds].value * 100 + field[acFatigue].value
     @value = field[acShaken].value * 10000 + field[acWounds].value * 100 + field[acFatigue].value
Line 110: Line 109:


<pre>
<pre>
<eval index="1" phase="Setup" priority="1000"><![CDATA[
<eval index="1" phase="Final" priority="1000"><![CDATA[
  ~if no damage has been incurred, assign a tag to indicate that state
   if (field[acShaken].value + field[acWounds].value + field[acFatigue].value = 0) then
   if (field[acShaken].value + field[acWounds].value + field[acFatigue].value = 0) then
     perform hero.assign[Hero.NoDamage]
     perform hero.assign[Hero.NoDamage]
  ~if the hero is dead or otherwise out of combat, indicate that state
  elseif (field[acWounds].value >= 4) then
    perform hero.assign[Hero.Dead]
  elseif (field[acFatigue].value >= 3) then
    perform hero.assign[Hero.Dead]
     endif
     endif
   ]]></eval>  
   ]]></eval>  
Line 121: Line 127:
A few minor cleanup steps still remain. With all of the portals eliminated from the template, the Position script for the template needs to be omitted as well - we'll restore it and adapt it once we put some portals back in. The template itself needs to be switched over to the "Actor" component instead of the deleted "Damage" component. And lastly, the "templateref" within the layout needs to reference the "actor" thing instead of the deleted "mscDamage" thing.
A few minor cleanup steps still remain. With all of the portals eliminated from the template, the Position script for the template needs to be omitted as well - we'll restore it and adapt it once we put some portals back in. The template itself needs to be switched over to the "Actor" component instead of the deleted "Damage" component. And lastly, the "templateref" within the layout needs to reference the "actor" thing instead of the deleted "mscDamage" thing.


At this point, you should now be able to re-compile the data files, although the "Health" section of the "In-Play" tab will not yet behave in any useful fashion.  
At this point, you should now be able to re-compile the data files, although the "Health" section of the "In-Play" tab will not yet behave in any useful fashion.


===Managing Damage===
===Managing Damage===
Line 132: Line 138:
   name="Shaken Text"
   name="Shaken Text"
   type="derived"
   type="derived"
  calcphase="Render"
  calcprior="1000"
   maxfinal="50">
   maxfinal="50">
   <calculate><![CDATA[
 
   <calculate phase="Render" priority="1000">
     @value = field[acShaken].value
     @value = field[acShaken].value
     ]]></calculate>
     </calculate>
 
   <finalize><![CDATA[
   <finalize><![CDATA[
     @text = ""
     @text = ""
Line 157: Line 163:
   <checkbox
   <checkbox
     field="acShaken"
     field="acShaken"
    message=""
     dynamic="acShakeTxt">
     dynamic="acShakeTxt">
     </checkbox>
     </checkbox>
Line 169: Line 174:
   id="chkLarge">
   id="chkLarge">
   <style_checkbox
   <style_checkbox
     textcolor="f0f0f0"
     textcolor="clrnormal"
     font="fntCheckLg">
     font="fntchecklg">
     </style_checkbox>
     </style_checkbox>
   <resource
   <resource
     id="fntCheckLg">
     id="fntchecklg">
     <font
     <font
       face="Arial"
       face="Arial"
Line 218: Line 223:
   tiptext="Click here to sustain one wound.">
   tiptext="Click here to sustain one wound.">
   <action
   <action
     action="trigger"
     action="trigger">
    buttontext="">
     <trigger><![CDATA[
     <trigger><![CDATA[
       ~if we've reached our maximum, there's nothing left to do
       ~if we've reached our maximum, there's nothing left to do
Line 238: Line 242:
   tiptext="Click here to heal one wound.">
   tiptext="Click here to heal one wound.">
   <action
   <action
     action="trigger"
     action="trigger">
    buttontext="">
     <trigger><![CDATA[
     <trigger><![CDATA[
       ~if we've reached our limit, there's nothing left to do
       ~if we've reached our limit, there's nothing left to do
Line 283: Line 286:
   tiptext="Click here to sustain one level of fatigue.">
   tiptext="Click here to sustain one level of fatigue.">
   <action
   <action
     action="trigger"
     action="trigger">
    buttontext="">
     <trigger><![CDATA[
     <trigger><![CDATA[
       ~if we've reached our maximum, there's nothing left to do
       ~if we've reached our maximum, there's nothing left to do
Line 301: Line 303:
   tiptext="Click here to restore one fatigue level.">
   tiptext="Click here to restore one fatigue level.">
   <action
   <action
     action="trigger"
     action="trigger">
    buttontext="">
     <trigger><![CDATA[
     <trigger><![CDATA[
       ~if we've reached our limit, there's nothing left to do
       ~if we've reached our limit, there's nothing left to do
Line 320: Line 321:
~set up our height based on our title, a gap, and our tallest portal
~set up our height based on our title, a gap, and our tallest portal
height = portal[title].height + portal[wounds].height + 8
height = portal[title].height + portal[wounds].height + 8
~if this is a "sizing" calculation, we're done
~if this is a "sizing" calculation, we're done
if (issizing <> 0) then
if (issizing <> 0) then
   done
   done
   endif
   endif
~the title should span the full width
~the title should span the full width
portal[title].width = width
portal[title].width = width
~position the tallest portal beneath the title
~position the tallest portal beneath the title
perform portal[wounds].alignrel[ttob,title,8]
perform portal[wounds].alignrel[ttob,title,8]
~center all non-text portals vertically on the wounds portal
~center all non-text portals vertically on the wounds portal
perform portal[shaken].centeron[vert,wounds]
perform portal[shaken].centeron[vert,wounds]
Line 335: Line 340:
perform portal[ftgsustain].centeron[vert,wounds]
perform portal[ftgsustain].centeron[vert,wounds]
perform portal[ftgheal].centeron[vert,wounds]
perform portal[ftgheal].centeron[vert,wounds]
~align the smaller text portals to have the same baseline
~align the smaller text portals to have the same baseline
perform portal[lblwounds].alignrel[btob,wounds,-3]
perform portal[lblwounds].alignrel[btob,wounds,-3]
perform portal[lblfatigue].alignrel[btob,wounds,-3]
perform portal[lblfatigue].alignrel[btob,wounds,-3]
~position the shaken portal on the left
~position the shaken portal on the left
portal[shaken].left = 15
portal[shaken].left = 15
~position the wound portals next to the shaken portal
~position the wound portals next to the shaken portal
perform portal[lblwounds].alignrel[ltor,shaken,35]
perform portal[lblwounds].alignrel[ltor,shaken,35]
perform portal[wounds].alignrel[ltor,lblwounds,3]
perform portal[wounds].alignrel[ltor,lblwounds,3]
portal[wounds].width = 30
portal[wounds].width = 30
~position the sustain and heal portals next to the wounds
~position the sustain and heal portals next to the wounds
perform portal[wndsustain].alignrel[ltor,wounds,6]
perform portal[wndsustain].alignrel[ltor,wounds,6]
perform portal[wndheal].alignrel[ltor,wndsustain,6]
perform portal[wndheal].alignrel[ltor,wndsustain,6]
~position the fatigue portals next to the wound portals
~position the fatigue portals next to the wound portals
perform portal[lblfatigue].alignrel[ltor,wndheal,35]
perform portal[lblfatigue].alignrel[ltor,wndheal,35]
perform portal[fatigue].alignrel[ltor,lblfatigue,3]
perform portal[fatigue].alignrel[ltor,lblfatigue,3]
portal[fatigue].width = 30
portal[fatigue].width = 30
~position the sustain and heal portals next to the fatigue
~position the sustain and heal portals next to the fatigue
perform portal[ftgsustain].alignrel[ltor,fatigue,6]
perform portal[ftgsustain].alignrel[ltor,fatigue,6]
Line 358: Line 369:
===Rename the "acHPSumm" Field===
===Rename the "acHPSumm" Field===


When we were overhauling everything above, we left the "acHPSumm" field in place and simply changed its behavior. However, that field is inappropriate for Savage Worlds, so we should change it now that everything is again stable. We'll change the id to "acDmgSumm". When we re-compile, three errors are reported, which we can chase down and correct quickly. The three errors occur in the files "panel_static.dat", "panel_dashboard.dat", and "panel_taccon.dat". All we need is a quick search-and-replace to swap in the new unique id.  
When we were overhauling everything above, we left the "acHPSumm" field in place and simply changed its behavior. However, that field is inappropriate for Savage Worlds, so we should change it now that everything is again stable. We'll change the id to "acDmgSumm". When we re-compile, three errors are reported, which we can chase down and correct quickly. The three errors occur in the files "form_static.dat", "form_dashboard.dat", and "form_taccon.dat". All we need is a quick search-and-replace to swap in the new unique id.  


===Applying Damage to Trait Rolls===
===Applying Damage to Trait Rolls===
Line 368: Line 379:
   id="acNetPenal"
   id="acNetPenal"
   name="Net Penalties"
   name="Net Penalties"
   type="derived"
   type="derived">
   calcphase="Traits"
   <calculate phase="Traits" priority="5000">
  calcprior="5000">
  <calculate>
     <before name="Derived trtFinal"/><![CDATA[
     <before name="Derived trtFinal"/><![CDATA[
     @value = -1 * (field[acWounds].value + field[acFatigue].value)
     @value = -1 * (field[acWounds].value + field[acFatigue].value)
Line 382: Line 391:


<pre>
<pre>
<eval value="1" phase="Traits" priority="6000" name="Derived trtFinal"><![CDATA[
<eval index="1" phase="Traits" priority="6000" name="Derived trtFinal"><![CDATA[
   field[trtFinal].value = field[trtBonus].value + field[trtInPlay].value + herofield[acNetPenal].value
   field[trtFinal].value = field[trtBonus].value + field[trtInPlay].value + herofield[acNetPenal].value
   ]]></eval>
   ]]></eval>
</pre>
</pre>


For attributes and skills, the penalty is applied to the actual trait "roll" instead of the trait itself. The trait roll is determined as part of the process for synthesizing the "trtDisplay" field in an Eval script for the "Trait" component. When the bonus is tallied from the "trtRoll" and "trtProf" fields, the penalty should also be factored in, resulting in the appropriate net roll adjustment being calculated, as shown below.
For attributes and skills, the penalty is applied to the actual trait "roll" instead of the trait itself. The trait roll is determined as part of the process for synthesizing the "trtDisplay" field in an Eval script for the "Trait" component. When the bonus is tallied from the "trtRoll" and "trtNoStack" fields, the penalty should also be factored in, resulting in the appropriate net roll adjustment being calculated, as shown below.


<pre>
<pre>
<eval value="4" phase="Render" priority="5000" name="Calc trtDisplay">
<eval index="4" phase="Render" priority="5000" name="Calc trtDisplay">
   <after name="Calc trtFinal"/><![CDATA[
   <after name="Calc trtFinal"/><![CDATA[
   ~if this is a derived trait, our display text is the final value
   ~if this is a derived trait, our display text is the final value
Line 397: Line 406:
     done
     done
     endif
     endif
   ~bound our final value including in-play adjustments
   ~bound our final value including in-play adjustments
   var final as number
   var final as number
Line 405: Line 415:
     final = 6
     final = 6
     endif
     endif
   ~convert the final value for the trait to the proper die type for display
   ~convert the final value for the trait to the proper die type for display
   var dietype as number
   var dietype as number
Line 410: Line 421:
   dietype = final * 2
   dietype = final * 2
   display = "d" & dietype
   display = "d" & dietype
   ~if there are any bonuses or penalties on the roll, append those the final result
   ~if there are any bonuses or penalties on the roll, append those the final result
   var bonus as number
   var bonus as number
   bonus = field[trtRoll].value + field[trtProf].value + herofield[acNetPenal].value
   bonus = field[trtRoll].value + field[trtNoStack].value + herofield[acNetPenal].value
   if (bonus <> 0) then
   if (bonus <> 0) then
     display &= signed(bonus)
     display &= signed(bonus)
     endif
     endif
   ~put the final result into the proper field
   ~put the final result into the proper field
   field[trtDisplay].text = display
   field[trtDisplay].text = display
   ]]></eval>  
   ]]></eval>  
</pre>
</pre>

Latest revision as of 22:39, 19 February 2009

Context: HL Kit &#133; Authoring Examples &#133; Savage Worlds Walk-Through 

Overview

The next facet of the Savage Worlds game system that we're going to address is the management of damage. In Savage Worlds, damage works completely differently from the Skeleton files, so we're going to need to make substantial changes.

New Mechanics

The damage mechanics for Savage Worlds are very simple, with characters suffering three wounds before being incapacitated. Fatigue works similarly, with there being two levels beyond zero before incapacitated. Characters must also track whether they are shaken, which is independent of the other two values. We could use a tracker for the wounds and fatigue, but the levels are so few and simple that it just doesn't make sense. Instead, we'll use a field to track the current value and use the normal health buttons for adjusting the damage level.

This results in the need for three new fields, which we'll add to the "Actor" component for simplicity. The "shaken" field must be designated as a "user" field, since the user will be responsible for toggling the state on/off. The other two fields will be controlled indirectly via up/down buttons. As such, they must both be declared as "derived" and must be assigned "full" persistence so that the values are saved and only change in response to the buttons. The three fields should look similar to the ones shown below.

<field
  id="acShaken"
  name="Is Shaken?"
  type="user">
  </field> 

<field
  id="acWounds"
  name="Wounds"
  type="derived"
  persistence="full">
  </field> 

<field
  id="acFatigue"
  name="Fatigue"
  type="derived"
  persistence="full">
  </field> 

Delete Old Mechanics

Since most game systems have a rather complex mechanic for damage, the Skeleton files provide support for that type of mechanic. However, none of that is needed for Savage Worlds, so we need to dispose of it all. The first item we need to delete is the "mscDamage" thing, which will be found in the file "thing_miscellaneous.dat". This particular thing is of zero use to us when managing damage for Savage Worlds.

The second task is to dispose of the "Damage" component and component set, which are both of no use to us. You'll find the component defined in the file "miscellaneous.str". The component set is auto-defined, since there is no need to blend other components into the component sets. Consequently, there is nothing to actually delete for the component set.

The third task is to dispose of all the health-related fields on the "Actor" component, except for the health summary ("acHPSumm"). These consist of the fields "acHPMin", "acHPMax", "acHPNow", and "acHPPenal".

Once the obsolete fields on the "Actor" component are gone, we need to adapt the health summary to the new mechanisms. The new logic should look something like the example below, where it properly reports the shaken state, wounds, and fatigue, highlighting any penalties appropriately.

<field
  id="acHPSumm"
  name="Health Summary"
  type="derived"
  maxfinal="50">
  <calculate phase="Render" priority="1000"><![CDATA[
    ~make sure this value consists of the elements that could cause the summary to change
    @value = field[acShaken].value * 10000 + field[acWounds].value * 100 + field[acFatigue].value
    ]]></calculate>

  <finalize><![CDATA[
    ~if we're not shaken and have incurred no negative effects, all is good
    var net as number
    net = field[acWounds].value + field[acFatigue].value
    if (field[acShaken].value + net = 0) then
      @text = "-"
      done
      endif

    ~if we're shaken, signal it with a special indicator
    @text = ""
    if (field[acShaken].value <> 0) then
      @text &= "{font wingdings}v{revert}"
      endif

    ~if we've incurred wounds, report them
    var wounds as number
    wounds = -field[acWounds].value
    if (wounds <> 0) then
      if (empty(@text) = 0) then
        @text &= "/"
        endif
      if (wounds <= -4) then
        @text &= "Inc"
      else
        @text &= wounds & "W"
        endif
      endif

    ~if we've incurred fatigue, report it
    var fatigue as number
    fatigue = -field[acFatigue].value
    if (fatigue <> 0) then
      if (empty(@text) = 0) then
        @text &= "/"
        endif
      if (fatigue <= -3) then
        @text &= "Inc"
      else
        @text &= fatigue & "F"
        endif
      endif

    ~anything we output must be in red to highlight the penalty
    @text = "{text ff0000}" & @text
    ]]></finalize>
  </field> 

The next step is to delete the usage pools associated with damage tracking. There are two of these ("DmgAdjust" and "DmgNet") and they will be found in the file "control.1st".

Once the usage pools are deleted, Eval script #1 within the "Actor" component will need to be revised to eliminate the reference to the usage pool. Change the script so that the test is based on the shaken state and the number of wounds and fatigue levels. The script below should serve nicely.

<eval index="1" phase="Final" priority="1000"><![CDATA[
  ~if no damage has been incurred, assign a tag to indicate that state
  if (field[acShaken].value + field[acWounds].value + field[acFatigue].value = 0) then
    perform hero.assign[Hero.NoDamage]

  ~if the hero is dead or otherwise out of combat, indicate that state
  elseif (field[acWounds].value >= 4) then
    perform hero.assign[Hero.Dead]
  elseif (field[acFatigue].value >= 3) then
    perform hero.assign[Hero.Dead]
    endif
  ]]></eval> 

The final big task is to eliminate most of the portals on the "In-Play" tab that are associated with damage tracking. These will be found in the file "tab_inplay.dat" and will be located in the template "ipHealth". Since we'll still be wanting to adapt a few of these portals to the new mechanism, it's better to simply comment out the various portal elements instead of outright deleting them right now.

A few minor cleanup steps still remain. With all of the portals eliminated from the template, the Position script for the template needs to be omitted as well - we'll restore it and adapt it once we put some portals back in. The template itself needs to be switched over to the "Actor" component instead of the deleted "Damage" component. And lastly, the "templateref" within the layout needs to reference the "actor" thing instead of the deleted "mscDamage" thing.

At this point, you should now be able to re-compile the data files, although the "Health" section of the "In-Play" tab will not yet behave in any useful fashion.

Managing Damage

We can now add damage management back into the data files in a systematic fashion. The first thing we need to add is a mechanism for the user to toggle the shaken state of the character. For now, we'll use a simple checkbox - we can always refine it later. In order to highlight the checkbox more prominently when a character is shaken, we'll modify the checkbox to display dynamically changing text, where the text changes to a bright red color when the character is shaken. This requires that we add another field to the "Actor" component wherein the dynamic text can be managed. The new field should look something like the following.

<field
  id="acShakeTxt"
  name="Shaken Text"
  type="derived"
  maxfinal="50">

  <calculate phase="Render" priority="1000">
    @value = field[acShaken].value
    </calculate>

  <finalize><![CDATA[
    @text = ""
    if (@value <> 0) then
      @text = "{text ff0000}"
      endif
    @text &= "Shaken"
    ]]></finalize>
  </field>

The checkbox portal that we're adding to the template should look similar to the following. 

<portal
  id="shaken"
  style="chkLarge"
  tiptext="Click to indicate the character is shaken">
  <checkbox
    field="acShaken"
    dynamic="acShakeTxt">
    </checkbox>
  </portal>

In the above checkbox, we specified a new checkbox style that is not pre-defined in the file "styles_ui.aug". We need a checkbox that shows larger text so that the shaken state is more prominently visible. This entails us defining a new style, which in turn requires that we define a new font. The new style definition should be very similar to the one shown below.

<style
  id="chkLarge">
  <style_checkbox
    textcolor="clrnormal"
    font="fntchecklg">
    </style_checkbox>
  <resource
    id="fntchecklg">
    <font
      face="Arial"
      size="48"
      style="bold">
      </font>
    </resource>
  </style>

We also need to track wounds and fatigue levels here. Both of them work pretty much the same, except that the fatigue track transitions to an incapacitated state one step earlier than wounds, so we'll use the same approach for both. Each will have a label portal to identify the grouping (wounds vs. fatigue), a script-based label portal to show the actual value, an action portal to sustain one level of negative progression, and an action portal to restore a level. We'll use the damage and healing buttons for sustaining and restoring, and we'll display the actual value with color highlighting when non-zero. We'll also make sure to use a large font to make things highly visible to the user. To keep wounds and fatigue distinct, we'll visually group the portals together for each purpose. All of the additional portals (besides the "shaken" checkbox and the title) are presented below.

<portal
  id="lblwounds"
  style="lblLarge">
  <label
    text="Wounds:">
    </label>
  </portal>

<portal
  id="wounds"
  style="lblXLarge">
  <label>
    <labeltext><![CDATA[
      var wounds as number
      wounds = -field[acWounds].value
      if (wounds > -4) then
        @text = wounds
      else
        @text = "Inc"
        endif
      if (wounds <> 0) then
        @text = "{text ff0000}" & @text
        endif
      ]]></labeltext>
    </label>
  </portal>

<portal
  id="wndsustain"
  style="actDamage"
  tiptext="Click here to sustain one wound.">
  <action
    action="trigger">
    <trigger><![CDATA[
      ~if we've reached our maximum, there's nothing left to do
      if (field[acWounds].value >= 4) then
        done
        endif
      ~sustain one new wound
      field[acWounds].value += 1
      ~any wound sustained automatically makes the character shaken
      field[acShaken].value = 1
      ]]></trigger>
    </action>
  </portal>

<portal
  id="wndheal"
  style="actHeal"
  tiptext="Click here to heal one wound.">
  <action
    action="trigger">
    <trigger><![CDATA[
      ~if we've reached our limit, there's nothing left to do
      if (field[acWounds].value = 0) then
        done
        endif
      ~heal one wound
      field[acWounds].value -= 1
      ]]></trigger>
    </action>
  </portal>

<portal
  id="lblfatigue"
  style="lblLarge">
  <label
    text="Fatigue:">
    </label>
  </portal>

<portal
  id="fatigue"
  style="lblXLarge">
  <label>
    <labeltext><![CDATA[
      var fatigue as number
      fatigue = -field[acFatigue].value
      if (fatigue > -3) then
        @text = fatigue
      else
        @text = "Inc"
        endif
      if (fatigue <> 0) then
        @text = "{text ff0000}" & @text
      endif
      ]]></labeltext>
    </label>
  </portal>

<portal
  id="ftgsustain"
  style="actDamage"
  tiptext="Click here to sustain one level of fatigue.">
  <action
    action="trigger">
    <trigger><![CDATA[
      ~if we've reached our maximum, there's nothing left to do
      if (field[acFatigue].value >= 3) then
        done
        endif
      ~sustain one new fatigue level
      field[acFatigue].value += 1
      ]]></trigger>
    </action>
  </portal>

<portal
  id="ftgheal"
  style="actHeal"
  tiptext="Click here to restore one fatigue level.">
  <action
    action="trigger">
    <trigger><![CDATA[
      ~if we've reached our limit, there's nothing left to do
      if (field[acFatigue].value = 0) then
        done
        endif
      ~restore one fatigue level
      field[acFatigue].value -= 1
      ]]></trigger>
    </action>
  </portal>

The Position script for the template that appropriately arranges everything should look something like the one presented below.

~set up our height based on our title, a gap, and our tallest portal
height = portal[title].height + portal[wounds].height + 8

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

~the title should span the full width
portal[title].width = width

~position the tallest portal beneath the title
perform portal[wounds].alignrel[ttob,title,8]

~center all non-text portals vertically on the wounds portal
perform portal[shaken].centeron[vert,wounds]
perform portal[wndsustain].centeron[vert,wounds]
perform portal[wndheal].centeron[vert,wounds]
perform portal[fatigue].centeron[vert,wounds]
perform portal[ftgsustain].centeron[vert,wounds]
perform portal[ftgheal].centeron[vert,wounds]

~align the smaller text portals to have the same baseline
perform portal[lblwounds].alignrel[btob,wounds,-3]
perform portal[lblfatigue].alignrel[btob,wounds,-3]

~position the shaken portal on the left
portal[shaken].left = 15

~position the wound portals next to the shaken portal
perform portal[lblwounds].alignrel[ltor,shaken,35]
perform portal[wounds].alignrel[ltor,lblwounds,3]
portal[wounds].width = 30

~position the sustain and heal portals next to the wounds
perform portal[wndsustain].alignrel[ltor,wounds,6]
perform portal[wndheal].alignrel[ltor,wndsustain,6]

~position the fatigue portals next to the wound portals
perform portal[lblfatigue].alignrel[ltor,wndheal,35]
perform portal[fatigue].alignrel[ltor,lblfatigue,3]
portal[fatigue].width = 30

~position the sustain and heal portals next to the fatigue
perform portal[ftgsustain].alignrel[ltor,fatigue,6]
perform portal[ftgheal].alignrel[ltor,ftgsustain,6] 

Rename the "acHPSumm" Field

When we were overhauling everything above, we left the "acHPSumm" field in place and simply changed its behavior. However, that field is inappropriate for Savage Worlds, so we should change it now that everything is again stable. We'll change the id to "acDmgSumm". When we re-compile, three errors are reported, which we can chase down and correct quickly. The three errors occur in the files "form_static.dat", "form_dashboard.dat", and "form_taccon.dat". All we need is a quick search-and-replace to swap in the new unique id.

Applying Damage to Trait Rolls

The final task we need to perform with regards to damage is to properly apply the resulting penalties to all trait rolls. Before we can do that, we must accrue the net penalty from both wounds and fatigue into a single value. This is most easily done by adding a new calculated field to the "Actor" component. The new field simply tallies the various values into one value that can be used whenever needed to adjust the trait rolls. Below is an example of what this field should look like.

<field
  id="acNetPenal"
  name="Net Penalties"
  type="derived">
  <calculate phase="Traits" priority="5000">
    <before name="Derived trtFinal"/><![CDATA[
    @value = -1 * (field[acWounds].value + field[acFatigue].value)
    ]]></calculate>
  </field>

The critical detail of the above field is its timing. We need to have the net penalties tallied up prior to the final calculation of derived traits, as well as before the final display text is generated for attributes and skills (to include the trait roll adjustments). The earlier of the two occurs at a timing of Traits/6000, so we need to pick something earlier. We'll use a timing of Traits/5000 out of convenience. Once the net penalty is properly calculated at a suitable timing, we can apply the penalty to the various trait rolls. For derived traits, the penalty needs to be added directly to the final calculated value. This is performed in the lone Eval script for the "Derived" component. By revising the script to simply add the penalty, we get a script that looks like the one below.

<eval index="1" phase="Traits" priority="6000" name="Derived trtFinal"><![CDATA[
  field[trtFinal].value = field[trtBonus].value + field[trtInPlay].value + herofield[acNetPenal].value
  ]]></eval>

For attributes and skills, the penalty is applied to the actual trait "roll" instead of the trait itself. The trait roll is determined as part of the process for synthesizing the "trtDisplay" field in an Eval script for the "Trait" component. When the bonus is tallied from the "trtRoll" and "trtNoStack" fields, the penalty should also be factored in, resulting in the appropriate net roll adjustment being calculated, as shown below.

<eval index="4" phase="Render" priority="5000" name="Calc trtDisplay">
  <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

  ~bound our final value including in-play adjustments
  var final as number
  final = field[trtFinal].value
  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
  var display as string
  dietype = final * 2
  display = "d" & dietype

  ~if there are any bonuses or penalties on the roll, append those the final result
  var bonus as number
  bonus = field[trtRoll].value + field[trtNoStack].value + herofield[acNetPenal].value
  if (bonus <> 0) then
    display &= signed(bonus)
    endif

  ~put the final result into the proper field
  field[trtDisplay].text = display
  ]]></eval>