Difference between revisions of "Show the Derivation of Values (Savage)"

From HLKitWiki
Jump to: navigation, search
(Explain How Derived Traits Are Calculated)
(Showing Derivation for Character Creation Resources)
 
(13 intermediate revisions by the same user not shown)
Line 3: Line 3:
 
===Overview===
 
===Overview===
  
There are a number of refinements that we can make to the interface, and this is probably a good time to do it. We've got all the fundamental mechanics in place now, so we might as well start pulling everything together more solidly.
+
Within Savage Worlds, many traits are influenced by a variety of factors. When the user sees one of these traits on the screen, there will be times when the number just doesn't look right. Usually, this will be due to some effect being included that the user has overlooked but the data files are properly tracking. At times like this, it would be incredibly helpful to inform the user how the final trait value was actually calculated for the character, presenting each of the contributing influences so the user can see what he's overlooked. We'll take the time to add this information in a few key spots.
  
===Using Bitmaps for Dice===
+
===Built-In History Tracking===
  
The contents of the attribute and skill incrementers currently show the die-type and any adjustment using the format "d6+2". It would be ideal if we actually showed a bitmap for each die-type, since it would tailor things much more closely to how Savage Worlds works. Fortunately, doing this is quite easy.
+
The first thing we need to do is provide a place to accrue the derivation details for each trait. Your first thought is probably to define a new text-based field where this information can be tracked, and that approach would definitely work. However, the Kit provides built-in handling for just this type of situation.
  
The Skeleton data files include an assortment of bitmaps for the various die-types. There are bitmaps for use on the screen and within character sheet output. All of these bitmaps are automatically copied into place for your use when you create a new game system. All you need to do is reference them.
+
Fields that are value-based and derived can leverage history tracking that is wholly managed by HL. The tracking can take three different forms. The first is "best" tracking, which only tracks the largest adjustment applied to a given field and is ideally suited for situations where adjustments do not stack. The second is "stack" tracking, where each individual adjustment is tracked. The third is "changes" and is identical to "stack", except that it automatically ignores any adjustments that have no net effect (e.g. adding zero). We'll want to use the second form and present a report to the user that shows all of the adjustments that were applied.
  
We've already centralized all of the handling for what gets displayed into a single place. The field "trtDisplay" contains the text to be output, and the procedure "FinalRoll" handles synthesizing this text. So all we need to do is modify the "FinalRoll" procedure to incorporate the bitmaps instead of using simple text.
+
We can enable history tracking very easily by adding a new attribute to the field definition. Since all of the adjustments we want to track are applied to the "trtBonus" field of Traits, we'll add history tracking to that field. The modified field definition should look similar to the one shown below.
  
Open the file "procedures.dat" and locate the "FinalRoll" procedure. Within this procedure, there is a line that generates the die-type for display by combining the letter "d" with the numeric value. We can easily change this to use the appropriate bitmap instead.
+
<pre>
 +
<field
 +
  id="trtBonus"
 +
  name="Bonus Value"
 +
  type="derived"
 +
  history="stack">
 +
  </field>
 +
</pre>
  
You can insert the bitmap into the text via the use of encoded text. The syntax for inserting a bitmap via encoded text is "{bmp filename}", where ''filename'' is the base name of the file to be inserted. Since the die-type bitmaps intended for use on screen are named in the form "d6_screen.bmp", the corresponding encoded text for a d6 should be "{bmp d6_screen}". This means that we can change one line of script code and get the die-type appearing as a bitmap. The new line of code should be the following.
+
===Tracking of Adjustments===
 +
 
 +
At this point, we could reload the data files and everything would continue to work as before. Although history tracking has been enabled for the "trtBonus" field, we can choose when to track adjustments and when not to do so. The tracking mechanism is optional, allowing us to apply adjustments that aren't tracked. This is important, because the same field may need to utilize history tracking for some things and not for others. This also makes it easy for us to integrate history tracking in a step-wise process instead of requiring us to do it all at once.
 +
 
 +
In order to track an adjustment within the history for a field, we need to utilize the "modify" target reference on the field. This target reference must be given an operator (e.g. "+" or "-"), a value, and a string that describes what the adjustment represents. Adding 2 to the "trtBonus" field for the "trCharisma" trait would look similar to the line of code below.
  
 
<pre>
 
<pre>
finaltext = "{bmp d" & dietype & "_screen}"
+
perform hero.child[trCharisma].field[trtBonus].modify[+,2,"The Reason"]
 
</pre>
 
</pre>
  
After a little bit of testing, though, the results aren't quite optimal, and we should probably refine the handling a bit. For example, if there is an adjustment made to the final roll (e.g. "d6+2"), the "+2" is a bit too small now. So we'll start by increasing the font size.
+
Since we're going to be using this basic statement in lots of places, we might as well make it easy on ourselves. So we'll define a new script macro for this field that works similarly to the "traitbonus" script macro that we already use whenever modifying the value of a trait. Open the file "definition.def" and locate the various script macros. Now define a new macro that takes four parameters and resolves to the "modify" usage shown above. The new macro should look like the following.
 +
 
 +
<pre>
 +
<scriptmacro
 +
  name="traitadjust"
 +
  param1="trait"
 +
  param2="oper"
 +
  param3="value"
 +
  param4="text"
 +
  result="hero.child[#trait].field[trtBonus].modify[#oper,#value,#text]"/>
 +
</pre>
  
