Using Bitmaps for Die Types (Savage)

From HLKitWiki
Jump to navigationJump to search

Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through 

Overview

The contents of the attribute and skill incrementers currently show the die-type and any adjustment using the format "d6+2". It would be ideal if we actually showed a bitmap for each die-type, since it would tailor things much more closely to how Savage Worlds works. Depending on what we ultimately want to achieve, doing this is either easy or a small amount of work.

The Easy Solution

The Skeleton data files include an assortment of bitmaps for the various die-types. There are bitmaps for use on the screen and within character sheet output. All of these bitmaps are automatically copied into place for your use when you create a new game system. All you need to do is reference them.

We've already centralized all of the handling for what gets displayed into a single place. The field "trtDisplay" contains the text to be output, and the procedure "FinalRoll" handles synthesizing this text. So all we need to do is modify the "FinalRoll" procedure to incorporate the bitmaps instead of using simple text.

Open the file "procedures.dat" and locate the "FinalRoll" procedure. Within this procedure, there is a line that generates the die-type for display by combining the letter "d" with the numeric value. We can easily change this to use the appropriate bitmap instead.

You can insert the bitmap into the text via the use of encoded text. The syntax for inserting a bitmap via encoded text is "{bmp filename}", where filename is the base name of the file to be inserted. Since the die-type bitmaps intended for use on screen are named in the form "d6_screen.bmp", the corresponding encoded text for a d6 should be "{bmp d6_screen}". This means that we can change one line of script code and get the die-type appearing as a bitmap. The new line of code should be the following.

finaltext = "{bmp d" & dietype & "_screen}"

Reload the data files and see what you think. The die-type uses the proper bitmap and any adjustment is appended as a text value. Not bad for a single line of code being changed, but we can probably do a bit better.

Refining the Behavior

As expected, after a little bit of testing, the results aren't quite optimal. So let's try refining the handling a bit. For example, if there is an adjustment made to the final roll (e.g. "d6+2"), the "+2" is a bit too small now. We'll start by increasing the font size.

The incrementers for showing die-types all use the "incrDie" style, so we'll need to modify it. Open up the file "styles_ui.aug" and locate the style. It currently uses the "fntincrsim" font resource, which is also used by another incrementer, so we can't simply change it. Instead, we need to define a new font resource, which we'll call "fntincrdie". We can define the new resource as part of the style, and we'll pick a size like 54 as a good balance that hopefully won't overshadow the die-type bitmap. This yields a revised style definition like the one below.

<style
  id="incrDie">
  <style_incrementer
    textcolor="f0f0f0"
    font="fntincrdie"
    editable="no"
    textleft="13" texttop="0" textwidth="44" textheight="20"
    fullwidth="70" fullheight="20"
    plusup="incplusup" plusdown="incplusdn" plusoff="incplusof"
    plusx="59" plusy="0"
    minusup="incminusup" minusdown="incminusdn" minusoff="incminusof"
    minusx="0" minusy="0">
    </style_incrementer>
  <resource
    id="fntincrdie">
    <font
      face="Arial"
      size="54">
      </font>
    </resource>
  </style>

If we add the Ace edge and the Boating skill, we can see what this looks like. Unfortunately, this new size is a little too prominent compared to the die-type bitmap, so we need to make it a little less white. This is achieved by modifying the "textcolor" attribute within the style and dialing down the brightness a little bit. We'll pick a value of "c0c0c0" as a good compromise.

This still doesn't look quite right. The "+" looks good, but the numeric adjustment looks a little too frail. We can change the font resource to be bold, but that results in the "+" looking blocky and ugly. What we need is for the "+" to be non-bold and the numeric value to bold. We can accomplish this by revising the procedure a little bit to carve up the bonus into two pieces. The net result is a revised procedure script that looks like below.

~declare variables that are used to communicate with our caller
var finaldie as number
var finalbonus as number
var finaltext as string

