Difference between revisions of "Character Sheet Phase 3 (Savage)"

From HLKitWiki
Jump to: navigation, search
(Table Portal)
(The Template)
 
(6 intermediate revisions by the same user not shown)
Line 179: Line 179:
 
We need a total of six portals in order to present all the information we selected for inclusion. The portals for showing the point cost and range will use a script that first checks the print-specific field and otherwise defaults to the normal field. The portal for duration will work the same way, with added handling for including any maintenance cost. The remaining portals will simply show the contents of an appropriate field.
 
We need a total of six portals in order to present all the information we selected for inclusion. The portals for showing the point cost and range will use a script that first checks the print-specific field and otherwise defaults to the normal field. The portal for duration will work the same way, with added handling for including any maintenance cost. The remaining portals will simply show the contents of an appropriate field.
  
We'll put the name on the left and then align the four fields across the top line within columns. The summary text will be placed beneath the line of fields, spanning the same width as the fields. If the name is too large to fit, we'll allow it span two lines, since we already need at least two lines of output. If the name still doesn't fit, will use the built-in "sizetofit" target reference to have HL incrementally scale the font size downward until the name actually fits. We can then center the name vertically. Putting all this together yields the template shown below.
+
We'll put the name on the left and then align the four fields across the top line within columns. The summary text will be placed beneath the line of fields, spanning the same width as the fields, and it can be more than one line in height. If the name is too large to fit, we'll allow it span two lines, since we already need at least two lines of output. Our total height will be determined after we position everything. If the name still doesn't fit, will use the built-in "sizetofit" target reference to have HL incrementally scale the font size downward until the name actually fits. We can then center the name vertically. Putting all this together yields the template shown below.
  
 
<pre>
 
<pre>
Line 259: Line 259:
 
     style="outPower">
 
     style="outPower">
 
     <output_label
 
     <output_label
       field="summary">
+
       field="summary"
 +
      ismultiline="yes">
 
       </output_label>
 
       </output_label>
 
     </portal>
 
     </portal>
  
 
   <position><![CDATA[
 
   <position><![CDATA[
    ~our height is based on the tallest portal on the top line plus our text area
 
    height = portal[name].height + portal[summary].height + 2
 
    if (issizing <> 0) then
 
      done
 
      endif
 
 
 
     ~position all portals with the same baseline as the name; since they use a
 
     ~position all portals with the same baseline as the name; since they use a
 
     ~smaller font, they will have a smaller height, so centering them will have
 
     ~smaller font, they will have a smaller height, so centering them will have
Line 292: Line 287:
 
     ~size the trappings portal to whatever space is left
 
     ~size the trappings portal to whatever space is left
 
     portal[trapping].width = width - portal[trapping].left
 
     portal[trapping].width = width - portal[trapping].left
 
    ~size the name to fit the available space, then realign to baseline after shrink
 
    perform portal[name].sizetofit[36]
 
    perform portal[name].autoheight
 
    perform portal[name].centervert
 
  
 
     ~size the summary portal to span the area excluding the name
 
     ~size the summary portal to span the area excluding the name
Line 305: Line 295:
 
     ~position the summary portal beneath the other line of fields
 
     ~position the summary portal beneath the other line of fields
 
     perform portal[summary].alignrel[ttob,points,2]
 
     perform portal[summary].alignrel[ttob,points,2]
 +
 +
    ~our height is the base of the summary portal
 +
    height = portal[summary].bottom
 +
 +
    ~size the name to fit the available space, then re-center after shrink
 +
    perform portal[name].sizetofit[36]
 +
    perform portal[name].autoheight
 +
    perform portal[name].centervert
 
     ]]></position>
 
     ]]></position>
  
Line 326: Line 324:
 
This looks very clear and readable. The amount of space available for showing the trappings of a power are small, but it's enough if the user is succinct with his descriptions of the trappings. So we're good to go for showing the powers.
 
This looks very clear and readable. The amount of space available for showing the trappings of a power are small, but it's enough if the user is succinct with his descriptions of the trappings. So we're good to go for showing the powers.
  
====Adding a Header====
+
====Adding a Custom Header====
  
===Armor===
+
It's now time to add the custom header to the table portal. Our header needs to label each column of information within the table. In order to easily synchronize the positions of the column headers with the actual data being output, we'll make use of a dual-purpose template. A dual-purpose template is when the same template is used for both the contents of the table and the header, and there are a number of details involved in making it all work.
  