The incrementers for showing die-types all use the "incrDie" style, so we'll need to modify it. Open up the file "styles_ui.aug" and locate the style. It currently uses the "fntincrsim" font resource, which is also used by another incrementer, so we can't simply change it. Instead, we need to define a new font resource, which we'll call "fntincrdie". We can define the new resource as part of the style, and we'll pick a size like 54 as a good balance that hopefully won't overshadow the die-type bitmap. This yields a revised style definition like the one below.
+
Since a macro is simply text that gets swapped in during compilation, we could also include the "perform " statement in the macro. If we do not include it, using the macro will look like the first example below. If we do include it, then our code will look like second example below. You are free to choose what works best for you, but we'll use the former approach as we continue this topic.
  
 
<pre>
 
<pre>
<style
+
perform #traitadjust[trCharisma,2,"The Reason"]
  id="incrDie">
+
  <style_incrementer
+
    textcolor="f0f0f0"
+
    font="fntincrdie"
+
    editable="no"
+
    textleft="13" texttop="0" textwidth="44" textheight="20"
+
    fullwidth="70" fullheight="20"
+
    plusup="incplusup" plusdown="incplusdn" plusoff="incplusof"
+
    plusx="59" plusy="0"
+
    minusup="incminusup" minusdown="incminusdn" minusoff="incminusof"
+
    minusx="0" minusy="0">
+
    </style_incrementer>
+
  <resource
+
    id="fntincrdie">
+
    <font
+
      face="Arial"
+
      size="54">
+
      </font>
+
    </resource>
+
  </style>
+
 
</pre>
 
</pre>
  
If we add the Ace edge and the Boating skill, we can see what this looks like. Unfortunately, this new size is a little too prominent compared to the die-type bitmap, so we need to make it a little less white. This is achieved by modifying the "textcolor" attribute within the style and dialing down the brightness a little bit. We'll pick a value of "c0c0c0" as a good compromise.
+
<pre>
 +
#traitadjust[trCharisma,2,"The Reason"]
 +
</pre>
  
This still doesn't look quite right. The "+" looks good, but the numeric adjustment looks a little too frail. We can change the font resource to be bold, but that results in the "+" looking blocky and ugly. What we need is for the "+" to be non-bold and the numeric value to bold. We can accomplish this by revising the procedure a little bit to carve up the bonus into two pieces. The net result is a revised procedure script that looks like below.
+
===Revise Adjustments for Derived Traits===
 +
 
 +
We now need to put the adjustment tracking to use within the data files. This entails identifying the places where we need to switch to the new mechanism and converting them appropriately. We'll start by focusing on the four derived traits for Savage Worlds, since these are the most heavily modified via different effects.
 +
 
 +
The first thing we need to do is setup the basic thing definitions for these traits to properly use the history tracking. Open the file "thing_traits.str" and locate the derived traits. We'll now go through them, one at a time.
 +
 
 +
The first trait is "trPace". It properly sets up the initial field value of 6, and the Eval script bounds the "trtFinal" field, so there is nothing we need to do here.
 +
 
 +
The next trait is "trParry", for which the Eval script includes both the starting value of 2 and adds half the Fighting skill. We need to change this so that we define the initial field value to be 2 via a "fieldval" element. Then we need to change the Eval script to utilize the macro to apply to adjustment for half the Fighting skill. We can put the adjustment for the Fighting skill within an "if" statement if we wish, which will only show the adjustment if there is an actual skill, but it's also valid to always include the adjustment and have it report a value of zero. These two changes are shown below.
  
 
<pre>
 
<pre>
~declare variables that are used to communicate with our caller
+
<fieldval field="trtBonus" value="2"/>
var finaldie as number
+
var finalbonus as number
+
var finaltext as string
+
  
