Integrated Editor Support (Savage)

From HLKitWiki
Revision as of 15:03, 18 February 2009 by Rob (Talk | contribs)

Jump to: navigation, search

Context: HL KitAuthoring Examples … Savage Worlds Walk-Through 


Our data files are basically complete. Our focus now shifts to the final steps necessary to prepare the data files for release to others. The first of these final steps is setting things up so that users can leverage the integrated Editor provided within HL.

Most gamers will have their own custom tweaks that they employ within their gaming group or that are unique to a particular game world. The goal is to enable them to easily add that material. You've invested quite a bit of time in creating your data files, so spending a small amount more to maximize the ability of others to use your files is a worthwhile investment.

Using the Editor

We assume that you're at least moderately familiar with using the Editor for a different game system. If you haven't done so yet, take the time to review the documentation for one our licensed game systems and familiarize yourself with how the Editor works. After a little bit of experimenting, you should be able to appreciate how much simpler the Editor is to use than manually editing XML files. Our new objective is to wrap our data files up in a way that allows users to quickly and easily create new content via the Editor.

Editor Basics

Internally, the Editor is driven by an assortment of "editthing" XML elements. Every "thing" is derived from a component set. Every type of thing that you want users to be able to create must have an "editthing" defined for it. Consequently, you'll need to define a separate "editthing" for every component set that users can create things for.

Within each "editthing", you must define one or more "inputthing" elements. Each "inputthing" corresponds to exactly one specific facet of a particular thing. For example, the value of a field is controlled via an "inputthing". So is whether a particular tag is assigned to the thing.

In an effort to keep everything as simple as possible for users, the Editor uses only a handful of simple mechanisms. These include checkboxes, picking an option from a list, entering a value, and a few others. Each "inputthing" specifies the exact user-interface mechanism to be used for customizing a single facet of the thing being edited. Along with the mechanism, it must also provide the internal details for how to map the visual mechanism to something concrete in the data files.

Let's look at an example. Skills can optionally require the user to specify a domain. Internally, this detail is controlled for skills via the "User.NeedDomain" tag. Within the Editor, we want the user to simply specify whether the skill requires the domain or not. The best way to handle an either-or situation like this is by showing the user a checkbox. So we would define an "inputthing" that uses the "tagcheck" mechanism. This mechanism presents a checkbox to the user and assigns a tag to the thing based on whether the checkbox is checked. If we specified the "User.NeedDomain" tag for the "tagcheck", that tag would be added to the thing if the checkbox is checked and omitted if unchecked.

The contents of an "editthing" are primarily just a list of "inputthing" elements. The order of the "inputthing" elements dictates the order in which everything is presented to the user within the Editor. This gives us complete control and allows us to incrementally get our data files to fully utilize the Editor. In the sections below, we'll be looking at this process in detail, with concrete examples. Through the process, you'll be exposed to most of the different "inputthing" mechanisms and how to use them.

Use Tags Whenever Practical

Before we start implementing Editor support, we need to first address an important detail. The majority of the "inputthing" mechanisms center on the use of tags. The reason for this is that tags provide a critical benefit that simple values and text fields do not. Tags offer a restricted set of inputs that the user can select from.

In most situations, the data files for any game system will be designed around various small groupings of options. In Savage Worlds, all attributes and skills use a die type that ranges from "d4" to "d12". Hindrances can be either minor or major in severity, thereby having a cost of 1 or 2 points. The character's rank ranges through five levels, and edges can specify a dependency on the rank level. The list goes on and on.

If we present the user with an empty field to enter a value into, we have no way of knowing that the user entered something valid. For example, if we present a field for the user to specify the minimum strength requirement for a ranged weapon, there is nothing to stop the user from entering a value of 42. Even if the user means well, the question arises whether he should be entering a value of 10 for a "d10" or a value of 5 to correspond with the technique we use internally. However, if we present a list of tags containing only the five valid die types, there is no opportunity for confusion and no opportunity for error.

There are still a variety of places where an arbitrary text or value field is exactly what we need. However, we will strive to use them only when they are truly appropriate. Keep this in mind as you're working on your own data files. We'll be running into a few situations below where we need to change our data files to leverage tags instead of fields.