~bound our final die type appropriately
var final as number
final = finaldie
if (final < 2) then
  final = 2
elseif (final > 6) then
  final = 6
  endif

~convert the final value for the trait to the proper die type for display
var dietype as number
dietype = final * 2
finaltext = "{bmp d" & dietype & "_screen}"

~if there are any bonuses or penalties on the roll, append those the final result
if (finalbonus <> 0) then
  var bonus as string
  bonus = signed(finalbonus)
  finaltext &= left(bonus,1) & "{b}" & right(bonus,1)
  endif

We now have something that works a bit better, but it still doesn't look great. When some traits have adjustments and others don't the die-type bitmaps seem to bounce around due to the centering logic we're using. In addition, having the adjustment value aligned at the baseline of the bitmap isn't very visually appealing. More importantly, the changes we've introduced have caused a number of cascading issues elsewhere, such as the damage and description text for weapons, which are now mucked up.

A Different Approach

Sometimes it's better to step back and re-consider the approach you're taking. In this case, we can continue trying to refine the behavior to get something we like, but it's doubtful we'll end up with something truly good. So we're going to abandon our current approach and put everything back the way it was when we started. This includes both the "incrDie" style and the "FinalRoll" procedure.

Instead of trying to squeeze both the die-type bitmap and the adjustment into the incrementer, let's re-think what we need. The incrementer controls the die-type and nothing more. Any adjustment is completely distinct from the die-type and does not need to be incorporated into the incrementer contents. In fact, it would be perfectly reasonable to move the adjustment out of the incrementer and have it appear next to the incrementer. So that's the approach we'll use this time.

In order to use this approach, we'll need to synthesize two different pieces of text for each trait. We'll need the current "trtDisplay" field that contains a text version we can use in places where we don't want to include the die-type bitmaps. For example, the description text looks better without the die-type bitmap. We'll also need a separate field with the die-type bitmap that we can show within the incrementers.

This means we need to define a new field for the die-type bitmap and integrate it appropriately. First, we define the field, which we'll call "trtIncr" due to its intended use with the incrementers. The field should look like the one defined below.

<field
  id="trtIncr"
  name="Die-Type for Incrementers"
  type="derived"
  maxlength="50">
  </field>

The next step is to properly synthesize the field contents. We can augment both the "FinalRoll" procedure and the Eval script that calls the procedure to achieve this. A new parameter can be passed into the procedure that is returned with the appropriate bitmap information, and that parameter can then be assigned to the new field. The revised procedure and Eval script should look similar to the ones shown below.

<procedure id="FinalRoll" scripttype="none"><![CDATA[
  ~declare variables that are used to communicate with our caller
  var finaldie as number
  var finalbonus as number
  var finaltext as string
  var dietext as string

  ~bound our final die type appropriately
  var final as number
  final = finaldie
  if (final < 2) then
    final = 2
  elseif (final > 6) then
    final = 6
    endif

  ~convert the final value for the trait to the proper die type for display
  var dietype as number
  dietype = final * 2
  finaltext = "d" & dietype

  ~if there are any bonuses or penalties on the roll, append those the final result
  if (finalbonus <> 0) then
    finaltext &= signed(finalbonus)
    endif

  ~convert the final value for the trait to the proper die type bitmap
  dietext = "{bmp d" & dietype & "_screen}"
  ]]></procedure>
<eval index="4" phase="Render" priority="5000" name="Calc trtDisplay">
  <after name="Calc trtNetRoll"/>
  <after name="Calc trtFinal"/><![CDATA[
  ~if this is a derived trait, our display text is the final value
  if (tagis[component.Derived] <> 0) then
    field[trtDisplay].text = field[trtFinal].value
    done
    endif

  ~calculate the net bonuses and penalties for the roll
  var finalbonus as number
  finalbonus = field[trtNetRoll].value

  ~generate the appropriate results for display
  var finaldie as number
  var finaltext as string
  var dietext as string
  finaldie = field[trtFinal].value
  call FinalRoll

  ~put the final results into the proper fields
  field[trtDisplay].text = finaltext
  field[trtInfo].text = dietext
  ]]></eval>