===Weapons===
+
The first step is to revise our table portal properly. We need to add a new "headertemplate" attribute that references the same template used as a the show template. We also need to eliminate the "headertitle" element, since we'll be defining our own header. This yields the following new portal.
  
===Adjustments===
+
<pre>
 +
<portal
 +
  id="oPower"
 +
  style="outNormal">
 +
  <output_table
 +
    component="Power"
 +
    showtemplate="oPowerPick"
 +
    headertemplate="oPowerPick"
 +
    varyheight="yes">
 +
    </output_table>
 +
  </portal>
 +
</pre>
  
===Injuries and Health===
+
Next up is adding the appropriate portals to the template that will be used for the header. We need one portal for the banner that will span the full width of the table portal. We also need four additional portals for the various labels at the top of each column. For the column headers, the Skeleton files provide the "outHeader" style that is generally an excellent choice. However, we're using a much smaller font for the various fields themselves, so we should use a similar font for the headers. This results in the following new portal definitions.
  
===Gear===
+
<pre>
 +
<portal
 +
  id="hdrtitle"
 +
  style="outTitle"
 +
  isheader="yes">
 +
  <output_label
 +
    text="Arcane Powers">
 +
    </output_label>
 +
  </portal>
 +
 
 +
<portal
 +
  id="hdrpoints
 +
  style="outTiny"
 +
  isheader="yes">
 +
  <output_label
 +
    text="Cost">
 +
    </output_label>
 +
  </portal>
 +
 
 +
<portal
 +
  id="hdrrange"
 +
  style="outTiny"
 +
  isheader="yes">
 +
  <output_label
 +
    text="Range">
 +
    </output_label>
 +
  </portal>
 +
 
 +
<portal
 +
  id="hdrlength"
 +
  style="outTiny"
 +
  isheader="yes">
 +
  <output_label
 +
    text="Dur/Maint">
 +
    </output_label>
 +
  </portal>
 +
 
 +
<portal
 +
  id="hdrtrap"
 +
  style="outTiny"
 +
  isheader="yes">
 +
  <output_label
 +
    text="Trappings">
 +
    </output_label>
 +
  </portal>
 +
</pre>
 +
 
 +
The final step is to define a Header script via the "header" element. This script is similar to the Position script, except that it's intended for positioning the various portals within the header. It is invoked after the normal Position script, which allows it to retrieve the positions of the various normal portals for the template and easily position the header portals in relation to them. Consequently, we can position each of the labels directly where they belong without any special hoop jumping. This yields the Header script below.
 +
 
 +
<pre>
 +
<header><![CDATA[
 +
  ~our header height is the title plus a gap plus the header text
 +
  height = portal[hdrtitle].height + 2 + portal[hdrpoints].height
 +
  if (issizing <> 0) then
 +
    done
 +
    endif
 +
 
 +
  ~our title spans the entire width of the template
 +
  portal[hdrtitle].width = width
 +
 
 +
  ~each of our header labels has the same width as the corresponding data beneath
 +
  portal[hdrpoints].width = portal[points].width
 +
  portal[hdrrange].width = portal[range].width
 +
  portal[hdrlength].width = portal[duration].width
 +
  portal[hdrtrap].width = portal[trapping].width
 +
 
 +
  ~center each header label on the corresponding data beneath
 +
  perform portal[hdrpoints].centeron[horz,points]
 +
  perform portal[hdrrange].centeron[horz,range]
 +
  perform portal[hdrlength].centeron[horz,duration]
 +
  perform portal[hdrtrap].centeron[horz,trapping]
 +
 
 +
  ~align all header labels at the bottom of the header region
 +
  perform portal[hdrpoints].alignedge[bottom,0]
 +
  perform portal[hdrrange].alignedge[bottom,0]
 +
  perform portal[hdrlength].alignedge[bottom,0]
 +
  perform portal[hdrtrap].alignedge[bottom,0]
 +
  ]]></header>
 +
</pre>
 +
 
 +
We can now reload the files and see how our header looks. It looks great, although we should probably include the total number of arcane power points possessed by the character within the title. That way, the total power points is conveniently grouped next to all the powers for the users. We can accomplish this by changing the "hdrtitle" portal to use a script and synthesize the appropriate information via the script. The revised portal should look similar to the one below.
 +
 
 +
<pre>
 +