Taking Inventory

The final thing we should do before starting on our implementation is take inventory of our data files. We need to go through all the different files to identify which component sets we want to expose to users within the Editor. In general, this list should consist of any objects that we expect users will want to customize for their own games. Once we have the list culled out, we should then organize it into a reasonable sequence that will be easiest to implement.

The list we can identify for Savage Worlds is presented below, showing the component set id and the corresponding group of objects the user will be operating upon. With this list, we have a clear path for getting full Editor integration operational.

  • Attribute - Attributes
  • Skill - Skills
  • Trait - Derived Traits
  • RaceAbil - Racial Abilities
  • Edge - Edges
  • Hindrance - Hindrances
  • Reward - Hindrance Rewards
  • Equipment - Mundane Items
  • Melee - Hand Weapons
  • Ranged - Ranged Weapons
  • SpecWeap - Special Weapons
  • Armor - Armor
  • Shield - Shields
  • Vehicle - Vehicles
  • Arcane - Arcane Backgrounds
  • Power - Arcane Powers
  • Drawback - Arcane Drawbacks
  • Injury - Injuries
  • Race - Races
  • Creature - Creatures
  • Simple - Simple (used for validation rules and such)
  • Mechanic - Mechanics (used for auto-bootstrapped behaviors)

Changes Needed

Since we want to utilize tags whenever possible within the Editor, we need to go through each of the identified component sets above and look for places where we must make revisions. There are a total of four changes we need to make to the mechanics to streamline Editor integration.

The first change we'll notice is within the "Edge" component. The "edgIsWild" field is a simple "yes" or "no" situation. As such, it must be changed to the use of a tag, with the tag indicating a wildcard is required. To handle this, we'll define a "User.NeedWild" tag. We can then delete the field and go through all edges to assign the tag whenever the edge requires a wildcard. The final modification is within the "prereq" element on the "Edge" component, where the test of the field value must instead test the presence of the tag.

The second change is within the "MinRank" component, which will impact both edges and arcane powers. The minimum rank is specified via a field value, but it should be handled via a tag. We'll define a new "MinRank" tag group with tags corresponding to each rank level, as shown below.

  <value id="0" name="Novice"/>
  <value id="1" name="Seasoned"/>
  <value id="2" name="Veteran"/>
  <value id="3" name="Heroic"/>
  <value id="4" name="Legendary"/>

With the tag group in place, we can delete the field and then tweak the "prereq" element on the component so that it checks for the tag instead of the field value (just like we did above). We can then go through all edges and arcane powers, converting all use of the "rnkMinRank" field over to the appropriate "MinRank" tag.

The third change is required within the "Hindrance" component. The "hinMajor" field has a simple "yes" or "no" behavior, so we can change it to key on the presence of a tag. We'll define a new "User.HindMajor" tag that indicates a hindrance starts out as a major one. Then we can go through all hindrances and assign the tag whenever a hindrance starts out as major.

This situation is a bit different from the wildcard field, though. The user can select the severity for some hindrances. Consequently, we cannot delete the field, since we need it to store the user-selected severity rating. What we'll do instead is configure the initial state of the field based on the presence of the tag. Since the field is user-modifiable, we can't do this in an Eval script, because we would end up clobbering the user-assigned state with our script every time. Instead, we'll handle it within a Creation script, since we can setup the initial state in the Creation script and then allow the user to adjust the field thereafter. Our script should look like below.

  field[hinMajor].value = tagis[User.HindMajor]

The final change we need to make is with weapons. The minimum strength requirement is currently handled via a field value. We need to change it in exactly the same way we handled the weapon die previously. If the tag is present, the corresponding requirement exists. The only caveat is that we must continue handling the presence of the weapon die as a strength requirement, with the separate requirement only applying when no weapon is given. Our new tag group should look like below.

  name="Strength Req Die">
  <value id="2" name="d4"/>
  <value id="3" name="d6"/>
  <value id="4" name="d8"/>
  <value id="5" name="d10"/>
  <value id="6" name="d12"/>

Upon closer examination of the files, we'll find it difficult to actually delete the field. This means we'll need to use the tag to set the field value appropriately. Since it's not user-adjustable, we can change the field from "static" to "derived", then we can define an Eval script that maps the tag to the field value. The mapping is easy due to the proper use of the value possessed by each tag, resulting in the Eval script below.

