Injuries (Savage): Difference between revisions

From HLKitWiki
Jump to navigationJump to search
No edit summary
No edit summary
Line 107: Line 107:
The one big drawback with this mechanism is that injuries are added just like new skills. This results in the interface presenting injuries for selection as if the user is selecting a new skill. The mechanism looks extremely odd. The solution is to introduce a new type of advancement and adapt the mechanism to work well for injuries as well. For the new advancement type, we'll define a new "Advance.Injury" tag within the file "advancements.core". This way, that we can readily distinguish injury advancements from other advancements. 
The one big drawback with this mechanism is that injuries are added just like new skills. This results in the interface presenting injuries for selection as if the user is selecting a new skill. The mechanism looks extremely odd. The solution is to introduce a new type of advancement and adapt the mechanism to work well for injuries as well. For the new advancement type, we'll define a new "Advance.Injury" tag within the file "advancements.core". This way, that we can readily distinguish injury advancements from other advancements. 


We can now open the file "panel_advance.dat" and look into adapting the current advancement mechanism for better use with injuries. Ideally, we could adapt the "advNew" chooser portal for use with injury selection. Unfortunately, the chooser lacks sufficient contextual details to be able to adapt the current portal. This means that we need to duplicate the chooser portal, rename it, and then look to adapt the copy. For simplicity, we'll start by duplicating the "advNew" chooser portal. After revising the chooser for suitability to injuries, we should have a new portal that looks very similar to the following.
We can now open the file "form_advance.dat" and look into adapting the current advancement mechanism for better use with injuries. Ideally, we could adapt the "advNew" chooser portal for use with injury selection. Unfortunately, the chooser lacks sufficient contextual details to be able to adapt the current portal. This means that we need to duplicate the chooser portal, rename it, and then look to adapt the copy. For simplicity, we'll start by duplicating the "advNew" chooser portal. After revising the chooser for suitability to injuries, we should have a new portal that looks very similar to the following.


<pre>
<pre>
Line 134: Line 134:
       @text = "Choose the New Injury to Add"
       @text = "Choose the New Injury to Add"
       ]]></titlebar>
       ]]></titlebar>
     <description><![CDATA[
     <description/>
      var descript as string
      call Descript
      @text = descript
      ]]></description>
     </chooser_table>
     </chooser_table>
   </portal>
   </portal>
Line 158: Line 154:
     ~setup a width that is generally reasonable
     ~setup a width that is generally reasonable
     width = 475
     width = 475
     ~set the fixed dimensions and render the title template
     ~set the fixed dimensions and render the title template
     template[advTitle].width = width
     template[advTitle].width = width
     perform template[advTitle].render
     perform template[advTitle].render
     ~position the chooser to add a new advancement
     ~position the chooser to add a new advancement
     portal[advNew].left = 20
     portal[advNew].left = 20
     portal[advNew].top = template[advTitle].bottom + 30
     portal[advNew].top = template[advTitle].bottom + 30
     portal[advNew].width = 310
     portal[advNew].width = 310
     ~position the chooser for injuries in the same place
     ~position the chooser for injuries in the same place
     portal[advInjury].left = portal[advNew].left
     portal[advInjury].left = portal[advNew].left
Line 174: Line 173:
     portal[advBoost].top = portal[advNew].top
     portal[advBoost].top = portal[advNew].top
     portal[advBoost].width = portal[advNew].width
     portal[advBoost].width = portal[advNew].width
     ~position the static template in the same place (for non-editable items)
     ~position the static template in the same place (for non-editable items)
     template[advStatic].left = portal[advNew].left
     template[advStatic].left = portal[advNew].left
     template[advStatic].top = portal[advNew].top
     template[advStatic].top = portal[advNew].top
     template[advStatic].width = portal[advNew].width
     template[advStatic].width = portal[advNew].width
     ~hide all of the different portals/templates - we'll show one below
     ~hide all of the different portals/templates - we'll show one below
     template[advStatic].visible = 0
     template[advStatic].visible = 0
Line 183: Line 184:
     portal[advInjury].visible = 0
     portal[advInjury].visible = 0
     portal[advBoost].visible = 0
     portal[advBoost].visible = 0
     ~if this is NOT the newest advancement, show the static template so the user
     ~if this is NOT the newest advancement, show the static template so the user
     ~can't change anything; otherwise, determine which chooser should be shown
     ~can't change anything; otherwise, determine which chooser should be shown
