Difference between revisions of "Creature Refinement (Savage)"

From HLKitWiki
Jump to: navigation, search
(Output Revisions)
(Cleanup the Interface)
 
(22 intermediate revisions by the same user not shown)
Line 63: Line 63:
 
   <!-- Append the immunity to the name -->
 
   <!-- Append the immunity to the name -->
 
   <eval index="1" phase="Traits" priority="10000"><![CDATA[
 
   <eval index="1" phase="Traits" priority="10000"><![CDATA[
     field[livename].text &= " " & field[abilText].text
+
     field[livename].text = field[thingname].text & ": " & field[abilText].text
 
     ]]></eval>
 
     ]]></eval>
 
   </thing>
 
   </thing>
Line 101: Line 101:
 
   style="tblNormal">
 
   style="tblNormal">
 
   <table_fixed
 
   <table_fixed
     component="Ability"
+
     component="RaceAbil"
 
     showtemplate="SimpleItem">
 
     showtemplate="SimpleItem">
    <list>component.RaceAbil</list>
 
 
     <headertitle><![CDATA[
 
     <headertitle><![CDATA[
 
       @text = "Monstrous Abilities"
 
       @text = "Monstrous Abilities"
Line 156: Line 155:
 
   isunique="yes"
 
   isunique="yes"
 
   description="Description goes here">
 
   description="Description goes here">
   <fieldval field="crePace" value="-3"/>
+
   <fieldval field="crePace" value="3"/>
 
   <tag group="AgiDie" tag="2"/>
 
   <tag group="AgiDie" tag="2"/>
 
   <tag group="SmaDie" tag="2"/>
 
   <tag group="SmaDie" tag="2"/>
Line 181: Line 180:
 
     <assignval field="abilText" value="+d6"/>
 
     <assignval field="abilText" value="+d6"/>
 
     </bootstrap>
 
     </bootstrap>
   <bootstrap thing="abAquatic"/>
+
   <bootstrap thing="abAquatic">
 +
    <assignval field="abilValue" value="5"/>
 +
    </bootstrap>
 
   <bootstrap thing="abRollover"/>
 
   <bootstrap thing="abRollover"/>
  <!-- We have an aquatic pace of 5 -->
 
  <eval index="1" phase="PreTraits" priority="5000"><![CDATA[
 
    hero.child[trPace].field[trtSpecial].value = 5
 
    ]]></eval>
 
 
   </thing>
 
   </thing>
  
Line 207: Line 204:
 
   isunique="yes"
 
   isunique="yes"
 
   description="Description goes here">
 
   description="Description goes here">
   <fieldval field="crePace" value="2"/>
+
   <fieldval field="crePace" value="8"/>
 
   <tag group="AgiDie" tag="3"/>
 
   <tag group="AgiDie" tag="3"/>
 
   <tag group="SmaDie" tag="3"/>
 
   <tag group="SmaDie" tag="3"/>
Line 253: Line 250:
 
   isunique="yes"
 
   isunique="yes"
 
   description="Description goes here">
 
   description="Description goes here">
 +
  <fieldval field="crePace" value="6"/>
 
   <tag group="User" tag="Wildcard"/>
 
   <tag group="User" tag="Wildcard"/>
 
   <tag group="AgiDie" tag="3"/>
 
   <tag group="AgiDie" tag="3"/>
Line 282: Line 280:
 
   <bootstrap thing="abUndead"/>
 
   <bootstrap thing="abUndead"/>
 
   <bootstrap thing="abZombie"/>
 
   <bootstrap thing="abZombie"/>
   <bootstrap thing="armMagLich">
+
   <bootstrap thing="armMagLch">
 
     <autotag group="Equipment" tag="StartEquip"/>
 
     <autotag group="Equipment" tag="StartEquip"/>
 
     </bootstrap>
 
     </bootstrap>
Line 288: Line 286:
  
 
<thing
 
<thing
   id="armMagLich"
+
   id="armMagLch"
 
   name="Magical Armor +6"
 
   name="Magical Armor +6"
 
   compset="Armor"
 
   compset="Armor"
Line 330: Line 328:
 
   description="Description goes here">
 
   description="Description goes here">
 
   </thing>
 
   </thing>
 +
</pre>
 +
 +
===Showing Edges===
 +
 +
A number of creatures are assigned edges. Unfortunately, those edges aren't appearing on the new "Abilities" tab that we created. We need to show them appropriately so that the user clearly sees them. That requires us to expand the "Abilities" to include a second table for the edges. The good news is that we can simply re-use the existing table of edges within a second layout.
 +
 +
We'll replace the simplistic "abilities" layout with something that intelligently handles the two tables. We have to handle the case where both tables are too tall to fit within the available vertical space. Since edges will be generally uncommon for creatures, and their number will be few, we'll put them at the bottom, with creature abilities at the top. We'll reserve space for a few edges, then dedicate the rest of the height to abilities. If we don't need all the space for abilities, the edges can expand to fill whatever room remains. Putting all this together yields the revised layout shown below.
 +
 +
<pre>
 +
<layout
 +
  id="abilities">
 +
  <portalref portal="abAbility" taborder="10"/>
 +
  <portalref portal="edEdges" taborder="20"/>
 +
 +
  <!-- This script sizes and positions the layout and its child visual elements. -->
 +
  <position><![CDATA[
 +
    ~size all tables to span the full layout width
 +
    portal[abAbility].width = width
 +
    portal[edEdges].width = width
 +
 +
    ~reserve space for at least three rows of edges (if we need them)
 +
    portal[edEdges].maxrows = 3
 +
 +
    ~position the edges table at the bottom
 +
    portal[edEdges].top = height - portal[edEdges].height
 +
 +
    ~assign the remaining space to the abilities table
 +
    portal[abAbility].height = portal[edEdges].top - 10 - portal[abAbility].top
 +
 +
    ~position the edge table beneath abilities and let it expand to fill space
 +
    portal[edEdges].top = portal[abAbility].bottom + 10
 +
    portal[edEdges].height = height - portal[edEdges].top
 +
    ]]></position>
 +
 +
  </layout>
 
</pre>
 
</pre>
  
Line 379: Line 412:
 
<pre>
 
<pre>
 
if (tagis[Helper.NPCImpact] + !hero.tagis[Hero.PC] >= 2) then
 
if (tagis[Helper.NPCImpact] + !hero.tagis[Hero.PC] >= 2) then
 +
</pre>
 +
 +
This exact same problem repeats itself one more time on the "Abilities" tab. The table of edges needs to have both the Titlebar and HeaderTitle scripts revised to only show the summary information for non-creatures. The two new scripts are shown below, respectively.
 +
 +
<pre>
 +
@text = "Add an Edge"
 +
if (hero.tagis[Hero.Creature] = 0) then
 +
  @text &= "  -  " & hero.child[resEdge].field[resSummary].text
 +
  endif
 +
</pre>
 +
 +
<pre>
 +
@text = "Edges"
 +
if (hero.tagis[Hero.Creature] = 0) then
 +
  @text &= "  -  " & hero.child[resEdge].field[resSummary].text
 +
  endif
 
</pre>
 
</pre>
  
Line 427: Line 476:
 
</pre>
 
</pre>
  
===Output Revisions===
+
===Creature Type in Output===
  
 
The final detail we haven't addressed yet for creatures is output. Both character sheet and statblock output assume that every character will be assigned a race. We need to revise those mechanisms to handle the presence of a creature type instead.
 
The final detail we haven't addressed yet for creatures is output. Both character sheet and statblock output assume that every character will be assigned a race. We need to revise those mechanisms to handle the presence of a creature type instead.
  
We'll start with the statblock output. The Synthesize script assumes the use of a race, so we need to handle both possibilities. We can key on the "Hero.Creature" tag, which results in the following revised block of script code below.
+
Doing a scan through the data files, there are actually six different places where the race is being output. Each of these will need to be revised to accommodate creatures as well. This is a perfect opportunity for us to add a new procedure that can be shared by all these places. Unfortunately, we need different formatting in different situations, so a procedure will not simplify matters for us.
 +
 
 +
However, there is another solution we could use. If we add two new fields, we can setup those fields to contain the appropriate prefix and name to be used (e.g. "Race" and "Human"). For creatures, we can set the fields to use the proper info for the creature (e.g. "Creature" and "Goblin"). Once this is done, all five places can simply retrieve the fields and not have to do any special handling for creatures.
 +
 
 +
Our two fields must be added to the "Actor" component. We'll call them "acRacePref" and "acRaceName", and we'll need to define an Eval script on the component to synthesize them properly. In case we decide to use short names anywhere for races in the future, we need to schedule our script after Render/100, but we otherwise want to have our names in place very early in the Render phase. This yields the following field definitions and script below.
 +
 
 +
<pre>
 +
<field
 +
  id="acRacePref"
 +
  name="Race Prefix"
 +
  type="derived"
 +
  maxlength="10">
 +
  </field>
 +
 
 +
<field
 +
  id="acRaceName"
 +
  name="Race Name"
 +
  type="derived"
 +
  maxlength="50">
 +
  </field>
 +
</pre>
 +
 
 +
<pre>
 +
<eval index="9" phase="Render" priority="1000"><![CDATA[
 +
  if (hero.tagis[Hero.Creature] <> 0) then
 +
    field[acRacePref].text = "Creature"
 +
    field[acRaceName].text = hero.firstchild["Creature.?"].field[name].text
 +
  else
 +
    field[acRacePref].text = "Race"
 +
    field[acRaceName].text = hero.firstchild["Race.?"].field[name].text
 +
    endif
 +
  if (field[acRaceName].isempty <> 0) then
 +
    field[acRaceName].text = "-none-"
 +
    endif
 +
  ]]></eval>
 +
</pre>
 +
 
 +
Now that the fields are being setup properly, we can go through and change all references to the race to utilize the new fields. We'll start with the statblock output. The Synthesize script assumes the use of a race, and switching it to the new fields results in the following revised block of script code below.
  
 
<pre>
 
<pre>
 
~output any race or creature type
 
~output any race or creature type
if (hero.tagis[Hero.Creature] <> 0) then
+
append @boldon & herofield[acRacePref].text & ": " & @boldoff
  txt = "Creature: "
+
append herofield[acRaceName].text & @newline
  name = hero.firstchild["Creature.?"].field[name].text
+
else
+
  txt = "Race: "
+
  name = hero.firstchild["Race.?"].field[name].text
+
  endif
+
if (empty(name) <> 0) then
+
  name = "-none-"
+
  endif
+
append @boldon & txt & @boldoff & name & @newline
+
 
</pre>
 
</pre>
  
Switching our focus over to the character sheet, the same situation exists. The "oHeroInfo" portal assumes we always use a race, and we need to add handling for both situations. We'll revise the code at the start of the Label script to look like the following.
+
Switching our focus over to the character sheet, the same situation exists. The "oHeroInfo" portal assumes we always use a race on the first sheet, and the "details" portal for allies makes the same assumption. We'll revise the pertinent code for each Label script to look like the following.
  
 
<pre>
 
<pre>
 
~start with the character's race
 
~start with the character's race
var prefix as string
+
@text &= "{size 36}" & herofield[acRacePref].text & ": " & herofield[acRaceName].text
var temp as string
+
</pre>
if (hero.tagis[Hero.Creature] <> 0) then
+
 
   prefix = "Creature: "
+
<pre>
   temp = hero.firstchild["component.Creature"].field[name].text
+
~output any race
 +
@text &= "{b}" & herofield[acRacePref].text & ":{/b} " & herofield[acRaceName].text
 +
</pre>
 +
 
 +
We're halfway completed. Next up is the Eval script on the "Actor" component that synthesizes the "acRecap" field for display on the "Allies" tab. The code for outputting the race gets changed to the following.
 +
 
 +
<pre>
 +
~output any race
 +
recap &= field[acRaceName].text & ", "
 +
</pre>
 +
 
 +
The race is included in the text synthesized by the LeadSummary script within the definition file. We can quickly replace the code that determines the race with a reference to the new field, as shown below.
 +
 
 +
<pre>
 +
@text &= herofield[acRaceName].text
 +
</pre>
 +
 
 +
Last on our list is the procedure that synthesizes the name for the display on the Dashboard and within the Tactical Console. The "DshBasics" procedure has its race handling code changed to what's shown below.
 +
 
 +
<pre>
 +
~output our race
 +
final = herofield[acRacePref].text & ": {b}" & herofield[acRaceName].text & "{/b}{br}{br}"
 +
</pre>
 +
 
 +
We can now reload our data files and verify that creatures are being handled properly within output everywhere.
 +
 
 +
===Creature Abilities in Output===
 +
 
 +
A number of creatures possess an edge or two, in addition to the various creature abilities that are normally assigned. This results in two separate tables being output on the character sheet. If a hindrance is also added, then we have three tables, and none of them contain more than a few entries. It would be optimal to consolidate all of these tables into a single table for creatures.
 +
 
 +
Converting the character sheet to only use a single abilities table is pretty easy. We'll start by defining a new table portal that shows all of the abilities. It's identical to the table of racial abilities, except that we specify the general "Ability" component to encompass all the different types of abilities. The new portal is shown below.
 +
 
 +
<pre>
 +
<portal
 +
   id="oCreature"
 +
   style="outNormal">
 +
  <output_table
 +
    component="Ability"
 +
    showtemplate="oAbilPick">
 +
    <list>!Print.NoPrint</list>
 +
    <headertitle><![CDATA[
 +
      @text = "Creature Abilities"
 +
      ]]></headertitle>
 +
    </output_table>
 +
  </portal>
 +
</pre>
 +
 
 +
With the portal defined, we can integrate it into the layouts. We need to integrate it into both the left and right side layouts, since it's conceivable (albeit very unlikely) that the list of abilities will extend past the bottom of the page on the left. The process is the same for both layouts. We add a new "portalref" element, then we determine the visibility of the various table portals based on the presence of the "Hero.Creature" tag, and then we add an "autoplace" statement to position the new portal. The revised Position script for the "oLeftSide" layout is shown below.
 +
 
 +
<pre>
 +
~hide and show appropriate portals for creatures and non-creatures
 +
if (hero.tagis[Hero.Creature] = 0) then
 +
  portal[oCreature].visible = 0
 
else
 
else
   prefix = "Race: "
+
   portal[oAbility].visible = 0
   temp = hero.firstchild["component.Race"].field[name].text
+
   portal[oHindrance].visible = 0
  endif
+
   portal[oEdge].visible = 0
if (empty(temp) <> 0) then
+
   temp = "Unknown"
+
 
   endif
 
   endif
@text &= "{size 36}" & prefix & temp
+
 
 +
~position the hero name at the top with the hero details beneath the name
 +
perform portal[oHeroName].autoplace[0]
 +
perform portal[oHeroInfo].autoplace[15]
 +
 
 +
~position the tables next
 +
perform portal[oAttribute].autoplace
 +
perform portal[oDerived].autoplace
 +
perform portal[oSkills].autoplace
 +
perform portal[oSkillsDom].autoplace[0]
 +
perform portal[oAbility].autoplace
 +
perform portal[oHindrance].autoplace
 +
perform portal[oEdge].autoplace
 +
perform portal[oCreature].autoplace
 +
 
 +
~our layout height is the extent of the elements within
 +
height = autotop
 
</pre>
 
</pre>
  
We're not quite finished yet. We still have not dealt with the output of allies. All the details for an ally are synthesized by a script for output. That script also assumes that we always use a race, so we have one more place where we need to deal with this change. In the Eval script on the "Actor" component, the first thing it does is include the race. We can revise the code to the following and creatures will be handled properly.
+
The "oRightSide" layout is handled the exact same way, resulting in the following revised Position script.
  
 
<pre>
 
<pre>
~output any race
+
~hide and show appropriate portals for creatures and non-creatures
if (hero.tagis[Hero.Creature] <> 0) then
+
if (hero.tagis[Hero.Creature] = 0) then
   txt = hero.firstchild["Creature.?"].field[name].text
+
   portal[oCreature].visible = 0
 
else
 
else
   txt = hero.firstchild["Race.?"].field[name].text
+
   portal[oEdge].visible = 0
 
   endif
 
   endif
if (empty(txt) = 0) then
+
 
   recap &= txt & ", "
+
~position the various tables appropriately
 +
perform portal[oEdge].autoplace
 +
perform portal[oCreature].autoplace
 +
perform portal[oDrawback].autoplace
 +
perform portal[oPower].autoplace
 +
 
 +
~our layout height is the extent of the elements within
 +
height = autotop
 +
</pre>
 +
 
 +
We can now print out a creature like the Orc and see a single table of creature abilities instead of two tiny tables of edges and racial abilities.
 +
 
 +
===Bonuses from Traits Over d12===
 +
 
 +
When we added the automatic spillover of a trait die larger than "d12", we broke something. If the trait die is larger than "d12", we translate that overage into the trait roll. However, that overage is not being properly factored into the calculation of the Parry and Toughness traits.
 +
 
 +
The problem stems from an issue with timing. The Parry and Toughness traits calculate the influence of Fighting and Vigor at a timing of Traits/4000. Meanwhile, the conversion of excess trait dice to the net roll doesn't happen until Traits/8000. The Eval script that does the translation only needs to be performed after the bounding of the "trtUser" field, which occurs at Traits/1000, so we can re-schedule the script to anytime after that, as long as it also occurs before Traits/4000. We'll move it to Traits/2000.
 +
 
 +
As a safeguard against future timing issues involving these scripts, we'll also add some timing dependency relationships. We'll name each of the scripts for the derived traits at Traits/4000 with a common name, such as "Calc Derived Bonus". We can then add a "before" dependency on this name to the script we changed. We'll also add an "after" dependency to this script to ensure it gets scheduled after the bounding of "trtUser". The revised framework for the script should now look like below.
 +
 
 +
<pre>
 +
<eval index="5" phase="Traits" priority="2000">
 +
  <before name="Calc trtNetRoll"/>
 +
  <after name="Bound trtUser"/><![CDATA[
 +
  ~script code goes here
 +
  ]]></eval>
 +
</pre>
 +
 
 +
If we reload and test our data files with a creature, the roll bonus is now being factored into the trait bonus. Unfortunately, the bonus is coming out wrong. Taking a look at the scripts that calculate the bonus, it seems there is an assumption that the final trait value will never exceed six. If we eliminate that assumption, a quick change to the scripts gets things working properly. The one line that needs to be changed is shown below for the Toughness calculation. An equivalent change must be made for the Parry calculation.
 +
 
 +
<pre>
 +
bonus = 6 + round(hero.child[attrVig].field[trtRoll].value / 2,0,-1)
 +
</pre>
 +
 
 +
===Creatures With Zero Pace===
 +
 
 +
When we added all of the creatures, there was a situation that we could specify properly, but that is being honored by our data files. The problem is when we have a creature with a Pace of zero. Since the Pace for normal characters can never drop below one, we setup appropriate bounding directly on the "trPace" trait. So even if we specify a creature as having a Pace of zero, it gets bumped back up to one.
 +
 
 +
We can solve this by modifying the Eval script that does the bounding. If the actor has the "Hero.Creature" tag, we can assume that the actor is exempt from the bounding rule. The revised Eval script is shown below.
 +
 
 +
<pre>
 +
if (field[trtFinal].value <= 0) then
 +
   if (hero.tagis[Hero.Creature] = 0) then
 +
    field[trtFinal].value = 1
 +
    endif
 
   endif
 
   endif
 
</pre>
 
</pre>
  
===Let User Add Abilities===
+
===Ignoring Rank Requirements===
  
*change to a dynamic table
+
A number of creatures are assigned edges that have a variety of requirements. Most of those requirements are a minimum rank. Since creatures don't have any XP, they have no rank, so they fail any tests on rank. We can silence these validation errors by pretending we have plenty of XP. Since we don't use XP anywhere for creatures, this is a safe tactic.  
*allow user to specify facets of dynamic abilities that are needed (value/text)
+
*User.NeedText and User.NeedValue tags
+
  
===Custom Creatures===
+
The easiest way to accomplish this is by inserting special handling when the total XP is calculated. That's done within the "resXP" resource thing in "thing_miscellaneous.dat". All we need to do is revise the Eval script to look like the one shown below.
  
*define a "custom" creature that allows the author to create whatever he wants
+
<pre>
*define a "custom" ability that allows the author to annotate the behaviors without actually creating a new ability in the Editor
+
~if this is a creature, give ourselves a bunch of XP to reach legendary rank
**just like custom containers
+
~Note: This silences various validation rules for edges and the like.
 +
if (hero.tagis[Hero.Creature] <> 0) then
 +
  #resmax[resXP] += 100
 +
 
 +
~otherwise, our xp total is our starting xp plus accrued xp via journal entries
 +
else
 +
  field[resMax].value = herofield[acStartXP].value + hero.usagepool[TotalXP].value
 +
  endif
 +
</pre>

Latest revision as of 04:31, 12 February 2009

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

We've now got the ability to define and customize creatures in place. However, there are still a fair number of tasks we need to complete before creatures are fully handled. The sections below address these remaining issues.

Organizing Abilities

Between all the races and creatures, we've got a long list of special abilities. These abilities seem to break down into five different classifications.

  • Common racial abilities that can be shared by multiple races (e.g. Tough, Agile)
  • Race-specific abilities that are unique to a single race (e.g. Avion Flight, Saurian Senses)
  • Common creature abilities that can be shared by multiple creature (e.g. Size, Infravision)
  • Creature-specific abilities that are unique to a single creature (e.g. Rollover, Bear Hug)
  • Generic abilities that are shared by both races and creatures (e.g. Natural Weapons, Natural Armor)

To keep everything organized, we'll partition these abilities across three separate data files.

  • One file will contain all the races and race-specific abilities ("thing_races.dat")
  • Another file will contain all the creatures and creature-specific abilities ("thing_creatures.dat")
  • The third file will contain only re-usable special abilities for both races and/or creatures ("thing_abilities.dat")

We need a way to readily identify common creature abilities as such. For this, we'll define a new "User.Creature" tag, which we can then assign to all abilities that can be used freely by any creature. These abilities will consist of the two generic abilities we created for natural weapons and armor, as well as all of the common creature abilities spelled out in the rulebook.

Defining Abilities

We can now focus on getting all of the common creature abilities properly defined. The core rulebook outlines a couple dozen of these abilities. We should get them all into place before we start defining lots of creatures that depend on them.

We'll pick a few of these abilities to implement as examples here. We'll start with the "Size" ability, which specifies a non-standard size for the creature. The ability requires the specification of a size bonus (or penalty). We'll utilize the customizable ability mechanism and define the size bonus via the "abilValue" field. This bonus is both applied to the "Toughness" trait as an adjustment and incorporated into the name for display. This results in the ability definition below, which is followed by an example of its use.

<thing
  id="abSize"
  name="Size"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  <tag group="User" tag="Creature"/>
  <!-- Apply the size bonus to the Toughness trait -->
  <eval index="1" phase="PreTraits" priority="5000">
    <before name="Calc trtFinal"/><![CDATA[
    perform #traitadjust[trTough,+,field[abilValue].value,"Size"]
    field[livename].text = field[name].text & " " & signed(field[abilValue].value)
    ]]></eval>
  </thing>
<bootstrap thing="abSize">
  <assignval field="abilValue" value="2"/>
  </bootstrap>

Our next example is the "Immunity" ability. This ability requires that specification of the nature of the immunity. The customizable ability mechanism can again be used, only this time we'll specify the specific immunity via the "abilText" field. The immunity can then be integrated into the name for the display. This yields the ability definition below, followed by an example of its use.

<thing
  id="abImmunity"
  name="Immunity"
  compset="RaceAbil"
  description="Description goes here">
  <tag group="User" tag="Creature"/>
  <!-- Append the immunity to the name -->
  <eval index="1" phase="Traits" priority="10000"><![CDATA[
    field[livename].text = field[thingname].text & ": " & field[abilText].text
    ]]></eval>
  </thing>
<bootstrap thing="abImmunity">
  <assignval field="abilText" value="Fire"/>
  </bootstrap>

Some abilities automatically confer other abilities. For example, the "Elemental" ability confers the "Fearless" ability. This is easily achieved by simply bootstrapping the conferred ability, as shown in the definition below.

<thing
  id="abElement"
  name="Elemental"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  <tag group="User" tag="Creature"/>
  <bootstrap thing="abFearless"/>
  </thing>

You should now be able to implement all the various abilities utilized in the rulebook. Alternately, you can simply look at the completed Savage Worlds data files provided with HL.

New Abilities Tab

We eliminated the "Edges" tab with the assumption that we would replace it with a suitable alternative. We'll take care of that now. What we need is a tab where all the abilities assigned to the creature can be shown to the user. We only need one table, so we can clone the "Skills" tab and adapt to our needs.

Start by copying the "tab_skills.dat" file to "tab_abilities.dat", then open the new file for modification. We need to define a new table portal. Since all the abilities are static for a creature, we simply need a fixed table. All we need to show is the name of each ability, so we can use the "SimpleItem" template to show the abilities. This results in the portal definition shown below.

<portal
  id="abAbility"
  style="tblNormal">
  <table_fixed
    component="RaceAbil"
    showtemplate="SimpleItem">
    <headertitle><![CDATA[
      @text = "Monstrous Abilities"
      ]]></headertitle>
    </table_fixed>
  </portal>

We can then adapt the layout to show our new portal and have its own identity. The new layout should look like the following.

<layout
  id="abilities">
  <portalref portal="abAbility" taborder="10"/>
  <position><![CDATA[
    ~position and size the table to span the full layout; it will only use the
    ~vertical space that it actually needs
    perform portal[abAbility].autoplace
    ]]></position>
  </layout>

The final step is to revise the panel definition. We want this panel to appear in place of the "Edges" panel, so we'll assign it the same "order" value as that panel. We also want this panel to only appear for creatures, so we'll use a Live tag expression that requires the presence of the "Hero.Creature" tag. After we modify the name and other references appropriately, we end up with the panel below.

<panel
  id="abilities"
  name="Abilities"
  marginhorz="5"
  marginvert="5"
  order="130">
  <live>Hero.Creature</live>
  <layoutref layout="abilities"/>
  <position><![CDATA[
    ]]></position>
  </panel>

We're now ready to give it a try. Reload the files and create a creature. The new tab should appear with a list of the various abilities assigned to the creature.

Defining Complete Creatures

All the pieces are in place to allow you to define any of the creatures in the core rulebook. We'll take this opportunity to define a few of them here as examples.

The first example we'll use is the "Alligator/Crocodile" creature. This creature has a non-standard "Pace" on the ground, as well as a special "Pace" in the water. It utilizes both a natural weapon and natural armor. Plus it has a creature-specific ability. The full definition of the creature and its new special ability is shown below.

<thing
  id="creAlligat"
  name="Alligator/Crocodile"
  compset="Creature"
  isunique="yes"
  description="Description goes here">
  <fieldval field="crePace" value="3"/>
  <tag group="AgiDie" tag="2"/>
  <tag group="SmaDie" tag="2"/>
  <tag group="SpiDie" tag="3"/>
  <tag group="StrDie" tag="5"/>
  <tag group="VigDie" tag="5"/>
  <bootstrap thing="skFighting">
    <autotag group="SkillDie" tag="4"/>
    </bootstrap>
  <bootstrap thing="skGuts">
    <autotag group="SkillDie" tag="3"/>
    </bootstrap>
  <bootstrap thing="skNotice">
    <autotag group="SkillDie" tag="3"/>
    </bootstrap>
  <bootstrap thing="skSwimming">
    <autotag group="SkillDie" tag="4"/>
    </bootstrap>
  <bootstrap thing="abArmor">
    <assignval field="abilValue" value="2"/>
    </bootstrap>
  <bootstrap thing="abWeapon">
    <assignval field="livename" value="Bite"/>
    <assignval field="abilText" value="+d6"/>
    </bootstrap>
  <bootstrap thing="abAquatic">
    <assignval field="abilValue" value="5"/>
    </bootstrap>
  <bootstrap thing="abRollover"/>
  </thing>

<thing
  id="abRollover"
  name="Rollover"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  </thing>

For our next example, we'll use the "Large Bear" entry. This creature introduces the use of a Strength rating above "d12". It also utilizes a natural weapon and the "Size" ability, as well as its own custom ability. The full definition of the creature is shown below.

<thing
  id="creBearLg"
  name="Bear, Large"
  compset="Creature"
  isunique="yes"
  description="Description goes here">
  <fieldval field="crePace" value="8"/>
  <tag group="AgiDie" tag="3"/>
  <tag group="SmaDie" tag="3"/>
  <tag group="SpiDie" tag="4"/>
  <tag group="StrDie" tag="10"/>
  <tag group="VigDie" tag="6"/>
  <bootstrap thing="skFighting">
    <autotag group="SkillDie" tag="4"/>
    </bootstrap>
  <bootstrap thing="skGuts">
    <autotag group="SkillDie" tag="5"/>
    </bootstrap>
  <bootstrap thing="skNotice">
    <autotag group="SkillDie" tag="4"/>
    </bootstrap>
  <bootstrap thing="skSwimming">
    <autotag group="SkillDie" tag="3"/>
    </bootstrap>
  <bootstrap thing="abWeapon">
    <assignval field="livename" value="Claw"/>
    <assignval field="abilText" value="+d6"/>
    </bootstrap>
  <bootstrap thing="abSize">
    <assignval field="abilValue" value="2"/>
    </bootstrap>
  <bootstrap thing="abBearHug"/>
  </thing>

<thing
  id="abBearHug"
  name="Bear Hug"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  </thing>

For our final example, we'll implement a creature that's got some interesting twists. The "Lich" is a wildcard, has skills that exceed a "d12" rating, and has a skill that requires a domain be specified. It also has gear in the form of magical armor and gains arcane powers with extra bonuses to spells and power points. The full implementation is shown below (the "Undead" ability is assumed already defined as one of the common abilities shared by creatures).

<thing
  id="creLich"
  name="Lich"
  compset="Creature"
  isunique="yes"
  description="Description goes here">
  <fieldval field="crePace" value="6"/>
  <tag group="User" tag="Wildcard"/>
  <tag group="AgiDie" tag="3"/>
  <tag group="SmaDie" tag="8"/>
  <tag group="SpiDie" tag="5"/>
  <tag group="StrDie" tag="5"/>
  <tag group="VigDie" tag="5"/>
  <bootstrap thing="skFighting">
    <autotag group="SkillDie" tag="4"/>
    </bootstrap>
  <bootstrap thing="skGuts">
    <autotag group="SkillDie" tag="6"/>
    </bootstrap>
  <bootstrap thing="skIntimid">
    <autotag group="SkillDie" tag="6"/>
    </bootstrap>
  <bootstrap thing="skKnow">
    <autotag group="SkillDie" tag="8"/>
    <assignval field="domDomain" value="Occult"/>
    </bootstrap>
  <bootstrap thing="skNotice">
    <autotag group="SkillDie" tag="5"/>
    </bootstrap>
  <bootstrap thing="skSpellcst">
    <autotag group="SkillDie" tag="6"/>
    </bootstrap>
  <bootstrap thing="abDeathTch"/>
  <bootstrap thing="abSpellLch"/>
  <bootstrap thing="abUndead"/>
  <bootstrap thing="abZombie"/>
  <bootstrap thing="armMagLch">
    <autotag group="Equipment" tag="StartEquip"/>
    </bootstrap>
  </thing>

<thing
  id="armMagLch"
  name="Magical Armor +6"
  compset="Armor"
  description="Description goes here"
  isunique="yes">
  <fieldval field="defDefense" value="6"/>
  <tag group="Equipment" tag="StartEquip"/>
  <tag group="ArmorLoc" tag="Torso"/>
  <tag group="ArmorLoc" tag="Arms"/>
  <tag group="ArmorLoc" tag="Legs"/>
  <tag group="ArmorLoc" tag="Head"/>
  </thing>

<thing
  id="abDeathTch"
  name="Death Touch"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  </thing>

<thing
  id="abSpellLch"
  name="Spells"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  <bootstrap thing="edgArcMag"/>
  <eval index="1" phase="PreTraits" priority="5000"><![CDATA[
    ~confer an extra 40 power points and an extra 7 spells (50/10 total)
    #resmax[resPowers] += 7
    #trkmax[trkPower] += 40
    ]]></eval>
  </thing>

<thing
  id="abZombie"
  name="Zombie"
  compset="RaceAbil"
  isunique="yes"
  description="Description goes here">
  </thing>

Showing Edges

A number of creatures are assigned edges. Unfortunately, those edges aren't appearing on the new "Abilities" tab that we created. We need to show them appropriately so that the user clearly sees them. That requires us to expand the "Abilities" to include a second table for the edges. The good news is that we can simply re-use the existing table of edges within a second layout.

We'll replace the simplistic "abilities" layout with something that intelligently handles the two tables. We have to handle the case where both tables are too tall to fit within the available vertical space. Since edges will be generally uncommon for creatures, and their number will be few, we'll put them at the bottom, with creature abilities at the top. We'll reserve space for a few edges, then dedicate the rest of the height to abilities. If we don't need all the space for abilities, the edges can expand to fill whatever room remains. Putting all this together yields the revised layout shown below.

<layout
  id="abilities">
  <portalref portal="abAbility" taborder="10"/>
  <portalref portal="edEdges" taborder="20"/>

  <!-- This script sizes and positions the layout and its child visual elements. -->
  <position><![CDATA[
    ~size all tables to span the full layout width
    portal[abAbility].width = width
    portal[edEdges].width = width

    ~reserve space for at least three rows of edges (if we need them)
    portal[edEdges].maxrows = 3

    ~position the edges table at the bottom
    portal[edEdges].top = height - portal[edEdges].height

    ~assign the remaining space to the abilities table
    portal[abAbility].height = portal[edEdges].top - 10 - portal[abAbility].top

    ~position the edge table beneath abilities and let it expand to fill space
    portal[edEdges].top = portal[abAbility].bottom + 10
    portal[edEdges].height = height - portal[edEdges].top
    ]]></position>

  </layout>

Cleanup the Interface

Due to our changes in how creatures are handled, there are a number of facets of the interface that need some work. We'll start with the issues on the "Basics" tab.

The first thing we can't miss on the "Basics" tab is the display of XP information and the list of creation resources. Creatures don't track any XP and they don't observe any special creation rules regarding point allocations. Consequently, both of these sections of information and any associated separators need to be hidden for creatures. This can be handled easily by adding the block of code below to the Position script for the "basics" layout.

~if we're creating a creature, don't show XP or creation details
if (hero.tagis[Hero.Creature] <> 0) then
  portal[baRank].visible = 0
  portal[baCreation].visible = 0
  portal[separator1].visible = 0
  portal[separator2].visible = 0
  endif

There is also the matter of the header above the table of attributes. The header shows the number of remaining points that can be spent, which is meaningless for creatures. We need to revise the header to simply show the text "Attributes" when the user is creating a creature. This is accomplished by changing the code for the HeaderTitle script to that shown below.

@text = "Attributes"
if (hero.tagis[Hero.Creature] = 0) then
  @text &= "  -  " & hero.child[resAttrib].field[resSummary].text
  endif

We can now proceed to the "Skills" tab, where we'll find a few additional issues to address. For example, the header above the list of skills suffers the same problem that the one above the attributes did on the previous tab. The solution is the same, replacing the HeaderTitle script with the new code below.

@text = "Skills"
if (hero.tagis[Hero.Creature] = 0) then
  @text &= "  -  " & hero.child[resSkill].field[resSummary].text
  endif

If we click on the "add" item at the bottom of the table of skills, we'll see a similar problem again. The title shown at the top of the selection form shows same extra information. We need to revise the Titlebar script in the same way, as shown below.

@text = "Add a Skill"
if (hero.tagis[Hero.Creature] = 0) then
  @text &= "  -  " & hero.child[resSkill].field[resSummary].text
  endif

The final issue on the "Skills" tab is that the "add" item at the bottom of the table is using the highlight information for a normal character. This information is controlled via the "resAddItem" field of the resource. We already disabled the special formatting for NPCs, and we need to change that logic to apply to any non-PC character (i.e. including creatures). This is done by changing the one line of code in the Finalize script to the following.

if (tagis[Helper.NPCImpact] + !hero.tagis[Hero.PC] >= 2) then

This exact same problem repeats itself one more time on the "Abilities" tab. The table of edges needs to have both the Titlebar and HeaderTitle scripts revised to only show the summary information for non-creatures. The two new scripts are shown below, respectively.

@text = "Add an Edge"
if (hero.tagis[Hero.Creature] = 0) then
  @text &= "  -  " & hero.child[resEdge].field[resSummary].text
  endif
@text = "Edges"
if (hero.tagis[Hero.Creature] = 0) then
  @text &= "  -  " & hero.child[resEdge].field[resSummary].text
  endif

Validation Rules

The first thing we'll notice regarding the validation report is that various resources will sometimes report that they have been overspent. These are the same resources that we disabled for NPC creation, and they must also be disabled for creatures. This can be solved by modifying the Eval Rule on the "Resource" component to consider the rule as valid for any non-PC. The new line of code required is shown below.

if (tagis[Helper.NPCImpact] + !hero.tagis[Hero.PC] >= 2) then

If the user elects to construct a creature, the specific creature type needs to be specified. We need a validation rule to verify this is done. We already a thing defined with a validation rule to verify a race is selected. We can easily clone that thing and adapt it for use with the creature type chooser, resulting in the thing shown below.

<thing
  id="valCreatur"
  name="Creature Type"
  compset="Simple">
  <tag group="Helper" tag="Bootstrap"/>

  <evalrule index="1" phase="Validate" priority="8000" message="Must be selected"><![CDATA[
    ~if we have a creature type selected, we're good
    if (hero.tagis[Creature.?] <> 0) then
      @valid = 1
      done
      endif
    ]]></evalrule>

  </thing>

Unfortunately, we have a small problem still. If we create a normal character, we always get a validation error about the creature type. If we create a creature, we always get a validation error about the race. Each of these validation rules needs to be applied only when the appropriate character type is in use. One solution would be to utilize an appropriate ContainerReq on each of the validation things. An easier solution is to test whether we have a creature within the Eval Rule script. The revised script code for the "valRace" and "valCreatur" things is shown below.

~if we have a race selected or we are a creature, we're good
if (hero.tagis[Race.?] + hero.tagis[Hero.Creature] <> 0) then
  @valid = 1
  done
  endif
~if we have a creature type selected or we're not a creature, we're good
if (hero.tagis[Creature.?] + !hero.tagis[Hero.Creature] <> 0) then
  @valid = 1
  done
  endif

Creature Type in Output

The final detail we haven't addressed yet for creatures is output. Both character sheet and statblock output assume that every character will be assigned a race. We need to revise those mechanisms to handle the presence of a creature type instead.

Doing a scan through the data files, there are actually six different places where the race is being output. Each of these will need to be revised to accommodate creatures as well. This is a perfect opportunity for us to add a new procedure that can be shared by all these places. Unfortunately, we need different formatting in different situations, so a procedure will not simplify matters for us.

However, there is another solution we could use. If we add two new fields, we can setup those fields to contain the appropriate prefix and name to be used (e.g. "Race" and "Human"). For creatures, we can set the fields to use the proper info for the creature (e.g. "Creature" and "Goblin"). Once this is done, all five places can simply retrieve the fields and not have to do any special handling for creatures.

Our two fields must be added to the "Actor" component. We'll call them "acRacePref" and "acRaceName", and we'll need to define an Eval script on the component to synthesize them properly. In case we decide to use short names anywhere for races in the future, we need to schedule our script after Render/100, but we otherwise want to have our names in place very early in the Render phase. This yields the following field definitions and script below.

<field
  id="acRacePref"
  name="Race Prefix"
  type="derived"
  maxlength="10">
  </field>

<field
  id="acRaceName"
  name="Race Name"
  type="derived"
  maxlength="50">
  </field>
<eval index="9" phase="Render" priority="1000"><![CDATA[
  if (hero.tagis[Hero.Creature] <> 0) then
    field[acRacePref].text = "Creature"
    field[acRaceName].text = hero.firstchild["Creature.?"].field[name].text
  else
    field[acRacePref].text = "Race"
    field[acRaceName].text = hero.firstchild["Race.?"].field[name].text
    endif
  if (field[acRaceName].isempty <> 0) then
    field[acRaceName].text = "-none-"
    endif
  ]]></eval>

Now that the fields are being setup properly, we can go through and change all references to the race to utilize the new fields. We'll start with the statblock output. The Synthesize script assumes the use of a race, and switching it to the new fields results in the following revised block of script code below.

~output any race or creature type
append @boldon & herofield[acRacePref].text & ": " & @boldoff
append herofield[acRaceName].text & @newline

Switching our focus over to the character sheet, the same situation exists. The "oHeroInfo" portal assumes we always use a race on the first sheet, and the "details" portal for allies makes the same assumption. We'll revise the pertinent code for each Label script to look like the following.

~start with the character's race
@text &= "{size 36}" & herofield[acRacePref].text & ": " & herofield[acRaceName].text
~output any race
@text &= "{b}" & herofield[acRacePref].text & ":{/b} " & herofield[acRaceName].text

We're halfway completed. Next up is the Eval script on the "Actor" component that synthesizes the "acRecap" field for display on the "Allies" tab. The code for outputting the race gets changed to the following.

~output any race
recap &= field[acRaceName].text & ", "

The race is included in the text synthesized by the LeadSummary script within the definition file. We can quickly replace the code that determines the race with a reference to the new field, as shown below.

@text &= herofield[acRaceName].text

Last on our list is the procedure that synthesizes the name for the display on the Dashboard and within the Tactical Console. The "DshBasics" procedure has its race handling code changed to what's shown below.

~output our race
final = herofield[acRacePref].text & ": {b}" & herofield[acRaceName].text & "{/b}{br}{br}"

We can now reload our data files and verify that creatures are being handled properly within output everywhere.

Creature Abilities in Output

A number of creatures possess an edge or two, in addition to the various creature abilities that are normally assigned. This results in two separate tables being output on the character sheet. If a hindrance is also added, then we have three tables, and none of them contain more than a few entries. It would be optimal to consolidate all of these tables into a single table for creatures.

Converting the character sheet to only use a single abilities table is pretty easy. We'll start by defining a new table portal that shows all of the abilities. It's identical to the table of racial abilities, except that we specify the general "Ability" component to encompass all the different types of abilities. The new portal is shown below.

<portal
  id="oCreature"
  style="outNormal">
  <output_table
    component="Ability"
    showtemplate="oAbilPick">
    <list>!Print.NoPrint</list>
    <headertitle><![CDATA[
      @text = "Creature Abilities"
      ]]></headertitle>
    </output_table>
  </portal>

With the portal defined, we can integrate it into the layouts. We need to integrate it into both the left and right side layouts, since it's conceivable (albeit very unlikely) that the list of abilities will extend past the bottom of the page on the left. The process is the same for both layouts. We add a new "portalref" element, then we determine the visibility of the various table portals based on the presence of the "Hero.Creature" tag, and then we add an "autoplace" statement to position the new portal. The revised Position script for the "oLeftSide" layout is shown below.

~hide and show appropriate portals for creatures and non-creatures
if (hero.tagis[Hero.Creature] = 0) then
  portal[oCreature].visible = 0
else
  portal[oAbility].visible = 0
  portal[oHindrance].visible = 0
  portal[oEdge].visible = 0
  endif

~position the hero name at the top with the hero details beneath the name
perform portal[oHeroName].autoplace[0]
perform portal[oHeroInfo].autoplace[15]

~position the tables next
perform portal[oAttribute].autoplace
perform portal[oDerived].autoplace
perform portal[oSkills].autoplace
perform portal[oSkillsDom].autoplace[0]
perform portal[oAbility].autoplace
perform portal[oHindrance].autoplace
perform portal[oEdge].autoplace
perform portal[oCreature].autoplace

~our layout height is the extent of the elements within
height = autotop

The "oRightSide" layout is handled the exact same way, resulting in the following revised Position script.

~hide and show appropriate portals for creatures and non-creatures
if (hero.tagis[Hero.Creature] = 0) then
  portal[oCreature].visible = 0
else
  portal[oEdge].visible = 0
  endif

~position the various tables appropriately
perform portal[oEdge].autoplace
perform portal[oCreature].autoplace
perform portal[oDrawback].autoplace
perform portal[oPower].autoplace

~our layout height is the extent of the elements within
height = autotop

We can now print out a creature like the Orc and see a single table of creature abilities instead of two tiny tables of edges and racial abilities.

Bonuses from Traits Over d12

When we added the automatic spillover of a trait die larger than "d12", we broke something. If the trait die is larger than "d12", we translate that overage into the trait roll. However, that overage is not being properly factored into the calculation of the Parry and Toughness traits.

The problem stems from an issue with timing. The Parry and Toughness traits calculate the influence of Fighting and Vigor at a timing of Traits/4000. Meanwhile, the conversion of excess trait dice to the net roll doesn't happen until Traits/8000. The Eval script that does the translation only needs to be performed after the bounding of the "trtUser" field, which occurs at Traits/1000, so we can re-schedule the script to anytime after that, as long as it also occurs before Traits/4000. We'll move it to Traits/2000.

As a safeguard against future timing issues involving these scripts, we'll also add some timing dependency relationships. We'll name each of the scripts for the derived traits at Traits/4000 with a common name, such as "Calc Derived Bonus". We can then add a "before" dependency on this name to the script we changed. We'll also add an "after" dependency to this script to ensure it gets scheduled after the bounding of "trtUser". The revised framework for the script should now look like below.

<eval index="5" phase="Traits" priority="2000">
  <before name="Calc trtNetRoll"/>
  <after name="Bound trtUser"/><![CDATA[
  ~script code goes here
  ]]></eval>

If we reload and test our data files with a creature, the roll bonus is now being factored into the trait bonus. Unfortunately, the bonus is coming out wrong. Taking a look at the scripts that calculate the bonus, it seems there is an assumption that the final trait value will never exceed six. If we eliminate that assumption, a quick change to the scripts gets things working properly. The one line that needs to be changed is shown below for the Toughness calculation. An equivalent change must be made for the Parry calculation.

bonus = 6 + round(hero.child[attrVig].field[trtRoll].value / 2,0,-1)

Creatures With Zero Pace

When we added all of the creatures, there was a situation that we could specify properly, but that is being honored by our data files. The problem is when we have a creature with a Pace of zero. Since the Pace for normal characters can never drop below one, we setup appropriate bounding directly on the "trPace" trait. So even if we specify a creature as having a Pace of zero, it gets bumped back up to one.

We can solve this by modifying the Eval script that does the bounding. If the actor has the "Hero.Creature" tag, we can assume that the actor is exempt from the bounding rule. The revised Eval script is shown below.

if (field[trtFinal].value <= 0) then
  if (hero.tagis[Hero.Creature] = 0) then
    field[trtFinal].value = 1
    endif
  endif

Ignoring Rank Requirements

A number of creatures are assigned edges that have a variety of requirements. Most of those requirements are a minimum rank. Since creatures don't have any XP, they have no rank, so they fail any tests on rank. We can silence these validation errors by pretending we have plenty of XP. Since we don't use XP anywhere for creatures, this is a safe tactic.

The easiest way to accomplish this is by inserting special handling when the total XP is calculated. That's done within the "resXP" resource thing in "thing_miscellaneous.dat". All we need to do is revise the Eval script to look like the one shown below.

~if this is a creature, give ourselves a bunch of XP to reach legendary rank
~Note: This silences various validation rules for edges and the like.
if (hero.tagis[Hero.Creature] <> 0) then
  #resmax[resXP] += 100

~otherwise, our xp total is our starting xp plus accrued xp via journal entries
else
  field[resMax].value = herofield[acStartXP].value + hero.usagepool[TotalXP].value
  endif