At this point, our new field is ready to go and we simply need to put it to use within the incrementers. That's a simple step. All you need to do is locate the "trtUser" field that drives the values shown within the incrementer and revise the "Finalize" script to pull the text from the "trtIncr" field instead of "trtDisplay". Once that's done, we can see how things look. This new approach looks like it could be a good bit better, so we'll continue refining it.

Refining the New Approach

The incrementer was sized to accommodate the adjustment, which is no longer being included. So we need to revise the incrementer style we use for showing the traits. Open the file "styles_ui.aug" and locate the "incrDie" style that we worked with previously.

This time, we need to adjust the overall width of the incrementer. That requires we also adjust the width of the text region in the center, as well as the offset of the arrow for increasing the value. If we drop the overall width to about 58 pixels, everything should look pretty good. This yields a new style definition like the one below.

<style
  id="incrDie">
  <style_incrementer
    textcolor="f0f0f0"
    font="fntincrsim"
    editable="no"
    textleft="13" texttop="0" textwidth="32" textheight="20"
    fullwidth="58" fullheight="20"
    plusup="incplusup" plusdown="incplusdn" plusoff="incplusof"
    plusx="47" plusy="0"
    minusup="incminusup" minusdown="incminusdn" minusoff="incminusof"
    minusx="0" minusy="0">
    </style_incrementer>
  </style>

Where's the Adjustment?

There's one critical piece that we still haven't dealt with. The new approach omits the adjustment from the incrementer, so we need to go back and add it to the interface somewhere. The two places where we will be showing the adjustment separately are on the Basics tab for attributes and on the Skills tab for skills. We'll need to address each of these locations, but the general approach will be the same.

We'll start with the Basics tab and the attributes, which are controlled by the "baAttrPick" template. Open the file "tab_basics.dat" and locate the template. We're going to need more horizontal space to fit the adjustment value, so the first thing to do is decrease the horizontal margin to something like "10" pixels.

The next step is to add a new portal for displaying the adjustment value. The contents of this new portal will be driven by a script, and we should either show the adjustment value or an indicator that there is no adjustment for this trait. If there is no adjustment, the indicator should be faint so that it doesn't draw attention away from the important information being shown. The adjustment value can be pulled from the "trtNetRoll" field, so this should yield a new portal similar to the one below.

<portal
  id="adjust"
  style="lblLarge">
  <label>
    <labeltext><![CDATA[
      if (field[trtNetRoll].value = 0) then
        @text = "{text 808080}" & chr(150)
      else
        @text = signed(field[trtNetRoll].value)
        endif
      ]]></labeltext>
    </label>
  </portal>

With the portal defined, we now need to integrate it into the Position script for the template. Since the adjustment can vary in contents, it can also vary in width. Consequently, we need to allocate a fixed width to the portal so that everything behaves consistently. The revised Position script should look similar to the one below.

~set up our height based on our tallest portal
height = portal[info].height

~if this is a "sizing" calculation, we're done
if (issizing <> 0) then
  done
  endif

~freeze our value in advancement mode or if an advancement has modified us
~Note: All freezing must be done *before* any positioning is performed.
if (state.iscreate = 0) then
  portal[value].freeze = 1
elseif (autonomous = 0) then
  portal[value].freeze = 1
  endif

~position our tallest portal at the top
portal[info].top = 0

~center the other portals vertically
perform portal[name].centervert
perform portal[value].centervert
perform portal[adjust].centervert

~position the info portal on the far right
perform portal[info].alignedge[right,0]

~assume a standard width for the adjustment, since it can vary, then position
~the adjustment to the left of the info portal (plus a gap)
portal[adjust].width = 20
perform portal[adjust].alignrel[rtol,info,-15]