Line 194: Line 196:
       portal[advBoost].visible = 1
       portal[advBoost].visible = 1
       endif
       endif
     ~position the domain template
     ~position the domain template
     template[advDomain].left = portal[advNew].left + 15
     template[advDomain].left = portal[advNew].left + 15
     template[advDomain].top = portal[advNew].bottom + 8
     template[advDomain].top = portal[advNew].bottom + 8
     ~position the notes template
     ~position the notes template
     template[advNotes].left = portal[advNew].left
     template[advNotes].left = portal[advNew].left


     setup fixed dimensions of our templates
     ~setup fixed dimensions of our templates
     template[advDomain].width = portal[advNew].width - template[advDomain].left
     template[advDomain].width = portal[advNew].width - template[advDomain].left
     template[advNotes].width = width - template[advNotes].left * 2
     template[advNotes].width = width - template[advNotes].left * 2
     ~render all remaining templates to finalize their dimensions
     ~render all remaining templates to finalize their dimensions
     perform template[advDomain].render
     perform template[advDomain].render
     perform template[advNotes].render
     perform template[advNotes].render
     ~determine whether the domain template should be shown
     ~determine whether the domain template should be shown
     ~Note: The final test below ensures that we only let the user change the domain
     ~Note: The final test below ensures that we only let the user change the domain
Line 217: Line 223:
         endif
         endif
       endif
       endif
      template[advDomain].visible = isdomain
    template[advDomain].visible = isdomain
 
     ~position the notes at the bottom
     ~position the notes at the bottom
     template[advNotes].top = template[advDomain].bottom + 20
     if (isdomain <> 0) then
      template[advNotes].top = template[advDomain].bottom + 20
    else
      template[advNotes].top = portal[advNew].bottom + 20
      endif
 
     ~determine our overall height
     ~determine our overall height
     height = template[advNotes].bottom
     height = template[advNotes].bottom
     ]]></position>
     ]]></position>
</layout>  
  </layout>  
</pre>
</pre>

Revision as of 12:39, 18 December 2008

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

Overview

Savage Worlds includes the notion of injuries that can be attributed to characters, with corresponding penalties being applied.

Permanent Versus Temporary

There are both temporary and permanent injuries to worry about within Savage Worlds. When a temporary injury is incurred, it can simply be managed on an interim basis by applying a temporary adjustment to the character on the In-Play tab. However, a permanent injury is an entirely different situation, as it has subsequent implications on character advancement. This is because some injuries will confer penalties to attributes, which impacts advancements to skills that are linked to those attributes (i.e. skills may transition from being less than the linked attribute to equal).

The upshot of this is that permanent injuries need to be treated as advancements. That way, any attribute adjustments due to the injuries will properly impact subsequent advancements to skills. There are a number of steps that need to be taken in order to manage permanent injuries via advancements. The following paragraphs will outline each of them.

Injury Component

The first step is to create a new component and component set for handling injuries. We'll call both of them "Injury". In order to incorporate the appropriate handling for advancements, the component set needs to include the "CanAdvance" component. We don't need any special fields or behaviors for injuries, since all of the mechanisms we need are provided by the "CanAdvance" component. However, we define a new component just to be safe, since this will make it easy for us to add new behaviors in the future, if necessary. This yields the following definitions for both the component and component set.

<component
  id="Injury"
  name="Injury"
  autocompset="no">
  </component>

<compset
  id="Injury">
  <compref component="Injury"/>
  <compref component="CanAdvance"/>
  </compset> 

Bootstrapping Hindrances

We can now assess exactly what the behaviors should be for each of the different injuries. A couple of the injuries will consist of nothing more than description text. A number of others need to apply adjustments to attributes and/or other traits (e.g. Pace). And the final ones will automatically confer a hindrance upon the character.

Due to the need to confer hindrances via injuries, we must be sure to modify the "Hindrance" component set to also inherit the "CanAdvance" component. This will ensure that hindrances can be bootstrapped by injuries and properly displaced up to the hero. This results in the revised "Hindrance" component set below.