<portal
 +
  id="hdrtitle"
 +
  style="outTitle"
 +
  isheader="yes">
 +
  <output_label>
 +
    <labeltext><![CDATA[
 +
      @text = "Arcane Powers (" & #trkmax[trkPower] & " Points)"
 +
      ]]></labeltext>
 +
    </output_label>
 +
  </portal>
 +
</pre>

Latest revision as of 17:32, 8 February 2009

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 

Overview

In this phase of developing the character sheet, we'll begin work on the right column of the sheet. This side is a bit more complex than the left side, containing both detailed sections (e.g. powers, weapons, and armor) and some interesting relationships between the different sections.

Logos

At the top of the right column of the character are the logos for both HL and the game system. These are handled automatically in the Skeleton files and there is nothing we need to do. If we wanted to stack the logos instead of placing them side-by-side, we could do that by simply configuring the orientation within the Position script for the sheet. However, we're trying to keep things compact in this character sheet, so we'll stick with the default behavior.

Eliminate Portrait Handling

The first thing we need to do for the right column is eliminate the character portrait. The Skeleton files include the output of a character portrait at the top if a portrait is specified for the character. In the interest of squeezing all the important information into the one-page sheet, we'll remove it from the sheet.

Unlike the way we handled the "Health and Power" material previously, we won't completely delete the logic for handling character portraits. We'll likely want to include the portrait in a different character layout in the future, at which point we'll want to use the logic that's provided by the Skeleton files. For this sheet, we'll simply eliminate the reference to the portrait, thereby removing it from this particular sheet without actually deleting it from the data files.

All of the logic for the portrait display resides within the "oPortrait" template. In order to remove the portrait from the character sheet, we simply need to eliminate the reference to this template from the "oRightSide" layout. This consists of deleting the "templateref" and the line of code from the Position script that places the template. Once we do that, the portrait is no longer included in the character sheet.

Edges

The next thing we need to worry about on the right side is edges. At this point, you're probably wondering why we're concerned with edges on the right side, since we already output them on the left side. The reason is that a highly complex character might not be able to fit all the edges on the left side. If there are more than will actually fit in the space at the bottom of the left column, only those that fit will be output. If that occurs, we still need to output the rest. One option for handling the extra edges is to use the second "spill-over" page for that purpose. However, edges are very important and should ideally be shown on the main character sheet, so we should output any extras at the top of the right column.

Outputting any remaining edges is actually quite easy. When generating the character sheet, HL automatically keeps track of which picks are actually output to the sheet. Each pick is output only once, which eliminates the need for you to worry about which edges have been output and which ones still need to be output. By simply re-using the existing edges table on the right side, any edges that have not yet been output will be included.

If all of the edges are properly output on the left side, then the table of edges on the right will be empty. By using the auto-place mechanism, an empty table is completely omitted (automatically) when rendering the character sheet. So the behavior we want is handled for us automatically.

This means that all we need to do is reference the existing edges table from within the "oRightSide" layout. Doing that requires adding a "portalref" element for the table and placing the portal properly. The revised layout should look like the following.

<layout
  id="oRightSide">
  <portalref portal="oEdge2"/>
  <portalref portal="oGear"/>
  <position><![CDATA[
    ~position the various tables appropriately
    perform portal[oEdge2].autoplace
    perform portal[oGear].autoplace

    ~our layout height is the extent of the elements within
    height = autotop
    ]]></position>
  </layout>

Add a bunch of edges to the character so that it will run out of room and need to spill over to the next column, then preview the character again. Everything works as intended for edges, except we've uncovered a new problem. The table of edges on the left extends down to the bottom of the page instead of being limited to the top of the validation region. By default, the "oLeftSide" layout will extend to the bottom of the page. In order to impose the necessary limit, we need to set the bottom of the layout before we render it. This consists of adding a single line of code immediately prior to the "render" statement within the Position script of the sheet, so the block of code that configures and renders the left side layout should look like below.

~position the leftside layout in the upper left corner
layout[oLeftSide].width = colwidth
layout[oLeftSide].height = extent - layout[oLeftSide].top
perform layout[oLeftSide].render

If we wanted to be truly paranoid, we could add spill-over handling for hindrances. Theoretically, if a character has enough skills and hindrances, the list of hindrances could overrun the available space on the left side. However, the feasibility of such a character in practice is unrealistic, so we won't bother.

Arcane Powers

