Difference between revisions of "Final Cleanup Continued (Savage)"

From HLKitWiki
Jump to: navigation, search
(Show Linked Attributes on Skills)
(Show Linked Attributes on Skills)
Line 177: Line 177:
 
===Show Linked Attributes on Skills===
 
===Show Linked Attributes on Skills===
  
*in description text
+
On the "Skills" tab, we neglected to show the linked attribute with each skill. While not critical, having the linked skill visible can be helpful. We should also include the linked attribute within the description text for each skill.
*in list of skills for selection (chooser)
+
 
*in list of skills shown on Skills tab
+
We'll start by modifying the description text. We can define a new procedure to synthesize the information for skills and integrate it into the "Descript" procedure when a skill is processed. All we need to do is add the one piece of data, which is solved with the simple procedure below.
*on Special tab
+
 
 +
<pre>
 +
<procedure id="InfoSkill" context="info"><![CDATA[
 +
  ~declare variables that are used to communicate with our caller
 +
  var iteminfo as string
 +
  iteminfo = ""
 +
 
 +
  ~report the linked attribute
 +
  iteminfo &= "Linked Attribute: " & linkage[attribute].field[name].text & "{br}"
 +
  ]]></procedure>
 +
</pre>
 +
 
 +
There are two different places where we should show the linked attribute within tables. The first is within the list of selected skills that appears on the "Skills" tab. The other is within the table presented for the user to select skills to be added.
 +
 
 +
The linked attribute is a secondary piece of information, so it should be less prominent than the other facets of the skill. As such, we'll add it in a separate portal that appears at the far right and in a softer color. Within the "skPick" template that shows the skills added to the character, we can add the portal shown below.
 +
 
 +
<pre>
 +
<portal
 +
  id="attribute"
 +
  style="lblSecond">
 +
  <label>
 +
    <labeltext><![CDATA[
 +
      @text = "(" & linkage[attribute].field[trtAbbrev].text & ")"
 +
      ]]></labeltext>
 +
    </label>
 +
  </portal>
 +
</pre>
 +
 
 +
Once the portal is added, we can integrate it into the Position script for the template. There are a few things we need to tweak to complete the integration. The pertinent lines of the script are shown below.
 +
 
 +
<pre>
 +
~position the other portals vertically
 +
...
 +
perform portal[attribute].centervert
 +
 
 +
...
 +
 
 +
~position the attribute portal to the left of the info button
 +
perform portal[attribute].alignrel[rtol,info,-8]
 +
 
 +
...
 +
 
 +
~position the name next to the adjustment
 +
perform portal[name].alignrel[ltor,adjust,8]
 +
 
 +
~if we don't need a domain, hide it and let the name use all available space
 +
if (tagis[User.NeedDomain] = 0) then
 +
  portal[lbldomain].visible = 0
 +
  portal[domain].visible = 0
 +
  portal[name].width = minimum(portal[name].width,portal[attribute].left - portal[name].left - 10)
 +
 
 +
~otherwise, position the domain portals next to the name
 +
else
 +
  perform portal[lbldomain].alignrel[ltor,name,15]
 +
  perform portal[domain].alignrel[ltor,lbldomain,2]
 +
  portal[domain].width = minimum(150,portal[attribute].left - portal[domain].left - 5)
 +
  endif
 +
</pre>
 +
 
 +
Adding the linked attribute to the table for choosing skills requires a little bit more work. The "skSkills" table portal currently just uses the "SimpleItem" template for selection. In order to add the linked attribute, we'll need to create our own new template. We can do this easily by copying the "SimpleItem" template and calling it "skThing". Once that's done, we can modify the "skSkills" portal to reference our new template for choosing a skill.
 +
 
 +
The final thing we need to do is revise the template for our purposes. We can get rid of all the portals except for the name, then we can add a new portal similar to the one we added above for showing the linked attribute. When we're finished, the new template should look similar to the one below.
 +
 
 +
<pre>
 +