<compset
  id="Hindrance"
  forceunique="yes">
  <compref component="Hindrance"/>
  <compref component="Ability"/>
  <compref component="Domain"/>
  <compref component="SpecialTab"/>
  <compref component="CanAdvance"/>
  </compset> 

Add Injuries

The next step is the creation of the various injury things. In order to keep them all together, we'll create a new data file exclusively for injuries. We'll name the file "thing_injuries.dat" and we can start with any of the current data files, then we can strip out everything but the outer framework so that we have an initial file that we can put to use.

An example of an injury that applies an attribute adjustment is the "Guts: Broken" injury, which degrades the character's Agility by one die type. When we apply adjustments to attributes and other traits, we'll modify the "trtBonus" field on the trait. This will keep injury adjustments distinct from normal in-play adjustments. The sample thing below reflects a suitable definition for the "Guts: Broken" injury.

<thing
  id="injGutsBrk"
  name="Guts Injury: Broken"
  compset="Injury"
  description="Description goes here">
  <eval phase="Setup" priority="9000">
    <before name="Calc trtFinal"/><![CDATA[
    #traitbonus[attrAgi] -= 1
    ]]></eval>
  </thing>

The other interesting type of injury is one that bootstraps an appropriate hindrance. For example, the "Head: Hideous Scar" injury confers the "Ugly" hindrance to the character. When we bootstrap the hindrance, it is critical that we also ensure that the "Helper.Displace" tag is assigned to the hindrance. Doing so guarantees that the hindrance will be properly displaced onto the character (from the advancement gizmo), which allows it to be clearly visible to the user (e.g. shown on the "Edges" tab). This yields a thing that should look like the following.

<thing
  id="injHeadScr"
  name="Head Injury: Hideous Scar"
  compset="Injury"
  isunique="yes"
  description="Description goes here">
  <bootstrap thing="hinUgly">
    <autotag group="Helper" tag="Displace"/>
    </bootstrap>
  </thing>

Note that one of the above examples is designated as "unique" and the other is not. Appropriate injuries must be designated as unique to avoid duplicate selection by the user. There are some that have an effect which makes it pointless to select the injury more than once (e.g. "Unmentionables"). These injuries should be marked as unique so that they are not able to be selected multiple times. Other injuries that can occur multiple times can omit this designation. You can now either define the remaining injuries yourself or pull them from the completed Savage Worlds data files.

Injury Advancement

Once the injuries are in place, we can setup the handling for the new advancement thing that will be used to add and select permanent injuries. Our advancement can now be configured to force the user to select a new injury. We set the "cost" of the advancement to zero so that adding a permanent injury does not consume a normal advancement slot. The net result is a thing that looks very similar to the following.

<thing
  id="advInjury"
  name="Permanent Injury"
  compset="Advance"
  description="Select this advance to apply a permanent injury to the character. The effects of temporary injuries should be applied via temporary adjustments on the In-Play tab.">
  <fieldval field="advAction" value="Permanent Injury"/>
  <fieldval field="advDynamic" value="component.Injury"/>
  <fieldval field="advCost" value="0"/>
  <tag group="Advance" tag="AddNew"/>
  <child entity="Advance">
    <tag group="Advance" tag="MustChoose"/>
    </child>
  </thing> 

Revise Interface

The one big drawback with this mechanism is that injuries are added just like new skills. This results in the interface presenting injuries for selection as if the user is selecting a new skill. The mechanism looks extremely odd. The solution is to introduce a new type of advancement and adapt the mechanism to work well for injuries as well. For the new advancement type, we'll define a new "Advance.Injury" tag within the file "advancements.core". This way, that we can readily distinguish injury advancements from other advancements. 

We can now open the file "form_advance.dat" and look into adapting the current advancement mechanism for better use with injuries. Ideally, we could adapt the "advNew" chooser portal for use with injury selection. Unfortunately, the chooser lacks sufficient contextual details to be able to adapt the current portal. This means that we need to duplicate the chooser portal, rename it, and then look to adapt the copy. For simplicity, we'll start by duplicating the "advNew" chooser portal. After revising the chooser for suitability to injuries, we should have a new portal that looks very similar to the following.