Moving downward on the right side, we have arcane powers next. Powers need to be handled a lot like weapons are handled in the provided Skeleton files. There are a number of fields that are best placed in columns within the table. As such, we'll want to use the same approach as with the weapons, wherein we use the same template as both a header and the contents of each item. This makes it possible to easily align the header elements with the contents of the table.

For the moment, though, we're just going to start with a normal table that does not possess a special header. The reason is that trying to setup everything for both the header and contents at the same time results in a more complex operation. When both are done at the same time, you have to get everything into place before you can begin testing and refining. By implementing everything in stages, we'll simplify the process for ourselves and make it possible to get the contents in good shape before we worry about the header.

There is a great deal of information that should ideally be output for arcane powers. In fact, there's so much information that it would be ridiculous to try and squeeze it all into a single line. We need to show the name, point cost, range, duration, maintenance, trappings, and description for each power. However, we're striving to keep everything on a single sheet, so we have to keep things as compact as possible. In order to achieve that goal, we're going to need our starting information to be more compact. The contents of each field that we've entered for display to the user within the UI are too large to fit in a compact space.

Fields for Output

The first thing we need to do is define a new set of fields for powers that will contain shortened versions for display on the character sheet. If these fields are non-empty, we'll use the contents, else we'll use the standard field, assuming that it's already short enough for our purposes. We'll need new fields for the point cost, range, duration, and maintenance. Open the file "miscellaneous.str" and locate the "Power" component. Copy the four existing fields for this information and give them new ids to indicate they are for use with printing. When you're done, the new fields should look similar to those below.

<field
  id="powPrtPts"
  name="Power Point Cost"
  type="static"
  maxlength="25">
  </field>

<field
  id="powPrtRng"
  name="Range"
  type="static"
  maxlength="25">
  </field>

<field
  id="powPrtLen"
  name="Duration"
  type="static"
  maxlength="25">
  </field>

<field
  id="powPrtMain"
  name="Maintenance"
  type="static"
  maxlength="25">
  </field>

Now open the file "thing_arcane.dat" and go through all of the powers. Any time you see one of the four fields with lengthy contents, define the corresponding new field with a suitably shortened version. Keep it all as brief as possible so that we can fit it all into the table for display on the character sheet.

Design the Table

Returning to the character sheet, we need to determine how the contents are going to look for each power. We already decided that we won't be able to squeeze everything into a single line. Now the question is whether we can limit ourselves to two lines or if we need more. So we should probably grab a piece of paper and sketch out how everything will look.

After sketching out various ideas, we'll settle on one that is compact and also includes all the most important details for each power. We'll put the name on the left in a moderate size font. We'll have a line of information that provides the point cost, range, duration, maintenance cost, and trappings. Beneath that line, we'll provide the summary information about how the power works. If a character has a dozen powers, this approach will consume a good amount of space. However, most characters with powers only have a handful of them, so this should generally work out quite well.

Table Portal

With the layout mapped out, the next step is to create a new table portal for our powers. This portal should look like many of the ones we've already defined. The key distinction is that the summary may end up being more than one line in length for some powers. As such, we need to designate the table as containing variable height items. The resulting portal should look like the following.

<portal
  id="oPower"
  style="outNormal">
  <output_table
    component="Power"
    showtemplate="oPowerPick"
    varyheight="yes">
    <headertitle><![CDATA[
      @text = "Arcane Powers"
      ]]></headertitle>
    </output_table>
  </portal>

Once defined, we need to integrate the new table portal into the appropriate layout. As we've done before, this entails adding a "portalref" element for the portal and then placing the portal via the Position script. The revised "oRightSide" layout should look like below.

<layout
  id="oRightSide">
  <portalref portal="oEdge"/>
  <portalref portal="oPower"/>
  <portalref portal="oGear"/>
  <position><![CDATA[
    ~position the various tables appropriately
    perform portal[oEdge].autoplace
    perform portal[oPower].autoplace
    perform portal[oGear].autoplace

    ~our layout height is the extent of the elements within
    height = autotop
    ]]></position>
  </layout>

New Styles Required

We now shift our focus to the template used to display each individual power. In order to fit all the information we plan, we're going to need to use a rather small font. Looking through the various styles provided in the file "styles_output.aug", there aren't any that will serve our needs. So we'll need to create a new style that uses a small font. We'll actually need two new styles, with one centering the text and the other aligning the text to the left. This means we need to define the font separately for use by both styles. These new definition should look like the ones below.