~bound our final die type appropriately
+
if (hero.childexists[skFighting] <> 0) then
var final as number
+
   perform field[trtBonus].modify[+,#traitfound[skFighting],"Half Fighting"]
final = finaldie
+
if (final < 2) then
+
  final = 2
+
elseif (final > 6) then
+
   final = 6
+
 
   endif
 
   endif
 +
</pre>
  
~convert the final value for the trait to the proper die type for display
+
Next up is the "trCharisma" trait, which doesn't require any changes. We could be complete and specify a "fieldval" element with an initial value of 0, but that's not truly necessary.
var dietype as number
+
dietype = final * 2
+
finaltext = "{bmp d" & dietype & "_screen}"
+
  
~if there are any bonuses or penalties on the roll, append those the final result
+
Lastly, we have the "trTough" trait. This trait is similar to the "trParry" trait in its behavior. We need to define the initial field value of 2 via the "fieldval" element. We also need to change the Eval script to apply the proper adjustment for the Vigor attribute. These two changes are shown below.
if (finalbonus <> 0) then
+
 
   var bonus as string
+
<pre>
   bonus = signed(finalbonus)
+
<fieldval field="trtBonus" value="2"/>
   finaltext &= left(bonus,1) & "{b}" & right(bonus,1)
+
 
 +
perform field[trtBonus].modify[+,#trait[attrVig],"Half Vigor"]
 +
</pre>
 +
 
 +
We can now go through all the places in the data files where we modify the bonus for these four traits, converting each to apply the tracked adjustment. For example, within the "thing_edges.dat" file, the "Berserk" edge applies two separate adjustments to these traits. We need to change the penalty on the "trParry" trait to use the new adjustment macro and do the same for the bonus to the "trTough" trait. We can attribute both to the "Berserk" edge, yielding a new script that looks similar to the one below.
 +
 
 +
<pre>
 +
if (field[abilActive].value = 0) then
 +
   perform #traitadjust[trParry,-,2,"Berserk"]
 +
   #traitroll[skFighting] += 2
 +
   perform #traitadjust[trTough,+,2,"Berserk"]
 
   endif
 
   endif
 
</pre>
 
</pre>
  
We now have something that will work smoothly, shows the die-type as a bitmap, and looks reasonable.
+
Go through the data files and locate all instances where the derived traits are being modified. Every instance should be modified to use the new "#traitadjust" macro that logs what adjustment was applied. Be sure to also track the effects of equipped weapons and shields on the Parry trait, as well as the effects of equipped armor on the Toughness trait.
  
===Explain How Derived Traits Are Calculated===
+
===Report the History for Derived Traits===
  
The derived traits are shown on the Basics tab. However, these traits are influenced by a variety of factors. When the user sees one of these traits, it would be incredibly helpful to inform the user how the final value was actually derived for the character. So we'll take the time to add this information now.
+
The derivation history should now be getting properly synthesized for each derived trait, so all we need to do now is make that history accessible to the user. Open the file "tab_basics.dat" and locate the "baTrtPick" template that is used to display the various derived traits. Within the template, the "details" portal shows the final value for the trait, and a MouseInfo script is defined that currently just outputs "???".  
  
The first thing we need to do is provide a place to accrue the derivation details for each derived trait. We can do this by defining a new field for the "Derived" component, which can be found in the file "traits.str". We'll name the new field "trtDerived" and make it a text field with adequate storage for multiple adjustments. This yields a field definition that looks like the one below.
+
We can change the script to report the adjustment history for the "trtBonus" field. This is achieved by using the "history" target reference on the field, specifying a suitable separator between each entry. We also want to be sure that the starting value is shown for each derived trait. The derivation details will now be available to the user by simply moving the mouse over the value. The revised portal should look like the one shown below.
 +
 
 +
<pre>
 +
<portal
 +
  id="details"
 +
  style="lblLarge">
 +
  <label>
 +
    <labeltext><![CDATA[
 +
      @text = field[trtDisplay].text
 +
      ]]></labeltext>
 +
    </label>
 +
  <mouseinfo mousepos="middle+above"><![CDATA[
 +
    @text = field[trtBonus].history[", ",start]
 +
    ]]></mouseinfo>
 +
  </portal>
 +
</pre>
 +
 
 +
===Showing Derivation for Roll Adjustments===
 +
 
 +
The derivation history for roll adjustments on attributes and skills would also be extremely useful. Seeing a "+1" or "-2" next to an attribute or skill is helpful, but it's vastly more helpful to be able to quickly see what is causing that adjustment. We can utilize the same history mechanisms outlined above for this purpose as well.
 +
 
 +
The calculation of the "trtNetRoll" field is based on three separate components. One of those is the impact of wounds and/or fatigue, so history tracking is not really applicable. However, the other two facets are the non-stacking adjustments due to professions and all the other stacking adjustments that can be accrued. Each of these is perfectly suited to history tracking.
 +
 
 +
The first step is to change the two fields to utilize history tracking. The "trtRoll" field allows all the changes to be stacked, so we can institute "stack" history tracking, just like we did for the "trtBonus" field of derived traits. The "trtNoStack" field, though, does not support stacking. As such, we can utilize "best" history tracking to automatically identify only the biggest adjustment. After putting these changes into place, we should end up with revised field definitions like below.
  
 
<pre>
 
<pre>
 
<field
 
<field
   id="trtDerived"
+
   id="trtRoll"
   name="Derivation"
+
   name="Bonus on Trait Rolls"
 
   type="derived"
 
   type="derived"
   maxlength="200">
+
   history="stack">
 +
  </field>
 +
 
 +
<field
 +
  id="trtNoStack"
 +
  name="Professional Trait Bonus"
 +
  type="derived"
 +
  history="best">
 
   </field>
 
   </field>
 
</pre>
 
</pre>
  
Since we're going to be modifying this field in a variety of places, we might as well make it easy on ourselves. So we'll define a new script macro for this field that works similarly to the "traitbonus" script macro that we already use whenever modifying the value of a derived trait. Open the file "definition.def" and locate the "traitbonus" script macro, then clone that macro. Edit the macro to have the name "traitderived" and operate on the "trtDerived" field, which is a text field. The new macro should look like the one shown below.
+
The next step is to setup appropriate macros to put the history mechanism to use for each field. We already have the "traitroll" and "traitprof" macros defined, so what we need to do is adapt them to use the history mechanism instead. This yields the revised macros shown below, which we'll then need to utilize differently throughout the data files.
  
 
<pre>
 
<pre>
 
<scriptmacro
 
<scriptmacro
   name="traitderived"
+
   name="traitroll"
 +
  param1="trait"
 +
  param2="oper"
 +
  param3="value"
 +
  param4="text"
 +
  result="hero.childfound[#trait].field[trtRoll].modify[#oper,#value,#text]"/>
 +
 
 +
<scriptmacro
 +
  name="traitprof"
 
   param1="trait"
 
   param1="trait"
   result="hero.child[#trait].field[trtDerived].text"/>
+
  param2="oper"
 +
  param3="value"
 +
  param4="text"
 +
   result="hero.childfound[#trait].field[trtNoStack].modify[#oper,#value,#text]"/>
 
</pre>
 
</pre>
  
The next step is to initialize the field to something appropriate for each derived trait. This is easily accomplished by defining the field value within each thing. For example, we can set the initial derivation text for the Pace trait to something like "Base of 6" via the "fieldval" element, as shown below. This should be done for all derived traits within the file "thing_traits.dat".
+
Before we change all the field references to use the new macros, let's first look at how everything will be shown to the user. We need to display the derivation details in two separate places - for attributes on the Basics tab and for skills on the Skills tab. These involve two separate templates, but the logic we want will be the same for both. So we'll define a single procedure that can be used from both places and setup a MouseInfo script in each place to retrieve the information.
 +
 
 +
The procedure needs to show all three facets of the derived value. So we'll setup the procedure to list each facet on a separate line, with the appropriate details for each being displayed. If there is nothing to report for a particular facet, we need to display that appropriately. Since the procedure will only be used from within a MouseInfo script, we can tailor it to directly generate the text to be displayed.
 +
 
 +
The resulting procedure is shown below. Just below that, the MouseInfo script that should be added to the "adjust" portal within both templates is also presented. The MouseInfo script calls the procedure directly, relying on the procedure to put the proper information into the "@text" special symbol that will be displayed to the user.
  
 
<pre>
 
<pre>
<fieldval field="trtDerived" value="Base of 6"/>
+
<procedure id="TrtDerive" scripttype="mouseinfo"><![CDATA[
 +
  var info as string
 +
 
 +
  ~start with the roll adjustments that stack
 +
  info = field[trtRoll].history[", "]
 +
  if (empty(info) <> 0) then
 +
    info = chr(150)
 +
  else
 +
    info = signed(field[trtRoll].value) & ", " & info
 +
    endif
 +
  @text = "Stacking Adjustments: " & info & "{br}"
 +
 
 +
  ~append the non-stacking professional adjustments
 +
  info = field[trtNoStack].history
 +
  if (empty(info) <> 0) then
 +
    info = chr(150)
 +
  else
 +
    info &= " (" & signed(field[trtNoStack].value) & ")"
 +
    endif
 +
  @text &= "Professional Adjustments: " & info & "{br}"
 +
 
 +
  ~append any penalties due to wounds or fatigue
 +
  @text &= "Wound/Fatigue Penalties: " & signed(herofield[acNetPenal].value)
 +
  ]]></procedure>
 
</pre>
 
</pre>
  
Throughout the data files, there will be places where each derived field is modified in some way. In each of the locations, the derivation details must be logged at the same time the adjustment is applied. This is achieved by simply appending a suitable, brief description of the adjustment as text. For example, the "Berserk" edge applies a +2 bonus to the character's Toughness and a -2 penalty to the character's Parry. Once the derivation details are added, the new script should look like the one shown below.
+
<pre>
 +
<mouseinfo mousepos="middle+above"><![CDATA[
 +
  call TrtDerive
 +
  ]]></mouseinfo>
 +
</pre>
 +
 
 +
We can now go through the data files and convert all uses of the "traitroll" and "traitprof" macros over to the new versions. The examples below show the properly revised Eval scripts for the "Ace" and "Strong Willed" edges.  
  
 
<pre>
 
<pre>
if (field[abilActive].value = 0) then
+
perform #traitprof[skBoating,+,2,"Ace"]
  #traitbonus[trParry] -= 2
+
perform #traitprof[skDriving,+,2,"Ace"]
  #traitderived[trParry] &= ", Berserk -2"
+
perform #traitprof[skPiloting,+,2,"Ace"]
  #traitroll[skFighting] += 2
+
  #traitbonus[trTough] += 2
+
  #traitderived[trTough] &= ", Berserk +2"
+
  endif
+
 
</pre>
 
</pre>
  
Go through the data files and locate all instances where the derived traits are modified. Every instance should be accompanied by a corresponding "#traitderived" macro that logs what adjustment was applied. Be sure to also track the effects of equipped weapons and shields on the Parry trait, as well as the effects of equipped armor on the Toughness trait.
+
<pre>
 +
perform #traitroll[skIntimid,+,2,"Strong Willed"]
 +
perform #traitroll[skTaunt,+,2,"Strong Willed"]
 +
</pre>
  
For two of the derived traits, the final value depends on another trait that can also be modified by other effects. So we need to wait until the end of the evaluation cycle before applying the adjustment for the trait. This requires we define a new Eval script for each of the traits that properly appends the final adjustment description to the derivation field. The two traits that require this are the Parry and Toughness traits, which are based upon the Fighting skill and Vigor attribute, respectively. Putting it all together, the two Eval scripts below should handle things appropriately for these two traits.
+
Don't forget that you'll also need to revise a few places where the "trtRoll" field is being used without the macro. These include the application of penalties for exceeding the load limit and the assignment of in-play adjustments. Once everything is switched over, you should be able to see all of the adjustment details by simply moving the mouse over the adjustment value within HL.
 +
 
 +
===Showing Derivation for Character Creation Resources===
 +
 
 +
We can use the exact same process to accrue how the user has spent the initial character creation resources and display them on the Basics tab. All character creation resources are managed via the "Resource" component, with the "resSpent" field tracking the actual resources that are consumed. So we can institute history tracking for the "resSpent" field and log the selections made by the user for subsequent reporting.
 +
 
 +
We start by modifying the field to enable history tracking, as shown below.
  
 
<pre>
 
<pre>
<eval index="2" phase="Render" priority="10000"><![CDATA[
+
<field
   if (hero.childexists[skFighting] <> 0) then
+
  id="resSpent"
    field[trtDerived].text &= ", Half Fighting +" & #trait[skFighting]
+
  name="Quantity Spent"
    endif
+
  type="derived"
   ]]></eval>
+
   history="stack">
 +
   </field>
 +
</pre>
  
<eval index="2" phase="Render" priority="10000"><![CDATA[
+
After that, we can revise the "resspent" macro to apply tracked modifications.
   field[trtDerived].text &= ", Half Vigor +" & #trait[attrVig]
+
 
 +
<pre>
 +
<scriptmacro
 +
  name="resspent"
 +
  param1="resource"
 +
  param2="oper"
 +
  param3="value"
 +
  param4="text"
 +
  result="hero.child[#resource].field[resSpent].modify[#oper,#value,#text]"/>
 +
</pre>
 +
 
 +
We can now go through all instances where the "resspent" macro is used and convert them appropriately. In general, we'll want to specify the name of the edge, hindrance, or whatever as the source of the adjustment. For example, arcane powers each consume one slot via an Eval script, and that script gets revised to the version shown below.
 +
 
 +
<pre>
 +
<eval index="2" phase="Setup" priority="5000"><![CDATA[
 +
   perform #resspent[resPowers,+,1,field[name].text]
 
   ]]></eval>
 
   ]]></eval>
 
</pre>
 
</pre>
  
The derivation explanation should now be getting properly synthesized for each trait, so all we need to do now is make it accessible to the user. Open the file "tab_basics.dat" and locate the "baTrtPick" template that is used to display the various derived traits. Within the template, the "details" portal shows the final value for the trait, and a MouseInfo script is defined that currently just outputs "???". Change the script to return the contents of the "trtDerived" field and the derivation details will be available to the user by simply moving the mouse over the value. The revised portal should look like the one shown below.
+
Similarly, the accumulation of attribute points should be revised to look like the following.
  
 
<pre>
 
<pre>
<portal
+
perform #resspent[resAttrib,+,field[trtUser].value - 2,field[name].text]
  id="details"
+
  style="lblLarge">
+
  <label>
+
    <labeltext><![CDATA[
+
      @text = field[trtDisplay].text
+
      ]]></labeltext>
+
    </label>
+
  <mouseinfo mousepos="middle+above"><![CDATA[
+
    @text = field[trtDerived].text
+
    ]]></mouseinfo>
+
  </portal>
+
 
</pre>
 
</pre>
 +
 +
These are all pretty simple to revise. The one that requires a little bit of thought is the handling of advances. For advances, we need to actually synthesize a truly useful name. If we simply use the type of the advance, then we'll end up with a bunch of vague references (e.g. "New Skill", "New Attribute", etc.). What we really need is both the type of advance and the details of the advance. This entails synthesizing an appropriate name, which yields a script that looks like the one below.
 +
 +
<pre>
 +
var name as string
 +
name = field[advAction].text & ": " & gizmo.firstchild["Advance.Gizmo"].field[name].text
 +
perform #resspent[resAdvance,+,field[advCost].value,name]
 +
</pre>
 +
 +
There is one place where the "#resspent" macro is being used to retrieve the value and not modify it. In this instance, which deals with the load limit handling, we need to access the appropriate field value directly. The code below shows the old line and the new line that replaces it.
 +
 +
<pre>
 +
spent = #resspent[resEncumb]
 +
 +
spent = hero.child[resEncumb].field[resSpent].value
 +
</pre>
 +
 +
We must also identify any other places where the "resSpent" field is used directly. There happens to be one for handling the accrual of skill points, and it can be changed to use the new macro. The line of code that needs to be changes should look like below.
 +
 +
<pre>
 +
perform #resspent[resSkill,+,points,field[name].text]
 +
</pre>
 +
 +
The final step is to display the history information via a MouseInfo script. Such a script is already in place within the "details" portal in the "baResource" template on the Basics tab. So all we need to do is change the script to return the appropriate information or a suitable message if there is nothing to report. This amounts to the code below.
 +
 +
<pre>
 +
@text = field[resSpent].history[", "]
 +
if (empty(@text) <> 0) then
 +
  @text = "No Adjustments"
 +
  endif
 +
</pre>
 +
 +
If we reload the data files and construct a character, we can move the mouse over the various character creation resource numbers and see a report of how those resources have been spent. However, there is a minor problem. Every attribute is logging its consumption, including attributes that are at the minimum rating of "d4" and consuming zero attribute points. Listing these attributes seems rather silly, since they don't provide any useful information for the user. Fortunately, we can easily omit them. If we go back to the "resSpent" field and modify the history tracking behavior to "changes", we'll only get adjustments that actually change something, so these adjustments of zero will be automatically thrown out.
 +
 +
===Showing Derivation for Encumbrance and Load Limit===
 +
 +
As a side-effect of showing the derivation of character creation resources, we've gained the ability to display the derivation of encumbrance for the character. Both the tables on the Basics tab use the same template, which means both are now hooked up to show the history within the "resSpent" field. Since the accrual of encumbrance also uses the "#resspent" macro, we had to modify it during our conversion.
 +
 +
The net result is that we can add some gear to a character and see the derivation without any further work. Move the mouse over the Encumbrance value shown and you should see a report of the items that contribute to the encumbrance rating for the character.
 +
 +
The one drawback is that we also inherit this behavior for the Load Limit value. Since there is nothing spent for this resource, moving the mouse over the value shows a message "No Adjustments". That's not ideal, but it's not horrible either.
 +
 +
If we really wanted to show something better for the Load Limit, we could do a lot of special handling. Alternately, we could get creative. If we setup a single adjustment using the "#resspent" macro for the resource, the text we use for the adjustment could be the text we want to show to the user via the MouseInfo script. Since we don't actually use the final value of the resource for anything, nor do we show it anywhere, spending the resource to add an entry to the history would work nicely.
 +
 +
If you think of something useful to report for the load limit, feel free to give this tactic a try.

Latest revision as of 00:07, 11 February 2009

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

Within Savage Worlds, many traits are influenced by a variety of factors. When the user sees one of these traits on the screen, there will be times when the number just doesn't look right. Usually, this will be due to some effect being included that the user has overlooked but the data files are properly tracking. At times like this, it would be incredibly helpful to inform the user how the final trait value was actually calculated for the character, presenting each of the contributing influences so the user can see what he's overlooked. We'll take the time to add this information in a few key spots.

Built-In History Tracking

The first thing we need to do is provide a place to accrue the derivation details for each trait. Your first thought is probably to define a new text-based field where this information can be tracked, and that approach would definitely work. However, the Kit provides built-in handling for just this type of situation.

Fields that are value-based and derived can leverage history tracking that is wholly managed by HL. The tracking can take three different forms. The first is "best" tracking, which only tracks the largest adjustment applied to a given field and is ideally suited for situations where adjustments do not stack. The second is "stack" tracking, where each individual adjustment is tracked. The third is "changes" and is identical to "stack", except that it automatically ignores any adjustments that have no net effect (e.g. adding zero). We'll want to use the second form and present a report to the user that shows all of the adjustments that were applied.

We can enable history tracking very easily by adding a new attribute to the field definition. Since all of the adjustments we want to track are applied to the "trtBonus" field of Traits, we'll add history tracking to that field. The modified field definition should look similar to the one shown below.

<field
  id="trtBonus"
  name="Bonus Value"
  type="derived"
  history="stack">
  </field>

Tracking of Adjustments

At this point, we could reload the data files and everything would continue to work as before. Although history tracking has been enabled for the "trtBonus" field, we can choose when to track adjustments and when not to do so. The tracking mechanism is optional, allowing us to apply adjustments that aren't tracked. This is important, because the same field may need to utilize history tracking for some things and not for others. This also makes it easy for us to integrate history tracking in a step-wise process instead of requiring us to do it all at once.

In order to track an adjustment within the history for a field, we need to utilize the "modify" target reference on the field. This target reference must be given an operator (e.g. "+" or "-"), a value, and a string that describes what the adjustment represents. Adding 2 to the "trtBonus" field for the "trCharisma" trait would look similar to the line of code below.

perform hero.child[trCharisma].field[trtBonus].modify[+,2,"The Reason"]

Since we're going to be using this basic statement in lots of places, we might as well make it easy on ourselves. So we'll define a new script macro for this field that works similarly to the "traitbonus" script macro that we already use whenever modifying the value of a trait. Open the file "definition.def" and locate the various script macros. Now define a new macro that takes four parameters and resolves to the "modify" usage shown above. The new macro should look like the following.

<scriptmacro
  name="traitadjust"
  param1="trait"
  param2="oper"
  param3="value"
  param4="text"
  result="hero.child[#trait].field[trtBonus].modify[#oper,#value,#text]"/>

Since a macro is simply text that gets swapped in during compilation, we could also include the "perform " statement in the macro. If we do not include it, using the macro will look like the first example below. If we do include it, then our code will look like second example below. You are free to choose what works best for you, but we'll use the former approach as we continue this topic.

perform #traitadjust[trCharisma,2,"The Reason"]
#traitadjust[trCharisma,2,"The Reason"]

Revise Adjustments for Derived Traits

We now need to put the adjustment tracking to use within the data files. This entails identifying the places where we need to switch to the new mechanism and converting them appropriately. We'll start by focusing on the four derived traits for Savage Worlds, since these are the most heavily modified via different effects.

The first thing we need to do is setup the basic thing definitions for these traits to properly use the history tracking. Open the file "thing_traits.str" and locate the derived traits. We'll now go through them, one at a time.

The first trait is "trPace". It properly sets up the initial field value of 6, and the Eval script bounds the "trtFinal" field, so there is nothing we need to do here.

The next trait is "trParry", for which the Eval script includes both the starting value of 2 and adds half the Fighting skill. We need to change this so that we define the initial field value to be 2 via a "fieldval" element. Then we need to change the Eval script to utilize the macro to apply to adjustment for half the Fighting skill. We can put the adjustment for the Fighting skill within an "if" statement if we wish, which will only show the adjustment if there is an actual skill, but it's also valid to always include the adjustment and have it report a value of zero. These two changes are shown below.

<fieldval field="trtBonus" value="2"/>

if (hero.childexists[skFighting] <> 0) then
  perform field[trtBonus].modify[+,#traitfound[skFighting],"Half Fighting"]
  endif

Next up is the "trCharisma" trait, which doesn't require any changes. We could be complete and specify a "fieldval" element with an initial value of 0, but that's not truly necessary.

Lastly, we have the "trTough" trait. This trait is similar to the "trParry" trait in its behavior. We need to define the initial field value of 2 via the "fieldval" element. We also need to change the Eval script to apply the proper adjustment for the Vigor attribute. These two changes are shown below.

<fieldval field="trtBonus" value="2"/>

perform field[trtBonus].modify[+,#trait[attrVig],"Half Vigor"]

We can now go through all the places in the data files where we modify the bonus for these four traits, converting each to apply the tracked adjustment. For example, within the "thing_edges.dat" file, the "Berserk" edge applies two separate adjustments to these traits. We need to change the penalty on the "trParry" trait to use the new adjustment macro and do the same for the bonus to the "trTough" trait. We can attribute both to the "Berserk" edge, yielding a new script that looks similar to the one below.

if (field[abilActive].value = 0) then
  perform #traitadjust[trParry,-,2,"Berserk"]
  #traitroll[skFighting] += 2
  perform #traitadjust[trTough,+,2,"Berserk"]
  endif

Go through the data files and locate all instances where the derived traits are being modified. Every instance should be modified to use the new "#traitadjust" macro that logs what adjustment was applied. Be sure to also track the effects of equipped weapons and shields on the Parry trait, as well as the effects of equipped armor on the Toughness trait.

Report the History for Derived Traits

The derivation history should now be getting properly synthesized for each derived trait, so all we need to do now is make that history accessible to the user. Open the file "tab_basics.dat" and locate the "baTrtPick" template that is used to display the various derived traits. Within the template, the "details" portal shows the final value for the trait, and a MouseInfo script is defined that currently just outputs "???".

We can change the script to report the adjustment history for the "trtBonus" field. This is achieved by using the "history" target reference on the field, specifying a suitable separator between each entry. We also want to be sure that the starting value is shown for each derived trait. The derivation details will now be available to the user by simply moving the mouse over the value. The revised portal should look like the one shown below.

<portal
  id="details"
  style="lblLarge">
  <label>
    <labeltext><![CDATA[
      @text = field[trtDisplay].text
      ]]></labeltext>
    </label>
  <mouseinfo mousepos="middle+above"><![CDATA[
    @text = field[trtBonus].history[", ",start]
    ]]></mouseinfo>
  </portal>

Showing Derivation for Roll Adjustments

The derivation history for roll adjustments on attributes and skills would also be extremely useful. Seeing a "+1" or "-2" next to an attribute or skill is helpful, but it's vastly more helpful to be able to quickly see what is causing that adjustment. We can utilize the same history mechanisms outlined above for this purpose as well.

The calculation of the "trtNetRoll" field is based on three separate components. One of those is the impact of wounds and/or fatigue, so history tracking is not really applicable. However, the other two facets are the non-stacking adjustments due to professions and all the other stacking adjustments that can be accrued. Each of these is perfectly suited to history tracking.

The first step is to change the two fields to utilize history tracking. The "trtRoll" field allows all the changes to be stacked, so we can institute "stack" history tracking, just like we did for the "trtBonus" field of derived traits. The "trtNoStack" field, though, does not support stacking. As such, we can utilize "best" history tracking to automatically identify only the biggest adjustment. After putting these changes into place, we should end up with revised field definitions like below.

<field
  id="trtRoll"
  name="Bonus on Trait Rolls"
  type="derived"
  history="stack">
  </field>

<field
  id="trtNoStack"
  name="Professional Trait Bonus"
  type="derived"
  history="best">
  </field>

The next step is to setup appropriate macros to put the history mechanism to use for each field. We already have the "traitroll" and "traitprof" macros defined, so what we need to do is adapt them to use the history mechanism instead. This yields the revised macros shown below, which we'll then need to utilize differently throughout the data files.

<scriptmacro
  name="traitroll"
  param1="trait"
  param2="oper"
  param3="value"
  param4="text"
  result="hero.childfound[#trait].field[trtRoll].modify[#oper,#value,#text]"/>

<scriptmacro
  name="traitprof"
  param1="trait"
  param2="oper"
  param3="value"
  param4="text"
  result="hero.childfound[#trait].field[trtNoStack].modify[#oper,#value,#text]"/>

Before we change all the field references to use the new macros, let's first look at how everything will be shown to the user. We need to display the derivation details in two separate places - for attributes on the Basics tab and for skills on the Skills tab. These involve two separate templates, but the logic we want will be the same for both. So we'll define a single procedure that can be used from both places and setup a MouseInfo script in each place to retrieve the information.

The procedure needs to show all three facets of the derived value. So we'll setup the procedure to list each facet on a separate line, with the appropriate details for each being displayed. If there is nothing to report for a particular facet, we need to display that appropriately. Since the procedure will only be used from within a MouseInfo script, we can tailor it to directly generate the text to be displayed.

The resulting procedure is shown below. Just below that, the MouseInfo script that should be added to the "adjust" portal within both templates is also presented. The MouseInfo script calls the procedure directly, relying on the procedure to put the proper information into the "@text" special symbol that will be displayed to the user.

<procedure id="TrtDerive" scripttype="mouseinfo"><![CDATA[
  var info as string

  ~start with the roll adjustments that stack
  info = field[trtRoll].history[", "]
  if (empty(info) <> 0) then
    info = chr(150)
  else
    info = signed(field[trtRoll].value) & ", " & info
    endif
  @text = "Stacking Adjustments: " & info & "{br}"

  ~append the non-stacking professional adjustments
  info = field[trtNoStack].history
  if (empty(info) <> 0) then
    info = chr(150)
  else
    info &= " (" & signed(field[trtNoStack].value) & ")"
    endif
  @text &= "Professional Adjustments: " & info & "{br}"

  ~append any penalties due to wounds or fatigue
  @text &= "Wound/Fatigue Penalties: " & signed(herofield[acNetPenal].value)
  ]]></procedure>
<mouseinfo mousepos="middle+above"><![CDATA[
  call TrtDerive
  ]]></mouseinfo>

We can now go through the data files and convert all uses of the "traitroll" and "traitprof" macros over to the new versions. The examples below show the properly revised Eval scripts for the "Ace" and "Strong Willed" edges.

perform #traitprof[skBoating,+,2,"Ace"]
perform #traitprof[skDriving,+,2,"Ace"]
perform #traitprof[skPiloting,+,2,"Ace"]
perform #traitroll[skIntimid,+,2,"Strong Willed"]
perform #traitroll[skTaunt,+,2,"Strong Willed"]

Don't forget that you'll also need to revise a few places where the "trtRoll" field is being used without the macro. These include the application of penalties for exceeding the load limit and the assignment of in-play adjustments. Once everything is switched over, you should be able to see all of the adjustment details by simply moving the mouse over the adjustment value within HL.

Showing Derivation for Character Creation Resources

We can use the exact same process to accrue how the user has spent the initial character creation resources and display them on the Basics tab. All character creation resources are managed via the "Resource" component, with the "resSpent" field tracking the actual resources that are consumed. So we can institute history tracking for the "resSpent" field and log the selections made by the user for subsequent reporting.

We start by modifying the field to enable history tracking, as shown below.

<field
  id="resSpent"
  name="Quantity Spent"
  type="derived"
  history="stack">
  </field>

After that, we can revise the "resspent" macro to apply tracked modifications.

<scriptmacro
  name="resspent"
  param1="resource"
  param2="oper"
  param3="value"
  param4="text"
  result="hero.child[#resource].field[resSpent].modify[#oper,#value,#text]"/>

We can now go through all instances where the "resspent" macro is used and convert them appropriately. In general, we'll want to specify the name of the edge, hindrance, or whatever as the source of the adjustment. For example, arcane powers each consume one slot via an Eval script, and that script gets revised to the version shown below.

<eval index="2" phase="Setup" priority="5000"><![CDATA[
  perform #resspent[resPowers,+,1,field[name].text]
  ]]></eval>

Similarly, the accumulation of attribute points should be revised to look like the following.

perform #resspent[resAttrib,+,field[trtUser].value - 2,field[name].text]

These are all pretty simple to revise. The one that requires a little bit of thought is the handling of advances. For advances, we need to actually synthesize a truly useful name. If we simply use the type of the advance, then we'll end up with a bunch of vague references (e.g. "New Skill", "New Attribute", etc.). What we really need is both the type of advance and the details of the advance. This entails synthesizing an appropriate name, which yields a script that looks like the one below.

var name as string
name = field[advAction].text & ": " & gizmo.firstchild["Advance.Gizmo"].field[name].text
perform #resspent[resAdvance,+,field[advCost].value,name]

There is one place where the "#resspent" macro is being used to retrieve the value and not modify it. In this instance, which deals with the load limit handling, we need to access the appropriate field value directly. The code below shows the old line and the new line that replaces it.

spent = #resspent[resEncumb]

spent = hero.child[resEncumb].field[resSpent].value

We must also identify any other places where the "resSpent" field is used directly. There happens to be one for handling the accrual of skill points, and it can be changed to use the new macro. The line of code that needs to be changes should look like below.

perform #resspent[resSkill,+,points,field[name].text]

The final step is to display the history information via a MouseInfo script. Such a script is already in place within the "details" portal in the "baResource" template on the Basics tab. So all we need to do is change the script to return the appropriate information or a suitable message if there is nothing to report. This amounts to the code below.

@text = field[resSpent].history[", "]
if (empty(@text) <> 0) then
  @text = "No Adjustments"
  endif

If we reload the data files and construct a character, we can move the mouse over the various character creation resource numbers and see a report of how those resources have been spent. However, there is a minor problem. Every attribute is logging its consumption, including attributes that are at the minimum rating of "d4" and consuming zero attribute points. Listing these attributes seems rather silly, since they don't provide any useful information for the user. Fortunately, we can easily omit them. If we go back to the "resSpent" field and modify the history tracking behavior to "changes", we'll only get adjustments that actually change something, so these adjustments of zero will be automatically thrown out.

Showing Derivation for Encumbrance and Load Limit

As a side-effect of showing the derivation of character creation resources, we've gained the ability to display the derivation of encumbrance for the character. Both the tables on the Basics tab use the same template, which means both are now hooked up to show the history within the "resSpent" field. Since the accrual of encumbrance also uses the "#resspent" macro, we had to modify it during our conversion.

The net result is that we can add some gear to a character and see the derivation without any further work. Move the mouse over the Encumbrance value shown and you should see a report of the items that contribute to the encumbrance rating for the character.

The one drawback is that we also inherit this behavior for the Load Limit value. Since there is nothing spent for this resource, moving the mouse over the value shows a message "No Adjustments". That's not ideal, but it's not horrible either.

If we really wanted to show something better for the Load Limit, we could do a lot of special handling. Alternately, we could get creative. If we setup a single adjustment using the "#resspent" macro for the resource, the text we use for the adjustment could be the text we want to show to the user via the MouseInfo script. Since we don't actually use the final value of the resource for anything, nor do we show it anywhere, spending the resource to add an entry to the history would work nicely.

If you think of something useful to report for the load limit, feel free to give this tactic a try.