~position the incrementer to the left of the adjustment portal (plus a gap)
perform portal[value].alignrel[rtol,adjust,-10]

~position the name on the left and make sure its width does not exceed the available space
portal[name].left = 0
portal[name].width = minimum(portal[name].width,portal[value].left - portal[name].left - 10)

The adjustment is too small, so try switching to the "lblXLarge" style for the portal. That's way too big. We need something inbetween, but there is no label style with a font in the size we need. We'll have to define a new style to give us what we want. Open the file "styles_ui.aug" and locate the existing "lblLarge" style. Clone it and we can adapt it to our purposes. Assign it a new id of "lblAdjust" and change the font to "fntAdjust". Then we need to define the "fntAdjust" resource as part of the style, using a font size of about 50. The net result is a style definition like the one below.

<style
  id="lblAdjust">
  <style_label
    textcolor="f0f0f0"
    font="fntadjust"
    alignment="center">
    </style_label>
  <resource
    id="fntadjust">
    <font
      face="Arial"
      size="50"
      style="bold">
      </font>
    </resource>
  </style>

Once the style is defined, change the "adjust" portal over to use the new style. Then reload and see how things look. Not bad at all.

Modify The Skills Tab

We now need to do something comparable on the Skills tab to show any adjustments. Fortunately, that's an easy task. We can start by copying over the portal from the Basics tab, after which we'll need to change the style to "lblLarge" for a more suitable font size. The portal should look like the following.

<portal
  id="adjust"
  style="lblLarge">
  <label>
    <labeltext><![CDATA[
      if (field[trtNetRoll].value = 0) then
        @text = "{text 808080}" & chr(150)
      else
        @text = signed(field[trtNetRoll].value)
        endif
      ]]></labeltext>
    </label>
  </portal>

Once the portal is in place, we need to integrate the new portal into the template via the Position script. The revised Position script should end up looking similar to the following.

~set up our height based on our tallest portal
height = portal[info].height

~if this is a "sizing" calculation, we're done
if (issizing <> 0) then
  done
  endif

~freeze our value in advancement mode or if an advancement has modified us
~Note: All freezing must be done *before* any positioning is performed.
if (state.iscreate = 0) then
  portal[value].freeze = 1
elseif (autonomous = 0) then
  portal[value].freeze = 1
  endif

~position our tallest portal at the top
portal[info].top = 0

~position the other portals vertically
perform portal[name].centervert
perform portal[domain].centervert
perform portal[lbldomain].alignrel[btob,domain,0]
perform portal[value].centervert
perform portal[delete].centervert
perform portal[adjust].centervert

~position the delete portal on the far right
perform portal[delete].alignedge[right,0]

~position the info portal to the left of the delete button
perform portal[info].alignrel[rtol,delete,-8]

~position the incrementer on the left
portal[value].left = 0

~assume a standard width for the adjustment, since it can vary, then position
~the adjustment to the right of the incrementer
portal[adjust].width = 20
perform portal[adjust].alignrel[ltor,value,10]

~position the name next to the adjustment
perform portal[name].alignrel[ltor,adjust,10]

~if we don't need a domain, hide it and let the name use all available space
if (tagis[User.NeedDomain] = 0) then
  portal[lbldomain].visible = 0
  portal[domain].visible = 0
  portal[name].width = minimum(portal[name].width,portal[info].left - portal[name].left - 10)

~otherwise, position the domain portals next to the name
else
  perform portal[lbldomain].alignrel[ltor,name,20]
  perform portal[domain].alignrel[ltor,lbldomain,5]
  portal[domain].width = 150
  endif

~if the ability is auto-added, change its font to indicate that fact
if (candelete = 0) then
  perform portal[name].setstyle[lblAuto]
  endif

At this point, we now have the bitmaps tied in properly for use within incrementers, and the adjustments are cleanly separated and handled in a very smooth fashion.