<resource
  id="ofnttiny">
  <font
    face="Arial"
    size="32">
    </font>
  </resource>

<style
  id="outTiny">
  <style_output
    textcolor="000000"
    font="ofnttiny"
    alignment="center">
    </style_output>
  </style>

<style
  id="outTinyLt">
  <style_output
    textcolor="000000"
    font="ofnttiny"
    alignment="left">
    </style_output>
  </style>

The Template

We need a total of six portals in order to present all the information we selected for inclusion. The portals for showing the point cost and range will use a script that first checks the print-specific field and otherwise defaults to the normal field. The portal for duration will work the same way, with added handling for including any maintenance cost. The remaining portals will simply show the contents of an appropriate field.

We'll put the name on the left and then align the four fields across the top line within columns. The summary text will be placed beneath the line of fields, spanning the same width as the fields, and it can be more than one line in height. If the name is too large to fit, we'll allow it span two lines, since we already need at least two lines of output. Our total height will be determined after we position everything. If the name still doesn't fit, will use the built-in "sizetofit" target reference to have HL incrementally scale the font size downward until the name actually fits. We can then center the name vertically. Putting all this together yields the template shown below.

<template
  id="oPowerPick"
  name="Output Powers Table"
  compset="Power"
  marginvert="2">

  <portal
    id="name"
    style="outMedLt">
    <output_label
      field="name">
      </output_label>
    </portal>

  <portal
    id="points"
    style="outTiny">
    <output_label>
      <labeltext><![CDATA[
        if (field[powPrtPts].isempty = 0) then
          @text = field[powPrtPts].text
        else
          @text = field[powPoints].text
          endif
        ]]></labeltext>
      </output_label>
    </portal>

  <portal
    id="range"
    style="outTiny">
    <output_label>
      <labeltext><![CDATA[
        if (field[powPrtRng].isempty = 0) then
          @text = field[powPrtRng].text
        else
          @text = field[powRange].text
          endif
        ]]></labeltext>
      </output_label>
    </portal>

  <portal
    id="duration"
    style="outTiny">
    <output_label>
      <labeltext><![CDATA[
        var maint as string
        if (field[powPrtMain].isempty = 0) then
          maint = field[powPrtMain].text
        else
          maint = field[powMaint].text
          endif
        if (field[powPrtLen].isempty = 0) then
          @text = field[powPrtLen].text
        else
          @text = field[powLength].text
          endif
        if (empty(maint) = 0) then
          @text &= " (" & maint & ")"
          endif
        ]]></labeltext>
      </output_label>
    </portal>

  <portal
    id="trapping"
    style="outTinyLt">
    <output_label
      field="powTraps">
      </output_label>
    </portal>

  <portal
    id="summary"
    style="outPower">
    <output_label
      field="summary"
      ismultiline="yes">
      </output_label>
    </portal>

  <position><![CDATA[
    ~position all portals with the same baseline as the name; since they use a
    ~smaller font, they will have a smaller height, so centering them will have
    ~them appear to float up relative to the name
    perform portal[points].alignrel[btob,name,0]
    perform portal[range].alignrel[btob,name,0]
    perform portal[duration].alignrel[btob,name,0]
    perform portal[trapping].alignrel[btob,name,0]

    ~establish suitable fixed widths for the various columns of data
    portal[name].width = 300
    portal[points].width = 100
    portal[range].width = 150
    portal[duration].width = 200

    ~position everything horizontally
    perform portal[points].alignrel[ltor,name,5]
    perform portal[range].alignrel[ltor,points,5]
    perform portal[duration].alignrel[ltor,range,5]
    perform portal[trapping].alignrel[ltor,duration,5]

    ~size the trappings portal to whatever space is left
    portal[trapping].width = width - portal[trapping].left

    ~size the summary portal to span the area excluding the name
    portal[summary].left = portal[points].left
    portal[summary].width = width - portal[summary].left
    perform portal[summary].autoheight

    ~position the summary portal beneath the other line of fields
    perform portal[summary].alignrel[ttob,points,2]

    ~our height is the base of the summary portal
    height = portal[summary].bottom

    ~size the name to fit the available space, then re-center after shrink
    perform portal[name].sizetofit[36]
    perform portal[name].autoheight
    perform portal[name].centervert
    ]]></position>

  </template>

Reloading the data files and previewing the character gives us a chance to review what we've got. The name and columns of data look pretty good, but the summary text beneath results in a rather jumbled look. If we output the summary text in a slightly fainter stroke and set it off against a grey background, everything would look significantly better. So we'll define a new style like the one below and use it with the summary text.

