A Tab for Skills (Savage)

From HLKitWiki
Revision as of 06:58, 31 January 2009 by Rob (talk | contribs) (→‎Delete Old Skills Table)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search

Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through 

Overview

In Savage Worlds, skills are a bit more complex that within the Skeleton files. For example, skills like "Knowledge" require that the user be able to specify a suitable domain. This requires more horizontal space than the half-width provided on the "Basics" panel. So we need to move skills to their own tab. This section walks you through the process.

Re-Purpose Existing Tab

The Skeleton files provide an "Abilities" tab that we don't need for Savage Worlds, so we'll re-purpose that tab as the new "Skills" tab. We start by renaming the file "tab_abilities.dat" to "tab_skills.dat". Then we need to open the file "tab_skills.dat" and convert the contents over to behave as a "Skills" tab.

At the bottom of the file, the "abilities" panel is defined. Change the id to "skills" and the name to "Skills". The Live tag expression must be changed to key on a "skills" tag (that we'll define in just a moment). And the layout referenced should be "skills" instead of "abilities".

Just above the panel definition is the layout definition. Change the id from "abilities" to "skills". The portal referenced should be changed to "skSkills". This entails that uses of the portal id within the Position script must also be changed to "skSkills" (there are three of them).

Moving to the top of the file, there is the table portal that needs to have its unique id changed to "skSkills". Within this portal, a variety of other facets must also be changed. The component must be "Skill" to show skills. The "showtemplate" should be changed to "skPick", and the "addthing" should be changed to "resSkill". Within the various scripts for the portal, change references from "Abilities" to "Skills" and uses of the "resAbility" resource to "resSkill".

Beneath the table portal is the template that needs to have its unique id changed to "skPick". The component set must be changed to "Skill" and the name should be changed to something appropriate. All other facets of the template can remain unchanged for now, as they are generic and will work fine for skills.

Once all these changes have been made, you should end up with something that looks like the following:

<portal
  id="skSkills"
  style="tblNormal">
  <table_dynamic
    component="Skill"
    showtemplate="skPick"
    choosetemplate="SimpleItem"
    fixedlast="yes"
    addpick="resSkill">
    <candidate>!Hide.Skill</candidate>
    <titlebar><![CDATA[
      @text = "Add a Special Skill - " & hero.child[resSkill].field[resSummary].text
      ]]></titlebar>
    <headertitle><![CDATA[
      @text = "Skills: " & hero.child[resSkill].field[resSummary].text
      ]]></headertitle>
    <additem><![CDATA[
      ~if we're in advancement mode, we've been frozen, so display accordingly
      if (state.iscreate = 0) then
        @text = "{text a0a0a0}Add/Increase Skills Via Advances Tab"
        done
        endif

      ~get the color-highlighted "add" text
      @text = field[resAddItem].text
      ]]></additem>
    </table_dynamic>
  </portal>

<template
  id="skPick"
  name="Skill Pick"
  compset="Skill"
  marginhorz="3"
  marginvert="2">

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

  <portal
    id="info"
    style="actInfo">
    <action
      action="info">
      </action>
    <mouseinfo mousepos="middle+above"/>
    </portal>

  <portal
    id="delete"
    style="actDelete"
    tiptext="Click to delete this item">
    <action
      action="delete">
      </action>
    </portal>

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

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

    ~position our tallest portal at the top
    portal[info].top = 0

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

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

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

    ~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[info].left - 5)

    ~if the ability is auto-added, change its font to indicate that fact
    if (candelete = 0) then
      perform portal[name].setstyle[lblAuto]
      endif
    ]]></position>
  </template>

