Character Creation Logic (Savage)
Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through
Overview
With all the basics now in place, it's a perfect time for us to get all of the basic character creation logic fully tested and operational. This entails tracking the allocation of attribute points and skill points, as well as the selection of races, edges, hindrances, and rewards.
Attribute Points
We'll focus on attribute points first. We already have a resource in place to track those points, so we need to properly consume those points. This can be most easily handled by adding a script to the "Attribute" component that spends the resource appropriately. The Skeleton files provide such a script for us already, so all we need to do is adapt it for our needs. The d4 rating for an attribute costs no points, but each increment beyond that costs one point. So we'll employ a component script that behaves accordingly, which should look similar to the one shown below.
<eval index="2" phase="Traits" priority="10000"> <before name="Calc resLeft"/> <after name="Bound trtUser"/><![CDATA[ ~since the base starting value for each attribute is two, we add only the extras perform #resspent[resAttrib,+,field[trtUser].value - 2,field[name].text] ]]></eval>
Skill Points
Skill points behave much like attributes, except that we have to treat the initial purchase of the skill as one skill point and all subsequent increases as either one or two additional skill points. We already have a resource in place to track the points, and the Skeleton files already provide a "Skill" component script that we can easily adapt to our needs.
The interesting logic here has to do with determining whether each die type for a skill costs one or two points. We can start by assuming that each notch counts at least one point. So we'll first calculate the total number of notches. We must also subtract one from our total, since the base values for skills is two, which costs the first point.
The extra skill point cost is incurred for each level that a skill exceeds its linked attribute. We can access the linked attribute and get its value via the "linkage" transition. Once we have that value, we can compare the two and add an extra skill point for each level of difference between them.
The net script that we need should look something like the following:
<eval index="2" phase="Traits" priority="10000"> <before name="Calc resLeft"/> <after name="Bound trtUser"/><![CDATA[ ~if this skill is not added directly to the hero (i.e. an advance), skip it entirely if (origin.ishero = 0) then done endif ~the base value for skills is two, so we need to adjust by one to get the proper cost var points as number points = field[trtUser].value - 1 ~add an extra point for every level we exceed our linked attribute var attrib as number attrib = linkage[attribute].field[trtUser].value if (field[trtUser].value > attrib) then points += field[trtUser].value - attrib endif ~accrue the total points spent on this skill hero.child[resSkill].field[resSpent].value += points ]]></eval>
Races
Try choosing all of the different races for the character and verify whether everything is behaving correctly. This includes racial bonuses to attributes, skills, and derived traits, plus other facets such as the extra edge conferred by the "Human" and "Half-Elven" races. After a quick check, everything looks correct, except for one facet.
Hindrances
Add a variety of hindrances of different severities. Also add hindrances that have a user-controlled severity and toggle that severity. While doing so, watch the total points of rewards that are available for selection on the right. The total should go up one point for each minor hindrance and two points for each major hindrance. In addition, when hindrances are added that confer negative effects, verify that the effects are being applied properly. If a hindrance has user-controlled effects that can be toggled on and off, make sure the option appears properly on the "In-Play" tab and that it all behaves correctly.
Everything is working as it should, with one exception. The rules stipulate that a character may choose a maximum of one major hindrance and two minor hindrances, but this restriction is not being validated anywhere. We need to add a new validation rule that will enforce this restriction and report when a character exceeds the limits. To do that, we need to add a new thing whose sole purpose is performing this validation. By convention, we add these "validation" things to the file "thing_validate.dat". So open up the file and we'll add a new validation thing whose sole purpose is to invoke our validation rule to verify hindrances. We derive the thing from the "Simple" component set, and we must make sure it's added to every character so that the validation is always performed. The net result looks like the following.
<thing id="valHinders" name="Hindrances" compset="Simple"> <tag group="Helper" tag="Bootstrap"/> <evalrule index="1" phase="Validate" priority="8000" message="Maximum of one major and two minor hindrances allowed" summary="Limit exceeded"><![CDATA[ ~iterate through all hindrances and tally up the number of majors and minors var major as number var minor as number foreach pick in hero where "component.Hindrance" if (each.field[hinMajor].value = 0) then minor += 1 else major += 1 endif nexteach ~if we have no more than one major and two minor, we're good if (major <= 1) then if (minor <= 2) then @valid = 1 done endif endif ~mark associated tabs as invalid container.panelvalid[edges] = 0 ]]></evalrule> </thing>
After making the above changes, validation is now working as it should. However, it would be better if the title above the hindrances table appeared in red to clearly identify where to fix the problem on the tab. This can be achieved by having the above EvalRule assign a tag to the hero, which can be checked by the table portal. So we start by defining a new "Hero.BadHinders" tag within the file "tags.1st". Add the following tag to the "Hero" tag group.
<value id="BadHinders"/> <!-- Indicates that the hindrances selected don't comply with the rules -->
Once that's in place, we can assign the tag within the above EvalRule script by adding the line below if the rule is failed.
perform hero.assign[Hero.BadHinders]
The final step is to check for and properly handle the tag within the table portal. Locate the "edHinders" portal within the file "tab_edges.dat" and then find the HeaderTitle script. This script returns the title to be displayed, which currently amounts to simply "Hindrances". We can change the script to detect if the tag is present and change the color to red when it is. The new script should look similar to the one below.
@text = "" if (hero.tagis[Hero.BadHinders] <> 0) then @text = "{text ff0000}" endif @text &= "Hindrances"
Rewards
Add a variety of rewards and verify that the available number of resources is being properly adjusted. Some rewards are worth two points and some are worth one. Also verify that the effects of each reward are being applied correctly. Everything seems to check out fine.
Edges
The final thing that we need to test on this tab is edges. Add a variety of edges and make sure that the appropriate resources are being adjusted. For edges that confer bonuses, verify that the effects are being applied properly. If an edge has user-controlled effects that can be toggled on and off, make sure the option appears properly on the "In-Play" tab and that it all behaves correctly. Once that's handled, we're ready to move on with our development.