<portal
  id="advInjury"
  style="chsNormal"
  width="275">
  <chooser_table
    component="none"
    template="advNew"
    prereqtarget="hero"
    candidatepick="advDetails"
    candidatefield="advTagexpr"
    descwidth="350">
    <denytag usehero="yes" container="IsAdvance" thing="IsAdvance"/>
    <autotag group="Helper" tag="Displace"/>
    <autotag group="Advance" tag="Gizmo"/>
    <chosen><![CDATA[
      if (@ispick = 0) then
        @text = "{text ff0000}Select New Injury"
      else
        @text = "New Injury: " & field[name].text
        endif
      ]]></chosen>
    <titlebar><![CDATA[
      @text = "Choose the New Injury to Add"
      ]]></titlebar>
    <description/>
    </chooser_table>
  </portal>

The final thing we need to do is add the new portal to the layout. Once added, we can properly position it within the layout and control its visibility. This can be seen in the revised layout shown below.

<layout
  id="advance">
  <portalref portal="advNew" taborder="20"/>
  <portalref portal="advInjury" taborder="20"/>
  <portalref portal="advBoost" taborder="20"/>
  <templateref template="advTitle" thing="advDetails" taborder="10"/>
  <templateref template="advStatic" thing="advDetails" taborder="20"/>
  <templateref template="advDomain" thing="advDetails" taborder="30"/>
  <templateref template="advNotes" thing="advDetails" taborder="40"/>
  <position><![CDATA[
    ~setup a width that is generally reasonable
    width = 475

    ~set the fixed dimensions and render the title template
    template[advTitle].width = width
    perform template[advTitle].render

    ~position the chooser to add a new advancement
    portal[advNew].left = 20
    portal[advNew].top = template[advTitle].bottom + 30
    portal[advNew].width = 310

    ~position the chooser for injuries in the same place
    portal[advInjury].left = portal[advNew].left
    portal[advInjury].top = portal[advNew].top
    portal[advInjury].width = portal[advNew].width

    ~position the chooser for boosting in the same place
    portal[advBoost].left = portal[advNew].left
    portal[advBoost].top = portal[advNew].top
    portal[advBoost].width = portal[advNew].width

    ~position the static template in the same place (for non-editable items)
    template[advStatic].left = portal[advNew].left
    template[advStatic].top = portal[advNew].top
    template[advStatic].width = portal[advNew].width

    ~hide all of the different portals/templates - we'll show one below
    template[advStatic].visible = 0
    portal[advNew].visible = 0
    portal[advInjury].visible = 0
    portal[advBoost].visible = 0

    ~if this is NOT the newest advancement, show the static template so the user
    ~can't change anything; otherwise, determine which chooser should be shown
    if (container.parent.tagis[Advance.Newest] = 0) then
      template[advStatic].visible = 1
    elseif (container.parent.tagis[Advance.AddNew] <> 0) then
      portal[advNew].visible = 1
    elseif (container.parent.tagis[Advance.Injury] <> 0) then
      portal[advInjury].visible = 1
    else
      portal[advBoost].visible = 1
      endif

    ~position the domain template
    template[advDomain].left = portal[advNew].left + 15
    template[advDomain].top = portal[advNew].bottom + 8

    ~position the notes template
    template[advNotes].left = portal[advNew].left

    ~setup fixed dimensions of our templates
    template[advDomain].width = portal[advNew].width - template[advDomain].left
    template[advNotes].width = width - template[advNotes].left * 2

    ~render all remaining templates to finalize their dimensions
    perform template[advDomain].render
    perform template[advNotes].render

    ~determine whether the domain template should be shown
    ~Note: The final test below ensures that we only let the user change the domain
    ~ when editing the newest advancement and not an old one.
    var isdomain as number
    isdomain = container.parent.tagis[Advance.AddNew]
    if (isdomain <> 0) then
      isdomain = container.firstchild["Advance.Gizmo"].tagis[User.NeedDomain]
      if (isdomain <> 0) then
        isdomain = container.parent.tagis[Advance.Newest]
        endif
      endif
    template[advDomain].visible = isdomain

    ~position the notes at the bottom
    if (isdomain <> 0) then
      template[advNotes].top = template[advDomain].bottom + 20
    else
      template[advNotes].top = portal[advNew].bottom + 20
      endif

    ~determine our overall height
    height = template[advNotes].bottom
    ]]></position>
  </layout>