<eval index="3" phase="Setup" priority="1000"><![CDATA[
  if (tagis[StrReqDie.?] <> 0) then
    field[wpStrReq].value = tagvalue[StrReqDie.?]

The change impacts both the "WeapMelee" and "WeapRange" components, and all weapons based upon them. Any weapon that specifies the "wpStrReq" field must be modified to specify the corresponding tag instead. Beyond that, the only remaining change is within the "prereq" element on the "WeaponBase" component, which must change the tag instead of the field value on things.

At this point, we've got everything converted that must be converted. We can now begin define entries for Editor integration.

Starting Simple

All the pieces are in place, so it should be relatively straightforward for us to begin hooking everything into the Editor. We'll start with something simple, such as attributes. While few users will want to actually define a new attribute, there's no reason they can't, and making it possible is easy. All of our internal mechanisms just display a list of attributes, so adding a new one will merely increase the space needed to show all of them.

We must now take a look at the "Attribute" component set. There are three components that comprise the compset. The first is the "Attribute" component, which has nothing interesting that must be configured for a new attribute. The last is the "CanAdvance" component, which also has no behaviors that must be tailored for individual attributes. That leaves the "Trait" component, for which the only interesting field is the abbreviation ("trtAbbrev").

Now let's take a look at all of the attributes we've defined so far. There are six of them, including the one we added for super power skills. In assessing the attributes, there are two tags that we define on one or more of them. Each of these tags should be made customizable by the user when creating a new attribute. Combining that with the field identified above, we have the following three special pieces of information that we want the user to configure.

  • Abbreviation - This will be a simple field where the user can enter the text he chooses. We can use a "field" input to capture the field value with the Editor.
  • Hide from User - The super power attribute is hidden, so we must allow users to hide any new attribute that they may add. This state is controlled through the presence of a tag. We'll use the "tagcheck" input for Editor integration, since the tag will be assigned if the user checks the box.
  • Display Sequence - The order in which attributes is shown depends on an explicit sequence defined by the author, which is dictated via a tag assignment. The user will need to assign a proper tag from a specific tag group, but the user can choose any tag that he wants to control the order. For this, we'll use a "tagpick" input for Editor integration, which presents the user with the option to select a tag from a specified tag group.

We can now define our "editthing". We'll specify the appropriate prefix to be used on all attributes ("attr"), along with a suitable description text and summary information. For each input, we'll provide a suitable name that tells the user what he's editing, as well as a information about how the input is used and what the implications are of any particular values or selections. Putting it all together yields an "editthing" that looks like the following.

  description="Attributes are incredibly simple. They only have an optional abbreviation."
  summary="Defines an Attribute to which starting attribute points can be allocated by a character.">
    helptext="Specify a very short abbreviation for use in displaying the attribute via its abbreviation (max 5 characters).">
    <it_field field="trtAbbrev"/>
    name="Hide from User?"
    helptext="Specify whether this attribute is hidden from the user and for internal use only.">
    <it_tagcheck group="Hide" tag="Attribute"/>
    name="Display Sequence"
    helptext="Specify the sequence in which this attribute should be shown to the user within lists of attributes. The unique id of the tag must be an integer value.">
    <it_tagpick group="explicit" tag="?" require="yes" deftag="1"/>

Editor support for attributes should now be in place, so reload the data files and let's test it. Launch the Editor via the "Tools" menu, then create a new data file. Click on the "Attribute" tab and then click on the "New (Copy)" button to create an attribute. Pick any of the standard five attributes and make a copy. Within the list of inputs below, you should see all three inputs we specified, with each having the proper behavior we expected.

Change the name to something conspicuous (e.g. "Zebra") and assign the thing a suitable unique id. Then specify a display sequence value of 10. Save the new attribute and click the button to test it now. Since we added an attribute, it's not user-selectable and doesn't appear automatically. We need to perform a quick-reload, after which it should appear everywhere it's supposed to. On the "Basics" tab, in the summary panel, on the character sheet, etc. Our attribute is working and we've got things integrated smoothly with the Editor.