<template
 +
  id="skThing"
 +
  name="Skill Thing"
 +
  compset="Skill"
 +
  marginhorz="3"
 +
  marginvert="2">
 +
 
 +
  <portal
 +
    id="name"
 +
    style="lblNormal"
 +
    showinvalid="yes">
 +
    <label
 +
      field="name">
 +
      </label>
 +
    </portal>
 +
 
 +
  <portal
 +
    id="attribute"
 +
    style="lblSecond">
 +
    <label>
 +
      <labeltext><![CDATA[
 +
        @text = linkage[attribute].field[name].text
 +
        ]]></labeltext>
 +
      </label>
 +
    </portal>
 +
 
 +
  <position><![CDATA[
 +
    ~set up our height based on our tallest portal
 +
    height = portal[name].height
 +
 
 +
    ~if this is a "sizing" calculation, we're done
 +
    if (issizing <> 0) then
 +
      done
 +
      endif
 +
 
 +
    ~center the portals vertically
 +
    perform portal[name].centervert
 +
    perform portal[attribute].centervert
 +
 
 +
    ~position the attribute portal on the far right
 +
    perform portal[attribute].alignedge[right,0]
 +
 
 +
    ~position the name on the left and let it use all available space
 +
    portal[name].left = 0
 +
    portal[name].width = minimum(portal[name].width,portal[attribute].left - 10)
 +
    ]]></position>
 +
 
 +
  </template>
 +
</pre>
  
 
===Next===
 
===Next===

Revision as of 03:09, 11 February 2009

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

Identify Hindrance Severity in Sheet Output

The list of hindrances that are output within the character sheet use the same mechanism as edges and racial abilities. However, hindrances can be taken in both major and minor forms, and that aspect of each hindrance is important to the player.

We can modify the Label script currently in use within the "oAbilPick" template to detect the presence of a hindrance and output it specially. We'll only flag major hindrances, and we'll do it by including a special symbol. This will keep the space requirements to an absolute minimum. We can pick a suitable character from the "Wingdings" font, resulting in the following revised Label script.

var major as string
if (tagis[component.Hindrance] <> 0) then
  if (field[hinMajor].value <> 0) then
    major = " {font Wingdings}" & chr(181) & "{revert}"
    endif
  endif
@text = field[shortname].text & major & "{/b}  {size 32}" & field[summary].text

Add Drawbacks to Character Sheet

It seems that we overlooked including the details of arcane drawbacks on the character sheet. Since it's something the player should not forget, we should probably include a reminder. One solution would be to show the drawback like a hindrance. Another would be to include the name of the drawback within the title above the table of arcane powers. And a third would be to add a new table that listed the drawback just like an edge or hindrance.

None of these options is ideal, and they each have their trade-offs. Treating drawbacks as hindrances would entail a fair amount of work to revise the data files. Since all we need is to display them, the extra work probably isn't worth it. Including the drawback within the title above the arcane powers table would not show its summary, plus it would result in a very cramped title area. Adding a new table is quick and easy, but the downside is that we'll consume additional vertical space on the sheet.

We'll go with the new table for the sake of simplicity. Our table portal can be adapted from the edges table, giving us the following.

<portal
  id="oDrawback"
  style="outNormal">
  <output_table
    component="Drawback"
    showtemplate="oDrawPick">
    <headertitle><![CDATA[
      @text = "Arcane Drawbacks"
      ]]></headertitle>
    </output_table>
  </portal>

The template can be readily adapted from the one used for special abilities. This results in the template shown below.

<template
  id="oDrawPick"
  name="Output Drawbacks Table"
  compset="Drawback"
  marginvert="2">

  <portal
    id="details"
    style="outMedLt">
    <output_label>
      <labeltext><![CDATA[
        @text = field[name].text & "{/b}  {size 32}" & field[summary].text
        ]]></labeltext>
      </output_label>
    </portal>

  <position><![CDATA[
    ~our details width spans the entire template width
    portal[details].width = width

    ~our height is the height of our portal
    height = portal[details].bottom
    ]]></position>
  </template>

The last step is to integrate the portal into a layout. We'll add it to the "oRightSide" layout and place immediately above the table of arcane powers. If we include it above the powers, it will always be prominently visible and immediately adjacent to the list of powers. All we need to do is add the "portalref" element and then auto-place the portal immediately beneath the continuation of the edges output.

Ignoring Wound Penalties

There are two edges that allow the character to ignore wound penalties (Nerves of Steel and the improved version). Since HL automatically applies the effects of all wounds penalties to rolls, users will assume the effects of these edges are properly factored in. We'd better make good on that assumption.

The wound and fatigue penalties are all handled via the "acNetPenal" field on the "Actor" component. We can add a new field to track the number of wound penalties that are ignored, and then we can factor that value into the final calculation for "acNetPenal". Our new field is shown below.

<field
  id="acIgnWound"
  name="Ignored Wound Penalties"
  type="derived">
  </field>

We can now modify the Calculate script on the "acNetPenal" field to take the ignored wounds into account. The revised script should look like the following.

