Character Sheet Phase 2 (Savage)
Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through
Overview
Our character sheet is starting to come together. We've got some of the most basic sections in place and we've got some momentum going, so we'll keep on rolling.
Swapping Sections Around
At this point, we need to make some structural adjustments to the sheet provided with the Skeleton files. We want to use the skills and special abilities sections, but they are currently shown in the righthand column. Similarly, armor and weapons are on the left, but we want to show them on the right. So the next thing we'll do is move these sections around, after which we can resume editing the individual sections.
Armor and weapons are both encapsulated by the "oArmory" layout, so we can move both of those at the same time by moving the layout. There are some interesting wrinkles associated with this layout that we'll need to deal with at some point, but we don't need to worry about them now. The "oArmory" layout is managed directly by the sheet, so we can move it to the right column within the Position script of the sheet. We'll have to fix this later, but for now we'll just move it to the bottom on the right side of the sheet. This is accomplished by moving the code that positions the layout to the end of the script and revising it properly for being at the bottom on the right. The new code should look like below.
~position the armory layout within the remaining space on the right layout[oArmory].width = colwidth layout[oArmory].top = layout[oRightSide].bottom + global[sectiongap] layout[oArmory].left = layout[oLogos].left layout[oArmory].height = extent - global[sectiongap] - layout[oArmory].top perform layout[oArmory].render
The next thing we need to do is move the skills and abilities sections from the right side to the left side. Fortunately, doing this is extremely easy. The skills and abilities are both handled via separate table portals. Both of these portals are currently managed within the "oRightSide" layout and positioned via the "autoplace" mechanism. Consequently, all we need to do is move them into the "oLeftSide" layout and place them properly within the new Position script.
Within the "oRightSide" layout, delete the two "portalref" elements for the "oAbility" and "oSkills" portals. Now go to the "oLeftSide" layout and add the elements there. Return to the "oRightSide" layout and delete the two "autoplace" operations on the portals we just removed. Then go to the "oLeftSide" layout and insert the two lines of code into the Position script there. When you're done, the two revised layouts should look like the below.
<layout id="oLeftSide"> <portalref portal="oHeroName"/> <portalref portal="oHeroInfo"/> <portalref portal="oAttribute"/> <portalref portal="oDerived"/> <portalref portal="oSkills"/> <portalref portal="oAbility"/> <position><![CDATA[ ~position the hero name at the top with the hero details beneath the name perform portal[oHeroName].autoplace[0] perform portal[oHeroInfo].autoplace[15] ~position the tables next perform portal[oAttribute].autoplace perform portal[oDerived].autoplace perform portal[oSkills].autoplace perform portal[oAbility].autoplace ~our layout height is the extent of the elements within height = autotop ]]></position> </layout>
<layout id="oRightSide"> <portalref portal="oGear"/> <templateref template="oPortrait" thing="actor"/> <position><![CDATA[ ~position the character portrait at the top and the various tables beneath perform template[oPortrait].autoplace perform portal[oGear].autoplace ~our layout height is the extent of the elements within height = autotop ]]></position> </layout>
That's all there is to it. Reload the data files and preview the character. Everything has now been swapped the way we intended.
Skills
Now that the swap is completed, we can resume with the next section on the left side, which happens to be skills. We'll handle skills the same way that we did attributes, using a two-column table and showing the die-type and adjustment for each skill. However, there is an important wrinkle with skills. We have to handle the knowledge skills specially, since we need to show the domain for those skills, and that means we can't use a two-column format for those skills.
The simplest solution is to create a second table portal that is the same as the standard portal for skills, with the key difference being a one-column format. Obviously, we also need to use a different List tag expression in each, with one table listing the non-knowledge skills and the other listing only the knowledge skills. Just to be safe, we should anticipate that a supplement may introduce new skills that require domains, and we need to handle those smoothly as well. So we'll differentiate based on whether the skill uses a domain or not.
We'll start by modifying the existing "oSkills" portal to properly display all skills in the two-column format. We can copy the portal for use with domain-based skills and give it the id "oSkillsDom". This portal then specifies a single column. Now we need to figure out how to differentiate between the domain-based skills and non-domain skills. Fortunately, this is easy. When we setup the handling of domains for skills, we based it on the presence of the "User.NeedDomain" tag, so we can do the same now within the List tag expression. This yields the following two table portals.
<portal id="oSkills" style="outNormal"> <output_table component="Skill" showtemplate="oSkillPick" columns="2"> <list>!User.NeedDomain</list> <headertitle><![CDATA[ @text = "Skills" ]]></headertitle> </output_table> </portal> <portal id="oSkillsDom" style="outNormal"> <output_table component="Skill" showtemplate="oSkillPick"> <list>User.NeedDomain</list> </output_table> </portal>
Now we can look at the "oSkillPick" template that is used to display each skill. Since we want our skills to look and behave basically the same as how we've already got attributes working, it's actually easiest to delete the current template, copy the attribute template, and adapt the attributes template slightly for skills. So that's how we'll proceed.
Once the attribute template is cloned, we can revise it. First, we need to give it the appropriate id of "oSkillPick". The four portals are the same for skills, so no changes are needed there. However, skills possess domains, so we need to handle that properly within our spacing. Within the Position script, we need to change the horizontal positions assigned to the "name" and "value" portals. Since the template is being used in both of the table portals, we actually want different positions based on whether the skill has a domain or not. Putting this all together results in the revised Position script shown below.
~our height is driven by the tallest portal height = portal[value].height if (issizing <> 0) then done endif ~center everything vertically within the template perform portal[name].centervert perform portal[value].centervert perform portal[adjust].centervert perform portal[dots].centervert ~position everything horizontally if (tagis[User.NeedDomain] = 0) then portal[name].left = 40 portal[value].left = 405 else portal[name].left = 115 portal[value].left = 875 endif perform portal[adjust].alignrel[ltor,value,5] ~extend the dots from the right of the name across to the value on the right perform portal[dots].alignrel[ltor,name,0] portal[dots].width = portal[value].left - 5 - portal[dots].left
If we reload the data files and preview the character again, we should now see our skills in a proper two-column layout. Once we add a Knowledge skill to the character and assign it a domain, we can preview it again. But our Knowledge skill isn't appearing. It seems we forgot to do something.
The problem is that we haven't integrated the new table portal for domain-based skills into the layout yet. So we'll do that now. Locate the "oLeftSide" layout and add a new "portalref" element for the "oSkillsDom" portal. Then add an "autoplace" statement to the Position script, immediately after the placement of the "oSkills" portal. The reload and preview again.
Now our Knowledge skill is showing up, but there is a gap between the normal skills and the domain-based skills. This is because the "autoplace" logic is using a default gap that is setup by the sheet. We don't want a gap between two skills tables, so we can explicitly use a gap of zero by using a statement like the one below.
perform portal[oSkillsDom].autoplace[0]
It's time for one last reload and preview. This time, everything is looking good for skills.
Racial Abilities
Next on the list is racial abilities. The Skeleton files already provide a table portal and template for showing abilities, and we'll simply adapt it for display of racial abilities. For the portal itself, we need to change to the more restrictive "RaceAbil" component so that we only show racial abilities. We also must change the header to specify "Racial" instead of "Special". This results in the following portal.
<portal id="oAbility" style="outNormal"> <output_table component="RaceAbil" showtemplate="oAbilPick"> <headertitle><![CDATA[ @text = "Racial Abilities" ]]></headertitle> </output_table> </portal>
Looking at the template, it already provides exactly what we need. The name of the ability is prominent, and a summary of the ability is shown in the remaining space. In the interest of consistency, we'll use this same template for edges and hindrances, so most characters will generally have lots of abilities. Based on that, the only thing we should probably change is the font size used for the ability name. By shrinking it a little bit, we'll reduce the vertical height of each entry in the table and also give ourselves a little more horizontal space for the summary text.
All we need to do is switch to a suitable style with a slightly smaller font size. The "ofntmedium" font should do nicely. Unfortunately, we also need a style that is left-aligned, and the only style using the medium font is center-aligned. So we can create a new output style that is left-aligned and uses the medium font, as shown below.
<style id="outMedLt"> <style_output textcolor="000000" font="ofntmedium" alignment="left"> </style_output> </style>
Once we've got our new style defined, we can simply change the style associated with the "details" portal to use the "outMedLt" style. Then we can reload the data files and preview again. The racial abilities look great now.
Hindrances and Edges
Both hindrances and edges are grouped together because they are extremely simple and work virtually the same. Both of these facets of the character are built upon the "Ability" component. Since all we're showing is the name and summary on the character sheet, we can readily re-use the same template that we setup for showing racial abilities.
This means that all we need to do is create two new table portals, both of which can be cloned and adapted from the portal used for racial abilities. We simply need to change the unique id, the component being filtered, and the header text. This results in the two new portals below.
<portal id="oHindrance" style="outNormal"> <output_table component="Hindrance" showtemplate="oAbilPick"> <headertitle><![CDATA[ @text = "Hindrances" ]]></headertitle> </output_table> </portal> <portal id="oEdge" style="outNormal"> <output_table component="Edge" showtemplate="oAbilPick"> <headertitle><![CDATA[ @text = "Edges" ]]></headertitle> </output_table> </portal>
Once the two portals are defined, we then need to integrate them into the "oLeftSide" layout. This entails adding "portalref" elements for each and then positioning them beneath the racial abilities. The revised layout is shown below.
<layout id="oLeftSide"> <portalref portal="oHeroName"/> <portalref portal="oHeroInfo"/> <portalref portal="oAttribute"/> <portalref portal="oDerived"/> <portalref portal="oSkills"/> <portalref portal="oSkillsDom"/> <portalref portal="oAbility"/> <portalref portal="oHindrance"/> <portalref portal="oEdge"/> <position><![CDATA[ ~position the hero name at the top with the hero details beneath the name perform portal[oHeroName].autoplace[0] perform portal[oHeroInfo].autoplace[15] ~position the tables next perform portal[oAttribute].autoplace perform portal[oDerived].autoplace perform portal[oSkills].autoplace perform portal[oSkillsDom].autoplace[0] perform portal[oAbility].autoplace perform portal[oHindrance].autoplace perform portal[oEdge].autoplace ~our layout height is the extent of the elements within height = autotop ]]></position> </layout>
If we reload the data files and preview the character sheet, we'll now see the new sections for hindrances and edges. At this point, the left side of our character sheet is complete.
Shorter Names for Output
At this point, you'll probably have noticed that some of the various edges, hindrances, and racial abilities have reasonably long names. The longer names severely limit the amount of description text that can be included in the character sheet. It would be ideal to utilize shorter names for some of these items.
The Kit makes it easy to add support for shorter names to a group of items in situations like this. If you identify a component where short names would be beneficial, you can set the "hasshortnames" attribute on the component to "yes". Doing so ensures that every thing derived from the component possesses a field with the id of "shortname". You can then specify this field within the definition of things derived from the component. If a particular thing has no "shortname" field, then the field automatically defaults to the contents of the basic name for the thing.
We use the same template for edges, hindrances, and racial abilities. As such, we need to add support for short names to all three. Conveniently, all three share the "Ability" component, so we only need to assign the new behavior to this one component.
Once that's done, we can go back through the various things and define "fieldval" elements to properly assign a "shortname" to those that require one. For example, the "Arcane Background: Super Powers" edge is extremely long and can be readily shorted to "Arcane Super Powers", or something along those lines.
With short names in place, our last step is to make use of the new field. This simply requires that we reference the "shortname" field instead of the "name" field within the template that outputs the abilities. Once that change is in place, our character sheet will begin showing the shorter names whenever they are defined.
Omitting Items from Character Sheet
As we've been doing our testing and experimenting with the character sheet, you may have noticed that there are a few items that really don't need to be included on the sheet. For example, the "Free Edge" ability for humans really doesn't need to be listed on the character sheet. Similarly, the "Power Points" edge is included in the total power points listed for the character and doesn't really need to take up space on the sheet.
We can omit selected items from inclusion on the character sheet very quickly. The Skeleton files provide a pre-defined "Print.NoPrint" tag. You can assign this tag to any thing that you don't want to have appear in the character sheet. Once assigned, you can then preclude all things possessing that tag from a given table via the List tag expression. For example, since the "oEdge" table portal currently has no List tag expression, you could define one comprising of "!Print.NoPrint" and skip all edges that possess the tag. The revised portal would like the following.
<portal id="oEdge" style="outNormal"> <output_table component="Edge" showtemplate="oAbilPick"> <list>!Print.NoPrint</list> <headertitle><![CDATA[ @text = "Edges" ]]></headertitle> </output_table> </portal>