<layout
  id="skills">
  <portalref portal="skSkills" taborder="10"/>
  <position><![CDATA[
    ~freeze our table in advancement mode to disable adding new choices
    ~Note: All freezing must be done *before* any positioning is performed.
    if (state.iscreate = 0) then
      portal[skSkills].freeze = 1
      endif

    ~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>

<panel
  id="skills"
  name="Skills"
  marginhorz="5"
  marginvert="5"
  order="120">
  <live>!HideTab.skills</live>
  <layoutref layout="skills"/>
  <position><![CDATA[
    ]]></position>
  </panel>

Getting Everything to Work

If we were to re-compile the data files at this point, we would have an error that still needs to be resolved, so we'll address it now. Open the "tags.1st" file and locate the "HideTab" tag group. The "abilities" tag needs to be changed to "skills", since that's what we now key on within the Live tag expression of the panel.

There are also a few panel linkages on components that are currently tied to the "abilities" tab as a placeholder. These linkages are for edges and the like that have no relationship to the "Skills" tab, but we need to change them to something so the compiler can resolve everything. We can change the linkages to "skills", "basics", or any other tab, as long as the tab exists. We'll change it to "basics" as something safe for the time being. We'll fix it once we have a suitable tab in place to hook these components up to.

We should now be able to re-load our data files and see our new "Skills" tab appear. It isn't working correctly yet, but we'll take care of that in the following sections.

Internal Mechanics Changes

We now need to make some internal changes to mechanics in order to get skills behaving properly. The first thing to address is the fact that all skills are being automatically added to every character. In Savage Worlds, skills are purchased, so each character should start out with zero skills. Open the file "bootstrap.1st" and look for the various "enmasse" entries. Each of these entries automatically adds all things that satisfy a particular tag expression. One entry specifies all things with the "component.Skill" tag, which will add all skills. Delete this "enmasse" entry.

The next detail we need to address is that there may be some skills that the user is not allowed to purchase directly and that are automatically added (e.g. via edges, powers, etc.). This means that we need a method for designating some skills as hidden from the user for selection. A general mechanism for hiding things/picks is already defined via the "Hide" tag group, and we already have a tag for hiding skills in the "Hide.Skill" tag. We can assign this tag to any things that should be hidden from the user. The Candidate tag expression of the table portal will preclude such things from being shown for selection.

If errors are identified with any skills, it would be helpful to the user for the "Skills" tab to appear in red, drawing attention to the problem for easy correction. To simplify this, we need to associate all skills with the new tab. This can be accomplished by linking the "Skill" component to the new "skills" panel via a "panellink" attribute.

<component
  id="Skill"
  name="Skill"
  autocompset="no"
  panellink="skills">
  ...
  </component>

The final detail is to eliminate the special validation thing that is defined for abilities. You'll find this thing in the file "thing_validate.dat" and it will have the unique id "valAbility". Since skills are governed by points, we don't need anything equivalent for Savage Worlds and can simply delete the thing.

Enhancing the Skill Template

The current "skPick" template within the file "tab_skills.dat" contains only generic mechanisms. We now need to enhance the template to include the important facets of skills that are currently missing. The first thing we need to add is an incrementer that will allow the user to adjust the die type for a given skill. We'll use the same incrementer style that is already being used for attributes. So we add the portal shown below.

<portal
  id="value"
  style="incrSimple">
  <incrementer
    field="trtUser">
    </incrementer>
  <mouseinfo mousepos="middle+above"><![CDATA[
    if (hero.tagis[mode.creation] = 0) then
      @text = "Skills must be modified via the Advances tab once the character is locked for play."
    elseif (autonomous = 0) then
      @text = "This trait has been improved via the Advances tab and cannot be modified further from here."
    else
      @text = "Allocate points to this skill by clicking on the arrows to increase/decrease the number of points assigned."
      endif
    ]]></mouseinfo>
  </portal>

We also need to allow the user to specify a suitable domain for skills that require one, such as the "Knowledge" skill. We need an edit portal in order to let the user enter the name of the domain, plus we need a label portal to identify the empty edit portal as being for the domain. We want the label portal to be in a smaller font and softer color than the name of the skill itself, so we choose a suitable style that reflects that behavior. The result is that we add the two portals shown below.

<portal
  id="lbldomain"
  style="lblSecond">
  <label
    text="Domain:">
    </label>
  </portal>

<portal
  id="domain"
  style="editNormal">
  <edit
    field="domDomain">
    </edit>
  </portal>

Now that the portals have been added to the template, we need to properly position everything. We want to put the incrementer on the far left, with the name of the skill just to the right of the incrementer. If the skill requires a domain, we'll place it just to the right of the skill name, but we need to hide the portals associated with the domain if the skill does not require a domain. By default, all of the portals are always shown for every skill, so we control the visibility of each portal to ensure the contents being displayed reflect the correct behaviors for each skill. We can determine whether a skill needs a domain based on the presence of the "User.NeedDomain" tag. Putting it all together yields the following new Position script.

~set up our height based on our tallest portal
height = portal[info].height

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

~position our tallest portal at the top
portal[info].top = 0

~position the other portals vertically
perform portal[name].centervert
perform portal[domain].centervert
perform portal[lbldomain].centervert
perform portal[value].centervert
perform portal[delete].centervert

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

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

~position the incrementer on the left
portal[value].left = 0

~position the name next to the incrementer
perform portal[name].alignrel[ltor,value,10]

~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[info].left - portal[name].left - 10)

~otherwise, position the domain portals next to the name
else
  perform portal[lbldomain].alignrel[ltor,name,20]
  perform portal[domain].alignrel[ltor,lbldomain,5]
  portal[domain].width = 150
  endif 

~if the ability is auto-added, change its font to indicate that fact
if (candelete = 0) then
  perform portal[name].setstyle[lblAuto]
  endif

Problem with Domains

Unfortunately, there is a problem with the way we're handling domains. The "Domain" component automatically integrates the domain into the name of the pick. This means that entering a domain of "foo" for a "Knowledge" skill will change the name shown for the pick to "Knowledge: foo". Since we are also showing an edit portal for the domain, this behavior will appear wrong to the user.

We need to show a name for the pick that excludes the domain. For that, we'll need to utilize the basic name for the pick. That name is accessible via the "thingname" field, so we need to change the "name" portal to reference the "thingname" field instead of the full name. Once we do this, everything works nicely.

Delete Old Skills Table

The final task we need to complete is to delete the old "Skills" table from the "Basics" tab. Open the file "tab_basics.dat" and locate the "baSkill" table portal. This table of skills is no longer applicable, so delete it. Now scroll further down to the "baSklPick" template. This template is only used by the table portal we just deleted, so it can also be deleted. Finally, we need to delete the table from the "basics" layout. This amounts to deleting the appropriate "portalref" element and deleting the positioning details for the portal from the Position script.

NOTE! Remember that you can always comment out sections instead of deleting them until you're certain what can be safely deleted.