@value = -field[acFatigue].value
if (field[acWounds].value > field[acIgnWound].value) then
  @value -= field[acWounds].value - field[acIgnWound].value
  endif

All the mechanics are in place. Our two edges can now use a simple Eval script to adjust the "acIgnWound" field, and everything works exactly as we want.

<eval index="1" phase="Setup" priority="5000"><![CDATA[
  herofield[acIgnWound].value += 1
  ]]></eval>

Dependencies on Load Limit

The "Acrobat" edge exposes a timing issue with our data files. The edge confers a +1 to a character's Parry if the character has no encumbrance penalty. To implement this, we need to have the encumbrance penalty determined before we act, and we need to act before the final value for the Parry trait is resolved. Our current script timing does not work this way, although we should be able to do this.

Our goal is to be able to adjust the Parry trait prior to its final resolution. Derived traits are officially calculated at a timing of Traits/6000. So we need to get the encumbrance penalty resolved before then if at all possible.

The encumbrance penalty is tracked by the "resEncumb" resource. The final value is currently calculated at a timing of Final/500. The only timing requirements for this calculation are the accrual of weight carried (which occurs at Effects/10000) and the load limit (which occurs at Traits/7000). The weight accrual occurs long before we need to act for the "Acrobat" edge, so we can move the timing of the penalty if we can move the timing of the load limit calculation.

The load limit is tracked by the "resLoadLim" resource and it calculates its maximum (the value we need for the encumbrance penalty) at a timing of Traits/7000. This calculation depends on the "acLoadMult" field, which is nailed down very early in the evaluation cycle, so that's not a problem. It also depends on the final value of the Strength attribute. Attributes are finalized within the "trtFinal" field, which is calculated at a timing of Traits/3000. This means we can move the load limit calculation to a timing of Traits/4000 without any problems.

Once we move the load limit calculation, we can then safely move the encumbrance penalty calculation. We'll move that to a timing of Traits/4500. This leaves nice gap in the evaluation cycle where we can inspect the encumbrance and adjust the Parry trait based on it. We'll schedule the Eval script for the edge at Traits/5000.

Since we've gone through and analyzed all these dependencies, we'll do one additional thing. Each of the various scripts we identified should be named. Then we can specify appropriate timing dependencies between these scripts. Now we can let the compiler safety check our timing dependencies for us all the time. If we need to adjust the timing of one of these scripts in the future, we can rely on the compiler to let us know if we messed something up.

Edges Need Domain Support

When we implemented edges, we identified all the ones that looked to be special in some way and added appropriate handling for them. We missed one. The "Connections" edge requires that the user specify the organization with which the connections exist. This means we have to add domain support for edges. Fortunately, we've already done that a couple times, so it should not be complicated. In fact, it's rather simple.

The first thing we need to do is add the "Domain" component to the "Edge" component set. This will integrate domain handling internally into edges. The "Domain" component handles modifying the name, so all the mechanics we need are provided.

Next, we need to add support for domains to the interface. We can copy the two portals dealing with domains from the "edHinder" template and add them to the "edEdge" template. Then we can copy the positioning code for those portals, which we can drop into place with a single change. The "lbldomain" portal can be aligned directly relative to the "name" portal instead of using the non-existent "edge" variable.

Domain support is fully operational for edges now. All that remains to be done is assign the "User.NeedDomain" tag to the "Connections" edge. Once we do that, the edge will prompt the user for a domain and integrate into the name for display.

Background Edges are Creation-Only

All background edges are generally only valid for selection during character creation. The Skeleton files provide built-in support for designating abilities as creation-only (via the "User.CreateOnly" tag). However, adding the tag individually to all background edges is error-prone. It would be best if we automatically treated all background edges as creation-only.

Doing this is easy. We can clone the pre-requisite used for this purpose within the "Ability" component and add it to the "Edge" component. We can then adapt it to only apply to background edges. Since GMs can allow the selection of background edges after creation, we'll only flag an error when showing things. Once an edge is added, we'll assume the player did so with the GM's approval. The resulting pre-requisite should look like below.

<prereq message="Background edges are only available at character creation">
  <!-- This pre-req is only applicable to background edges -->
  <match><![CDATA[
    EdgeType.Background
    ]]></match>

  <validate><![CDATA[
    ~we only report a failure on things (once added, we assume the user knows best)
    if (@ispick <> 0) then
      @valid = 1
      done
      endif

    ~if the mode is creation, we're valid
    if (state.iscreate <> 0) then
      @valid = 1
      endif
    ]]></validate>
  </prereq>

Buying Off Hindrances

  • use an advancement to buy off a hindrance

