Revise "Basics" Tab (Savage)
Now would be an excellent time for us to revise the "Basics" tab to contain the material we want it to show. In general, that material consists of the proper set of derived traits for Savage Worlds and details relating to the creation and advancement of the character, such as rank, XP, and unallocated resources.
The first thing we'll adjust visually is move the derived traits list up to the right instead of below the attributes. Derived traits are important and it makes sense to feature them prominently on the tab, so we'll put them next to the attributes. We'll start by moving the table portal within the Position script of the layout. This is as simple as revising the script to position the portal as shown below.
portal[baTrait].width = portal[baAttrib].width portal[baTrait].left = width - portal[baTrait].width portal[baTrait].height = height
We now need to prune the list to only show the derived traits that we want for Savage Worlds. One option is to actually delete all the extraneous derived traits, but they are hooked into the Skeleton logic and will require some work to eliminate, so we'll simply hide them for the time being and eliminate them fully later one. The standard technique for this type of approach is to define a new tag in the "Hide" group, assign it to traits that we don't want shown, and then ignore those within the table. That's the technique we'll use. We start by adding a "Trait" tag to the "Hide" group, then we assign that tag to all derived traits that were inherited from the Skeleton files.
<tag group="Hide" tag="Trait"/>
Next, we modify the "baTrait" table portal to have a List tag expression that excludes all traits with the "Hide.Trait" tag, as shown below. After that, we're good to go.
Once the list is pruned, we'll make sure the remaining traits are shown in the order that they are presented on the Savage Worlds character sheet, since that will provide a consistent and familiar organization for the user. To manually sequence things in a table, HL provides the built-in "explicit" tag group and "explicit" sortset. The sortset is already employed by the table portal, so we just need to make sure each thing is properly configured. The "explicit" tag group is "dynamic", which means we can define tags implicitly by just assigning whatever tags we need to things. The tag group also presumes that all tags are numeric values. So we can assign an "explicit.1" tag to the "Pace" trait, an "explicit.2" tag to the "Parry" trait, etc.
The derived traits table just lists the traits now, and it should really have a title above it. We can include one by adding a "headertitle" element to the table portal, specifying the text we want displayed. The new element should look like the one shown below.
<headertitle><![CDATA[ @text = "Derived Traits" ]]></headertitle>
The last thing we should do is change the visual presentation of derived traits slightly - i.e. by making them a little bigger and reducing the gap between the names and the values. We can reduce the spacing by increasing the outer margins on the left and right of the template. This can be accomplished by changing the "marginhorz" attribute to a value like "30". We can increase the size of the text shown slightly by changing the style associated with the "name" and "details" portals from "lblNormal" to "lblLarge". We can also improve the spacing and relative placement of the portals by allowing the "name" portal to auto-size itself and assigning a fixed width to the "details" portal, since it's just a simple numeric value. This results in the last two sections of the Position script being changed to something like the following:
~position the name on the left portal[name].left = 0 ~position the details to the left of the info portal portal[details].width = 40 perform portal[details].alignrel[rtol,info,-10]
The next thing we should do with the "Basics" tab is add details about the character's rank and accrued experience points, and we should probably put it beneath the derived traits. The tab currently has a "Horizontal" portal that is beneath the attributes, so we'll start by moving it over to beneath the derived traits table portal. This can be done by simply changing the Position script for the layout. The pertinent lines for placing the separator are shown below.
~position the separator beneath the derived traits portal[Horizontal].top = portal[baTrait].bottom + 15 portal[Horizontal].left = portal[baTrait].left + (portal[baTrait].width - portal[Horizontal].width) / 2
Displaying the character's rank and accrued XP entails showing some text for a single facet of the character. Consequently, it only requires a simple label portal, as opposed to the tables we've been using thus far. We'll define our new label portal outside of a template, just like we've been defining the table portals. Labels don't need to be within a template unless they are linked to fields, so we'll use a script-based label and avoid needing the extra overhead of a template. The script will display the character rank and put the current XP in parentheses, with the entire portal looking something like the one shown below.
<portal id="baRank" style="lblLarge"> <label> <labeltext><![CDATA[ var rank as number rank = herofield[acRank].value if (rank = 0) then @text = "Novice" elseif (rank = 1) then @text = "Seasoned" elseif (rank = 2) then @text = "Veteran" elseif (rank = 3) then @text = "Heroic" else @text = "Legendary" endif @text &= " (" & hero.child[resXP].field[resMax].value & " XP)" ]]></labeltext> </label> </portal>
Once the portal is defined, we next need to add it to the layout and position it properly. Adding it to the layout amounts to adding a "portalref" element that references our new portal. We also need to specify where to sequence the contents of the portal in the overall tab sequence within the layout. This is controlled via the "taborder" attribute. Then we need to properly position the portal beneath the separator within the Position script for the layout. Both the new reference and the additional script code are shown below.
<portalref portal="baRank" taborder="30"/> ~size and position the rank details table beneath the separator portal[baRank].width = portal[baTrait].width portal[baRank].left = portal[baTrait].left portal[baRank].top = portal[Horizontal].bottom + 15
We still want to display information about the character creation state to the user, and that should probably go beneath the character rank. However, it should also be clearly decoupled from the rank, so we need to add another separator to the layout that we'll place beneath the rank. The state information can then be placed beneath the second separator.
Every use of a portal via a "portalref" element assumes the official name of the portal is name to be used within the layout. However, we want to use the same portal multiple times within a single layout. This requires that we give each portal reference a distinct "logical name" for use within the layout, which is accomplished via the "reference" attribute within the "portalref" element.
We'll start by changing the existing "Horizontal" portal to use a new reference id of "separator1". This requires we modify the "portalref" element as shown below and then change all references to "Horizontal" within the Position script to use "separator1" instead.
<portalref reference="separator1" portal="Horizontal"/>
We can now add the second separator. To do this, we'll duplicate the existing "portalref" element in the layout. Then we'll change the "reference" attribute to indicate a different name. We'll call it "separator2". The last thing we need to do is properly position the new separator beneath the character rank. That can be done by adding the following lines of code at the end of the Position script of the layout.
~position the second separator beneath the rank details portal[separator2].top = portal[baRank].bottom + 15 portal[separator2].left = portal[baRank].left + (portal[baRank].width - portal[separator2].width) / 2
The final piece we need to add to the "Basics" tab is the list of character creation details, but we can't do that quite yet. Most of the information we need to display is already available through a variety of resources, yet there is one piece of information that is currently missing. We need to track the number of "advances" that the character has accrued and applied. The number of accrued advances is calculated based on the number of experience points accrued (e.g. one advance per five XP). This is most easily handled as a new resource, so we'll define the resource we need in the file "thing_miscellaneous.dat". This resource will auto-calculate the quantity available based on the XP, resulting in something similar to what's presented below.
<thing id="resAdvance" name="Advances" compset="Resource"> <fieldval field="resObject" value="Advance"/> <tag group="Helper" tag="Bootstrap"/> <eval index="1" phase="Setup" priority="2000"> <after name="Calc XP Max"/><![CDATA[ var xp as number xp = #resmax[resXP] if (xp < 80) then field[resMax].value = round(xp / 5,0,-1) else xp -= 80 field[resMax].value = 16 + round(xp / 10,0,-1) endif ]]></eval> </thing>
Character Creation Details
All of the character creation details that we need to display are now being properly tracked via resources, so we can add a new table that lists the various resources and presents them to the user. In order to do this, we need to identify each resource to be included in the list. We define a new "Helper.Creation" tag for this purpose, then we assign the tag to all resources that pertain to character creation details. This includes advances, attribute points, skill points, hindrances points, and edges.
As we did for derived traits, we should specify the sequence in which the various character creation details are presented to the user. This entails assigning an "explicit" tag with a suitable numeric order value to each resource that we just assigned a "Helper.Creation" tag. Then we can use the "explicit" sortset to display all the details in the order we want. The revised "resHinder" resource is shown below.
<thing id="resHinder" name="Hindrance Points" compset="Resource"> <fieldval field="resObject" value="Reward"/> <tag group="Helper" tag="Bootstrap"/> <tag group="Helper" tag="Creation"/> <tag group="explicit" tag="3"/> </thing>
We can now define a new table portal wherein we'll display the character creation details. We limit the list of picks included in the table to only include those with the "Helper.Creation" tag. The net result is something similar to the following:
<portal id="baCreation" style="tblInvis"> <table_fixed component="Resource" template="baResource" sortset="explicit" scroller="no"> <list>Helper.Creation</list> </table_fixed> </portal>
With the table portal in place, the next step is to define an appropriate template for displaying resources in the list. We can easily adapt the "baTrtPick" template for this purpose. We can start by copying the entire template. After that, we can systematically convert the template. We start by changing the unique id and eliminating the "info" portal. We then change the script that generates the text for display in the "details" portal. Resources already synthesize a suitable text summary that is perfect for our needs, so we simply need to adapt the script over to utilize the "resShort" field. We can revise our margins to something more suitable for our purposes. Lastly, we re-work the Position script to omit the "info" portal, position the "details" on the far right, and put the name on the left. When we're done, our new template should look similar to the one shown below.
<template id="baResource" name="Resource Pick" compset="Resource" marginhorz="25" marginvert="3"> <portal id="name" style="lblLeft" showinvalid="yes"> <label> <labeltext><![CDATA[ @text = field[name].text & ":" ]]></labeltext> </label> </portal> <portal id="details" style="lblLeft"> <label> <labeltext><![CDATA[ @text = field[resShort].text ]]></labeltext> </label> <mouseinfo mousepos="middle+above"><![CDATA[ @text = "???" ]]></mouseinfo> </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[details].centervert ~position the details portal on the far right portal[details].width = 55 perform portal[details].alignedge[right,0] ~position the name on the left portal[name].left = 0 ]]></position> </template>
The final task is to properly add the new portal to the layout and position it beneath the character rank and the lower separator. Adding the portal to the layout consists of adding a new "portalref" element, along with an appropriate tab order. Once added, it can be readily positioned at the end of the Position script for the layout. By adding the script code below at the bottom of the script, the new portal is conveniently positioned beneath the character rank and separator.
portal[baCreation].width = portal[baTrait].width portal[baCreation].left = portal[baTrait].left portal[baCreation].top = template[separator2].bottom + 15 portal[baCreation].height = height - portal[baCreation].top