<style
  id="outPower">
  <style_output
    textcolor="404040"
    backcolor="e8e8e8"
    font="ofnttiny"
    alignment="left">
    </style_output>
  </style>

This looks very clear and readable. The amount of space available for showing the trappings of a power are small, but it's enough if the user is succinct with his descriptions of the trappings. So we're good to go for showing the powers.

Adding a Custom Header

It's now time to add the custom header to the table portal. Our header needs to label each column of information within the table. In order to easily synchronize the positions of the column headers with the actual data being output, we'll make use of a dual-purpose template. A dual-purpose template is when the same template is used for both the contents of the table and the header, and there are a number of details involved in making it all work.

The first step is to revise our table portal properly. We need to add a new "headertemplate" attribute that references the same template used as a the show template. We also need to eliminate the "headertitle" element, since we'll be defining our own header. This yields the following new portal.

<portal
  id="oPower"
  style="outNormal">
  <output_table
    component="Power"
    showtemplate="oPowerPick"
    headertemplate="oPowerPick"
    varyheight="yes">
    </output_table>
  </portal>

Next up is adding the appropriate portals to the template that will be used for the header. We need one portal for the banner that will span the full width of the table portal. We also need four additional portals for the various labels at the top of each column. For the column headers, the Skeleton files provide the "outHeader" style that is generally an excellent choice. However, we're using a much smaller font for the various fields themselves, so we should use a similar font for the headers. This results in the following new portal definitions.

<portal
  id="hdrtitle"
  style="outTitle"
  isheader="yes">
  <output_label
    text="Arcane Powers">
    </output_label>
  </portal>

<portal
  id="hdrpoints
  style="outTiny"
  isheader="yes">
  <output_label
    text="Cost">
    </output_label>
  </portal>

<portal
  id="hdrrange"
  style="outTiny"
  isheader="yes">
  <output_label
    text="Range">
    </output_label>
  </portal>

<portal
  id="hdrlength"
  style="outTiny"
  isheader="yes">
  <output_label
    text="Dur/Maint">
    </output_label>
  </portal>

<portal
  id="hdrtrap"
  style="outTiny"
  isheader="yes">
  <output_label
    text="Trappings">
    </output_label>
  </portal>

The final step is to define a Header script via the "header" element. This script is similar to the Position script, except that it's intended for positioning the various portals within the header. It is invoked after the normal Position script, which allows it to retrieve the positions of the various normal portals for the template and easily position the header portals in relation to them. Consequently, we can position each of the labels directly where they belong without any special hoop jumping. This yields the Header script below.

<header><![CDATA[
  ~our header height is the title plus a gap plus the header text
  height = portal[hdrtitle].height + 2 + portal[hdrpoints].height
  if (issizing <> 0) then
    done
    endif

  ~our title spans the entire width of the template
  portal[hdrtitle].width = width

  ~each of our header labels has the same width as the corresponding data beneath
  portal[hdrpoints].width = portal[points].width
  portal[hdrrange].width = portal[range].width
  portal[hdrlength].width = portal[duration].width
  portal[hdrtrap].width = portal[trapping].width

  ~center each header label on the corresponding data beneath
  perform portal[hdrpoints].centeron[horz,points]
  perform portal[hdrrange].centeron[horz,range]
  perform portal[hdrlength].centeron[horz,duration]
  perform portal[hdrtrap].centeron[horz,trapping]

  ~align all header labels at the bottom of the header region
  perform portal[hdrpoints].alignedge[bottom,0]
  perform portal[hdrrange].alignedge[bottom,0]
  perform portal[hdrlength].alignedge[bottom,0]
  perform portal[hdrtrap].alignedge[bottom,0]
  ]]></header>

We can now reload the files and see how our header looks. It looks great, although we should probably include the total number of arcane power points possessed by the character within the title. That way, the total power points is conveniently grouped next to all the powers for the users. We can accomplish this by changing the "hdrtitle" portal to use a script and synthesize the appropriate information via the script. The revised portal should look similar to the one below.

<portal
  id="hdrtitle"
  style="outTitle"
  isheader="yes">
  <output_label>
    <labeltext><![CDATA[
      @text = "Arcane Powers (" & #trkmax[trkPower] & " Points)"
      ]]></labeltext>
    </output_label>
  </portal>