Special Weapons (Savage): Difference between revisions

From HLKitWiki
Jump to navigationJump to search
No edit summary
 
(One intermediate revision by the same user not shown)
Line 7: Line 7:
===New Component and Component Set===
===New Component and Component Set===


Special weapons are virtually the same as ranged weapons in terms of what we need to manage them properly. However, it's critical that we clearly delineate between ranged weapons and special weapons. Consequently, we need to define a new component and component set for special weapons. Provided that we ensure that we have a clear and simple means of distinguishing between ranged and special weapons, we are free to re-use all the logic for ranged weapons. This allows us to define a component for special weapons that does nothing and use the "WeapRange" component in our new component set. We can rely on the automatically defined component tag to identify a special weapon distinctly from a ranged weapon. Based on this, our new component and component set should look similar to the following.  
Special weapons are virtually the same as ranged weapons in terms of what is needed to manage them properly. However, it's critical that we clearly delineate between ranged weapons and special weapons. Consequently, we need to define a new component and component set for special weapons. Provided that we ensure that we have a clear and simple means of distinguishing between ranged and special weapons, we are free to re-use all the logic for ranged weapons. This allows us to define a component for special weapons that does '''nothing''' and re-use the "WeapRange" component in our new component set. We can rely on the automatically defined component tag to identify a special weapon distinctly from a ranged weapon. Based on this, our new component and component set should look similar to the following.  


<pre>
<pre>
Line 19: Line 19:
   id="SpecWeap"
   id="SpecWeap"
   stackable="yes">
   stackable="yes">
   <compref component="BaseWeapon" primary="yes"/>
   <compref component="BaseWeapon"/>
   <compref component="WeapRange"/>
   <compref component="WeapRange"/>
   <compref component="WeapSpec"/>
   <compref component="WeapSpec"/>
Line 31: Line 31:
All special weapons will identify themselves as ranged weapons now because both possess the "component.WeapRange" tag. Anywhere that we currently rely on this tag to identify a ranged weapon needs to be revised to properly exclude special weapons. In order to determine whether a given weapon is solely a ranged weapon, we need to verify that it possesses the "component.WeapRange" tag and that it does not possess the "component.WeapSpec" tag. 
All special weapons will identify themselves as ranged weapons now because both possess the "component.WeapRange" tag. Anywhere that we currently rely on this tag to identify a ranged weapon needs to be revised to properly exclude special weapons. In order to determine whether a given weapon is solely a ranged weapon, we need to verify that it possesses the "component.WeapRange" tag and that it does not possess the "component.WeapSpec" tag. 


Scanning through our data files, most references to the component tag are simply to determine whether to show range information for the weapon, so we need to preserve those references intact. The only case where we need to actively apply this new restriction is within the "arRange" table portal on the "Armory" tab, which will be found in the file "tab_armory.dat". Both the List and Candidate tag expressions for the table must be revised to explicitly exclude things that possess the "component.WeapSpec" tag. The new versions of each of these elements is shown below.
Scanning through our data files, most references to the component tag are simply to determine whether to show range information for the weapon, so we need to preserve those references intact. The only case where we need to actively apply this new restriction is within the "arRange" table portal on the "Armory" tab, which will be found in the file "tab_armory.dat". The List tag expression for the table must be revised to explicitly exclude things that possess the "component.WeapSpec" tag. The new version of this element is shown below.


<pre>
<pre>
<list>component.WeapRange &amp; !component.WeapSpec</list>
<list>component.WeapRange &amp; !component.WeapSpec</list>
<candidate>component.WeapRange &amp; !component.WeapSpec &amp; !Equipment.Natural</candidate>
</pre>
</pre>


Line 52: Line 51:
</pre>
</pre>


In order to sort special weapons by grouping in the same way that we do for other weapons, we'll need to define a number of new "WeaponType" tags. Each new tag will correspond to a grouping that is used within the Savage Worlds rulebook, and we'll also preserve the order used within the rulebook. The new tags should consist of the following:
In order to sort special weapons by grouping in the same way that we do for other weapons, we'll need to define a number of new "WeaponType" tags in the file "tags.1st". Each new tag will correspond to a grouping that is used within the Savage Worlds rulebook, and we'll also preserve the order used within the rulebook. The new tags should consist of the following:


<pre>
<pre>
Line 74: Line 73:
===Table for Special Weapons===
===Table for Special Weapons===


All of the mechanics are now in place for special weapons, but we still need to make them accessible to the user. We'll add a new table portal to the "Armory" tab for this purpose. Since all of the behaviors will be very similar to those for the existing ranged weapons table, we'll start by copying the "arRange" portal. We can then rename it to "arSpecial" and change it to only list things belonging to the "WeapSpec" component. We need to revise the List and Candidate tag expressions to only process the appropriate weapons, then we can change the various strings used in scripts to properly indicate that we're manipulating special weapons instead of ranged weapons. Everything else can remain the same. Putting it all together yields the revised table portal shown below.
All of the mechanics are now in place for special weapons, but we still need to make them accessible to the user. We'll add a new table portal to the "Armory" tab for this purpose. Since all of the behaviors will be very similar to those for the existing ranged weapons table, we'll start by copying the "arRange" portal. We can then rename it to "arSpecial" and change it to only list things belonging to the "WeapSpec" component. We need to revise the List tag expression to only process the appropriate weapons, then we can change the various strings used in scripts to properly indicate that we're manipulating special weapons instead of ranged weapons. Everything else can remain the same. Putting it all together yields the revised table portal shown below.


<pre>
<pre>
Line 85: Line 84:
     choosetemplate="arWpnThing"
     choosetemplate="arWpnThing"
     choosesortset="Weapon"
     choosesortset="Weapon"
    addspace="2"
     buytemplate="BuyCash"
     buytemplate="BuyCash"
     selltemplate="SellCash"
     selltemplate="SellCash"
     descwidth="275">
     descwidth="275">
     <list>component.WeapSpec</list>
     <list>component.WeapSpec</list>
     <candidate>component.WeapSpec &amp; !Equipment.Natural</candidate>
     <candidate inheritlist="yes">!Equipment.Natural</candidate>
     <description><![CDATA[
     <titlebar><![CDATA[
      var descript as string
       @text = "Select Special Weapons to Purchase from the List Below"
      call Descript
       ]]></titlebar>
       @text = descript
       ]]></description>
     <headertitle><![CDATA[
     <headertitle><![CDATA[
       @text = "Special Weapons"
       @text = "Special Weapons"
Line 106: Line 102:
</pre>
</pre>


We've got a new table portal defined now, so we need to integrate it into the layout that controls what's shown for the tab. This entails adding it to the layout and then properly sizing and positioning the new portal. We'll put it at the bottom of the other portals, since the others are more likely to see regular access by users. We'll reserve space to show at least two items in the table, unless there are fewer items selected by the user. At the end, if there is still unused space left, we'll extend the table downward to use up whatever space is available. This makes it possible to preserve all of the existing logic for adaptively sizing the other table portals within the layout. All we need to do is integrate our new portal in appropriately. The revised layout element that cleanly integrates the new portal is presented below.  
===Positioning Everything===
 
We've got a new table portal defined now, so we need to integrate it into the layout that controls what's shown for the tab. This entails adding an appropriate "portalref" element to the layout and then properly sizing and positioning the new portal. We'll put the table at the bottom of the other portals, since the others are more likely to see regular access by users. We'll reserve space to show at least two items in the table, unless there are fewer items selected by the user. At the end, if there is still unused space left, we'll extend the table downward to use up whatever space is available. This makes it possible to preserve all of the existing logic for adaptively sizing the other table portals within the layout. All we need to do is integrate our new portal in appropriately. The revised layout element that cleanly integrates the new portal is presented below.  


<pre>
<pre>
Line 116: Line 114:
   <portalref portal="arSpecial" taborder="40"/>  
   <portalref portal="arSpecial" taborder="40"/>  
   <position><![CDATA[
   <position><![CDATA[
    ~determine the gap to use between tables
    var gap as number
    gap = 10
     ~set the width of all tables to the full width of the layout
     ~set the width of all tables to the full width of the layout
     portal[arMelee].width = width
     portal[arMelee].width = width
Line 121: Line 123:
     portal[arDefense].width = width
     portal[arDefense].width = width
     portal[arSpecial].width = width  
     portal[arSpecial].width = width  
    ~determine the gap to use between tables
 
    var gap as number
    gap = 10
     ~position the special weapons table at the bottom, allowing for at most two rows
     ~position the special weapons table at the bottom, allowing for at most two rows
     portal[arSpecial].maxrows = 2
     portal[arSpecial].maxrows = 2
     portal[arSpecial].top = height - portal[arSpecial].height  
     portal[arSpecial].top = height - portal[arSpecial].height  
     ~determine the height remaining that can be used by other tables
     ~determine the height remaining that can be used by other tables
     var ht as number
     var ht as number
     ht = height - portal[arSpecial].height - gap  
     ht = height - portal[arSpecial].height - gap  
     ~position the armor/shield table above special weapons, allowing for at most two rows
     ~position the armor/shield table above special weapons, allowing for at most two rows
     portal[arDefense].maxrows = 2
     portal[arDefense].maxrows = 2
     portal[arDefense].top = ht - portal[arDefense].height  
     portal[arDefense].top = ht - portal[arDefense].height  
     ~position the melee table at the top
     ~position the melee table at the top
     portal[arMelee].top = 0  
     portal[arMelee].top = 0  
     ~set the heights of the two weapon tables to use all the space available
     ~set the heights of the two weapon tables to use all the space available
     portal[arMelee].height = ht
     portal[arMelee].height = ht
     portal[arRange].height = ht  
     portal[arRange].height = ht  
     ~determine how much space we have left for the two tables; be sure to exclude
     ~determine how much space we have left for the two tables; be sure to exclude
     ~the extra title and the extra spacing we'll use inbetween
     ~the extra title and the extra spacing we'll use inbetween
Line 150: Line 155:
     remain = portal[arDefense].top - portal[arMelee].top
     remain = portal[arDefense].top - portal[arMelee].top
     remain -= (gap - 1) * 2  
     remain -= (gap - 1) * 2  
     ~if the height of both tables exceeds what we have left, we need to divvy up
     ~if the height of both tables exceeds what we have left, we need to divvy up
     ~that space between the two tables
     ~that space between the two tables
     if (portal[arMelee].height + portal[arRange].height > remain) then  
     if (portal[arMelee].height + portal[arRange].height > remain) then  
       ~if the melee table is less than half the space, limit the ranged table
       ~if the melee table is less than half the space, limit the ranged table
       ~to whatever space is leftover
       ~to whatever space is leftover
       if (portal[arMelee].height < remain / 2) then
       if (portal[arMelee].height < remain / 2) then
         portal[arRange].height = remain - portal[arMelee].height  
         portal[arRange].height = remain - portal[arMelee].height  
       ~if the ranged table is less than half the space, limit the melee table
       ~if the ranged table is less than half the space, limit the melee table
       ~to whatever space is leftover
       ~to whatever space is leftover
       elseif (portal[arRange].height < remain / 2) then
       elseif (portal[arRange].height < remain / 2) then
         portal[arMelee].height = remain - portal[arRange].height  
         portal[arMelee].height = remain - portal[arRange].height  
       ~otherwise, both tables are larger than half the space, so we need to limit
       ~otherwise, both tables are larger than half the space, so we need to limit
       ~the height of both of them
       ~the height of both of them
Line 174: Line 183:
         endif
         endif
       endif  
       endif  
     ~position the ranged weapons table beneath the melee table
     ~position the ranged weapons table beneath the melee table
     portal[arRange].top = portal[arMelee].bottom + gap  
     portal[arRange].top = portal[arMelee].bottom + gap  
     ~position the armor/shields table beneath the ranged weapons table
     ~position the armor/shields table beneath the ranged weapons table
     ~NOTE! we already positioned this table, but the above logic could result in
     ~NOTE! we already positioned this table, but the above logic could result in
     ~a gap between the tables, so we close that gap by repositioning again
     ~a gap between the tables, so we close that gap by repositioning again
     portal[arDefense].top = portal[arRange].bottom + gap  
     portal[arDefense].top = portal[arRange].bottom + gap  
     ~set the height of the armor/shields table to the whatever height is left;
     ~set the height of the armor/shields table to the whatever height is left;
     ~if the armor list is long and the weapon lists are short, this will show as
     ~if the armor list is long and the weapon lists are short, this will show as
     ~much armor as there is remaining room to accommodate
     ~much armor as there is remaining room to accommodate
     portal[arDefense].height = ht - portal[arDefense].top  
     portal[arDefense].height = ht - portal[arDefense].top  
     ~position the special weapons table beneath the armor/shields table
     ~position the special weapons table beneath the armor/shields table
     portal[arSpecial].top = portal[arDefense].bottom + gap  
     portal[arSpecial].top = portal[arDefense].bottom + gap  
     ~set the height of the special weapons table to the whatever height is left;
     ~set the height of the special weapons table to the whatever height is left;
     ~if the above tables are short, this will show as many special weapons as
     ~if the above tables are short, this will show as many special weapons as

Latest revision as of 12:24, 18 December 2008

Context: HL Kit &#133; Authoring Examples &#133; Savage Worlds Walk-Through 

Overview

Now that all the basic gear is in place and being handled properly, we can go back and start adding special kinds of gear. We'll start with the various "Special Weapons" given in the Savage Worlds rulebook.

New Component and Component Set

Special weapons are virtually the same as ranged weapons in terms of what is needed to manage them properly. However, it's critical that we clearly delineate between ranged weapons and special weapons. Consequently, we need to define a new component and component set for special weapons. Provided that we ensure that we have a clear and simple means of distinguishing between ranged and special weapons, we are free to re-use all the logic for ranged weapons. This allows us to define a component for special weapons that does nothing and re-use the "WeapRange" component in our new component set. We can rely on the automatically defined component tag to identify a special weapon distinctly from a ranged weapon. Based on this, our new component and component set should look similar to the following.

<component
  id="WeapSpec"
  name="Special Weapon"
  autocompset="no">
  </component> 

<compset
  id="SpecWeap"
  stackable="yes">
  <compref component="BaseWeapon"/>
  <compref component="WeapRange"/>
  <compref component="WeapSpec"/>
  <compref component="Equippable"/>
  <compref component="Gear"/>
  </compset> 

Distinguish from Ranged Weapons

All special weapons will identify themselves as ranged weapons now because both possess the "component.WeapRange" tag. Anywhere that we currently rely on this tag to identify a ranged weapon needs to be revised to properly exclude special weapons. In order to determine whether a given weapon is solely a ranged weapon, we need to verify that it possesses the "component.WeapRange" tag and that it does not possess the "component.WeapSpec" tag. 

Scanning through our data files, most references to the component tag are simply to determine whether to show range information for the weapon, so we need to preserve those references intact. The only case where we need to actively apply this new restriction is within the "arRange" table portal on the "Armory" tab, which will be found in the file "tab_armory.dat". The List tag expression for the table must be revised to explicitly exclude things that possess the "component.WeapSpec" tag. The new version of this element is shown below.

<list>component.WeapRange & !component.WeapSpec</list>

New Behaviors

There are a variety of new behaviors the must be handled in support of special weapons. The first new behavior is that some special weapons possess a standard range, while others (e.g. mines) have no range. For the latter group of special weapons, we need to appropriately deal with the lack of range by displaying something appropriate. The "WeapRange" component already possesses a "wpRange" field that uses a Finalize script to synthesize the range appropriately. So we'll modify this Finalize script to handle this new case, resulting in a revised script that looks similar to the following.

if (tagis[Weapon.SpecRange] <> 0) then
  @text = "Special"
elseif (field[wpShort].value + field[wpMedium].value + field[wpLong].value = 0) then
  @text = "--"
else
  @text = field[wpShort].value & "/" & field[wpMedium].value & "/" & field[wpLong].value
  endif

In order to sort special weapons by grouping in the same way that we do for other weapons, we'll need to define a number of new "WeaponType" tags in the file "tags.1st". Each new tag will correspond to a grouping that is used within the Savage Worlds rulebook, and we'll also preserve the order used within the rulebook. The new tags should consist of the following:

<value id="SpecCannon" name="Special: Cannons" order="20"/>
<value id="SpecRocket" name="Special: Rocket Launchers" order="21"/>
<value id="SpecMines" name="Special: Mines" order="22"/>
<value id="SpecFlame" name="Special: Flamethrowers" order="23"/>
<value id="SpecGren" name="Special: Grenades" order="24"/>
<value id="SpecExplos" name="Special: Explosives" order="25"/> 

The final new behavior that we must handle is an assortment of new special abilities. The new abilities are those pertaining to the various templates that are used by the different weapons. By adding new "Weapon" tags for these abilities, simply assigning the tags to the weapons will automatically incorporate the details into the description and other output for each weapon, thanks to the revisions we made earlier. The new tags should consist of the following:

<value id="MedBurst" name="Medium Burst Template"/>
<value id="SmallBurst" name="Small Burst Template"/>
<value id="LargeBurst" name="Large Burst Template"/>
<value id="ConeTempl" name="Cone Template"/> 

Table for Special Weapons

All of the mechanics are now in place for special weapons, but we still need to make them accessible to the user. We'll add a new table portal to the "Armory" tab for this purpose. Since all of the behaviors will be very similar to those for the existing ranged weapons table, we'll start by copying the "arRange" portal. We can then rename it to "arSpecial" and change it to only list things belonging to the "WeapSpec" component. We need to revise the List tag expression to only process the appropriate weapons, then we can change the various strings used in scripts to properly indicate that we're manipulating special weapons instead of ranged weapons. Everything else can remain the same. Putting it all together yields the revised table portal shown below.

<portal
  id="arSpecial"
  style="tblNormal">
  <table_dynamic
    component="WeapSpec"
    showtemplate="arWpnPick"
    choosetemplate="arWpnThing"
    choosesortset="Weapon"
    buytemplate="BuyCash"
    selltemplate="SellCash"
    descwidth="275">
    <list>component.WeapSpec</list>
    <candidate inheritlist="yes">!Equipment.Natural</candidate>
    <titlebar><![CDATA[
      @text = "Select Special Weapons to Purchase from the List Below"
      ]]></titlebar>
    <headertitle><![CDATA[
      @text = "Special Weapons"
      ]]></headertitle>
    <additem><![CDATA[
      @text = "Add New Special Weapons"
      ]]></additem>
    </table_dynamic>
  </portal> 

Positioning Everything

We've got a new table portal defined now, so we need to integrate it into the layout that controls what's shown for the tab. This entails adding an appropriate "portalref" element to the layout and then properly sizing and positioning the new portal. We'll put the table at the bottom of the other portals, since the others are more likely to see regular access by users. We'll reserve space to show at least two items in the table, unless there are fewer items selected by the user. At the end, if there is still unused space left, we'll extend the table downward to use up whatever space is available. This makes it possible to preserve all of the existing logic for adaptively sizing the other table portals within the layout. All we need to do is integrate our new portal in appropriately. The revised layout element that cleanly integrates the new portal is presented below.

<layout
  id="armory">
  <portalref portal="arMelee" taborder="10"/>
  <portalref portal="arRange" taborder="20"/>
  <portalref portal="arDefense" taborder="30"/>
  <portalref portal="arSpecial" taborder="40"/> 
  <position><![CDATA[
    ~determine the gap to use between tables
    var gap as number
    gap = 10 

    ~set the width of all tables to the full width of the layout
    portal[arMelee].width = width
    portal[arRange].width = width
    portal[arDefense].width = width
    portal[arSpecial].width = width 

    ~position the special weapons table at the bottom, allowing for at most two rows
    portal[arSpecial].maxrows = 2
    portal[arSpecial].top = height - portal[arSpecial].height 

    ~determine the height remaining that can be used by other tables
    var ht as number
    ht = height - portal[arSpecial].height - gap 

    ~position the armor/shield table above special weapons, allowing for at most two rows
    portal[arDefense].maxrows = 2
    portal[arDefense].top = ht - portal[arDefense].height 

    ~position the melee table at the top
    portal[arMelee].top = 0 

    ~set the heights of the two weapon tables to use all the space available
    portal[arMelee].height = ht
    portal[arRange].height = ht 

    ~determine how much space we have left for the two tables; be sure to exclude
    ~the extra title and the extra spacing we'll use inbetween
    ~NOTE! If a value of 10 is added to the bottom coordinate of a portal, the
    ~net value will yield an actual GAP of one less. For example, if the bottom
    ~is at pixel 15, that pixel is part of the physical height of the portal. If
    ~you add 10 to that position for the next portal, it starts on pixel 25, so
    ~pixel 25 is part of the next portal. That means that pixels 16 through 24
    ~represent the dead space inbetween, which is a span of 9 pixels. We have to
    ~factor this detail in when adjusting the space remaining by our gaps.
    var remain as number
    remain = portal[arDefense].top - portal[arMelee].top
    remain -= (gap - 1) * 2 

    ~if the height of both tables exceeds what we have left, we need to divvy up
    ~that space between the two tables
    if (portal[arMelee].height + portal[arRange].height > remain) then 

      ~if the melee table is less than half the space, limit the ranged table
      ~to whatever space is leftover
      if (portal[arMelee].height < remain / 2) then
        portal[arRange].height = remain - portal[arMelee].height 

      ~if the ranged table is less than half the space, limit the melee table
      ~to whatever space is leftover
      elseif (portal[arRange].height < remain / 2) then
        portal[arMelee].height = remain - portal[arRange].height 

      ~otherwise, both tables are larger than half the space, so we need to limit
      ~the height of both of them
      ~NOTE! If we just divide the remaining amount by two and set both tables to
      ~that height, we could end up with both tables being truncated by more than
      ~a half item, with the combined height being a full item short of taking up
      ~the full space. So we have to set the height of one table to half the
      ~remaining space, then subtract that table's final height from our remaining
      ~space, and finally set that as the height for the second table.
      else
        portal[arRange].height = remain / 2
        portal[arMelee].height = remain - portal[arRange].height
        endif
      endif 

    ~position the ranged weapons table beneath the melee table
    portal[arRange].top = portal[arMelee].bottom + gap 

    ~position the armor/shields table beneath the ranged weapons table
    ~NOTE! we already positioned this table, but the above logic could result in
    ~a gap between the tables, so we close that gap by repositioning again
    portal[arDefense].top = portal[arRange].bottom + gap 

    ~set the height of the armor/shields table to the whatever height is left;
    ~if the armor list is long and the weapon lists are short, this will show as
    ~much armor as there is remaining room to accommodate
    portal[arDefense].height = ht - portal[arDefense].top 

    ~position the special weapons table beneath the armor/shields table
    portal[arSpecial].top = portal[arDefense].bottom + gap 

    ~set the height of the special weapons table to the whatever height is left;
    ~if the above tables are short, this will show as many special weapons as
    ~there is remaining room to accommodate
    portal[arSpecial].height = height - portal[arSpecial].top
    ]]></position> 
  </layout>