Injuries As Advancements

  • treating injuries as advancements makes them inaccessible to NPCs and makes it difficult to heal them at a later date, so they must be handled separately on the Personal tab

Hiding Equipment

Various creatures define their own custom equipment, such as the "Goblin" and "Lich". This gear needs to be private to the particular creature and not make public for general selection by the user. To solve this, we need to define a new "Hide.Equipment" tag and then assign that tag to all such gear.

Once the tags are assigned, we need to modify all of the table portals on the "Armory" tab. This new tag must be integrated into the Candidate tag expression of each portal so that equipment with the tag is not shown for selection. In each case, the revised element should look like the one shown below.

<candidate inheritlist="yes"><![CDATA[
  !Equipment.Natural & !Hide.Equipment
  ]]></candidate>

Show Linked Attributes on Skills

On the "Skills" tab, we neglected to show the linked attribute with each skill. While not critical, having the linked skill visible can be helpful. We should also include the linked attribute within the description text for each skill.

We'll start by modifying the description text. We can define a new procedure to synthesize the information for skills and integrate it into the "Descript" procedure when a skill is processed. All we need to do is add the one piece of data, which is solved with the simple procedure below.

<procedure id="InfoSkill" context="info"><![CDATA[
  ~declare variables that are used to communicate with our caller
  var iteminfo as string
  iteminfo = ""

  ~report the linked attribute
  iteminfo &= "Linked Attribute: " & linkage[attribute].field[name].text & "{br}"
  ]]></procedure>

There are two different places where we should show the linked attribute within tables. The first is within the list of selected skills that appears on the "Skills" tab. The other is within the table presented for the user to select skills to be added.

The linked attribute is a secondary piece of information, so it should be less prominent than the other facets of the skill. As such, we'll add it in a separate portal that appears at the far right and in a softer color. Within the "skPick" template that shows the skills added to the character, we can add the portal shown below.

<portal
  id="attribute"
  style="lblSecond">
  <label>
    <labeltext><![CDATA[
      @text = "(" & linkage[attribute].field[trtAbbrev].text & ")"
      ]]></labeltext>
    </label>
  </portal>

Once the portal is added, we can integrate it into the Position script for the template. There are a few things we need to tweak to complete the integration. The pertinent lines of the script are shown below.

~position the other portals vertically
...
perform portal[attribute].centervert

...

~position the attribute portal to the left of the info button
perform portal[attribute].alignrel[rtol,info,-8]

...

~position the name next to the adjustment
perform portal[name].alignrel[ltor,adjust,8]

~if we don't need a domain, hide it and let the name use all available space
if (tagis[User.NeedDomain] = 0) then
  portal[lbldomain].visible = 0
  portal[domain].visible = 0
  portal[name].width = minimum(portal[name].width,portal[attribute].left - portal[name].left - 10)

~otherwise, position the domain portals next to the name
else
  perform portal[lbldomain].alignrel[ltor,name,15]
  perform portal[domain].alignrel[ltor,lbldomain,2]
  portal[domain].width = minimum(150,portal[attribute].left - portal[domain].left - 5)
  endif

Adding the linked attribute to the table for choosing skills requires a little bit more work. The "skSkills" table portal currently just uses the "SimpleItem" template for selection. In order to add the linked attribute, we'll need to create our own new template. We can do this easily by copying the "SimpleItem" template and calling it "skThing". Once that's done, we can modify the "skSkills" portal to reference our new template for choosing a skill.

The final thing we need to do is revise the template for our purposes. We can get rid of all the portals except for the name, then we can add a new portal similar to the one we added above for showing the linked attribute. When we're finished, the new template should look similar to the one below.

<template
  id="skThing"
  name="Skill Thing"
  compset="Skill"
  marginhorz="3"
  marginvert="2">

  <portal
    id="name"
    style="lblNormal"
    showinvalid="yes">
    <label
      field="name">
      </label>
    </portal>

  <portal
    id="attribute"
    style="lblSecond">
    <label>
      <labeltext><![CDATA[
        @text = linkage[attribute].field[name].text
        ]]></labeltext>
      </label>
    </portal>

  <position><![CDATA[
    ~set up our height based on our tallest portal
    height = portal[name].height

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

    ~center the portals vertically
    perform portal[name].centervert
    perform portal[attribute].centervert

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

    ~position the name on the left and let it use all available space
    portal[name].left = 0
    portal[name].width = minimum(portal[name].width,portal[attribute].left - 10)
    ]]></position>

  </template>

Next