Basic Vehicles (Savage): Difference between revisions
No edit summary |
|||
(14 intermediate revisions by the same user not shown) | |||
Line 77: | Line 77: | ||
===New Fields for Vehicles=== | ===New Fields for Vehicles=== | ||
Vehicles in Savage Worlds have a bunch of fields that need to handled properly. In addition to the fields used in the rulebook, we'll utilize the same mechanism we used for weapons and armor, in which we have a "notes" field where we synthesize all the details for display. Since we're going to be adding vehicles to the "Gear" tab, we can also associated the component with the appropriate panel. This yields an expanded definition for the "Vehicle" component that is defined in the file "equipment.str". The new component should look similar to below, including a suitable script for synthesizing the notes. | |||
<pre> | |||
<component | |||
id="Vehicle" | |||
name="Vehicle" | |||
panellink="gear" | |||
autocompset="no"> | |||
<field | |||
id="vhAccel" | |||
name="Acceleration" | |||
type="static" | |||
maxlength="10"> | |||
</field> | |||
<field | |||
id="vhTopSpeed" | |||
name="Top Speed" | |||
type="static" | |||
maxlength="15"> | |||
</field> | |||
<field | |||
id="vhClimb" | |||
name="Climb" | |||
type="static" | |||
maxlength="15"> | |||
</field> | |||
<field | |||
id="vhTough" | |||
name="Toughness" | |||
type="static" | |||
maxlength="10"> | |||
</field> | |||
<field | |||
id="vhArmor" | |||
name="Armor" | |||
type="static" | |||
maxlength="10"> | |||
</field> | |||
<field | |||
id="vhCrew" | |||
name="Crew" | |||
type="static" | |||
maxlength="10"> | |||
</field> | |||
<field | |||
id="vhSpecial" | |||
name="Special" | |||
type="derived" | |||
maxlength="200"> | |||
</field> | |||
<field | |||
id="vhNotes" | |||
name="Notes" | |||
type="derived" | |||
maxlength="250"> | |||
</field> | |||
<eval index="1" phase="Render" priority="1000"><![CDATA[ | |||
var special as string | |||
~append any special attributes appropriately (if any) | |||
var attribs as string | |||
attribs = tagnames[Vehicle.?,", "] | |||
if (empty(attribs) = 0) then | |||
if (empty(special) = 0) then | |||
special &= ", " | |||
endif | |||
special &= attribs | |||
endif | |||
~append any special details for this vehicle | |||
if (field[vhSpecial].isempty = 0) then | |||
if (empty(special) = 0) then | |||
special &= ", " | |||
endif | |||
special &= field[vhSpecial].text | |||
endif | |||
~we've synthesized the notes for the vehicle | |||
field[vhNotes].text = special | |||
]]></eval> | |||
</component> | |||
</pre> | |||
===Vehicle Cost as a Range=== | |||
There is a key difference with how vehicles behave from normal gear, though. Many vehicles are given a price range instead of an absolute price. If we want to support this, we need to figure out a way to integrate this different behavior into the way everything is handled for gear. In order to ensure that buy/sell transaction handling all works correctly, we need to retain the use of the "grCost" field and that field must specify an explicit value to be used as the default cost. | |||
Probably the easiest way to handle this is to define a new field in which to display the vehicle cost range. Then we simply need to decide what value we'll use for the explicit "grCost" field. The obvious choices are to always use the lowest price in the range, the highest price in the range, or the mid-point of the range. We'll standardize on the lowest price in the range. | |||
We now need to define the new field in which the cost range will be be given. This can be done with something simple like the field definition below. | |||
<pre> | |||
<field | |||
id="vhCost" | |||
name="Cost Range" | |||
type="static" | |||
maxlength="20"> | |||
</field> | |||
</pre> | |||
Once we've got the new field in place, we need to integrate it into the standard display handling of the cost for gear. This entails using it appropriately within the Finalize script for the "grCost" field. Within this script, if the gear is a vehicle, we need to retrieve the cost for display from our special "vhCost" field instead of the normal "grCost" field. However, we only do this for non-military vehicles. The solution is to add a few lines of code to the script that handle this appropriately, which results in the insertion of the following code immediately after the we check for a military cost. | |||
<pre> | |||
~if this is a vehicle, pull the cost from the special vehicle field | |||
if (tagis[component.Vehicle] <> 0) then | |||
@text = field[vhCost].text | |||
done | |||
endif | |||
</pre> | |||
There is one remaining detail we need to address. The "vhCost" field can be up to 20 characters long, while the maximum length of the finalized value for the "grCost" field is limited to 10 characters. Consequently, we need to increase the maximum size of the finalized text to accommodate the larger size of the "vhCost" field. | |||
===Description Output=== | ===Description Output=== | ||
Since vehicles have their own custom fields, we need to handle them specially when synthesizing detailed description text for display. So open up the file "procedures.dat" and locate the Descript procedure. The first thing we need to do is specify that the vehicle details are handled in a separate procedure. In the code where each component type is handled separately, add the lines shown below to invoke the proper procedure for vehicles. | |||
<pre> | |||
elseif (tagis[component.Vehicle] <> 0) then | |||
call InfoVeh | |||
</pre> | |||
Once this is done, we now need to define the appropriate procedure. Scroll down a bit further in the file and add a "InfoVeh" procedure that properly synthesizes the output for the new fields we added for vehicles. We want to keep the format looking similar to the one used in the book, so we'll combine the acceleration and top speed on one line, as well as combining the toughness and armor. We also need to include climb only for aircraft. The new procedure should look similar to the one shown below. | |||
<pre> | |||
<procedure id="InfoVeh" context="info"><![CDATA[ | |||
~declare variables that are used to communicate with our caller and for temporary use | |||
var iteminfo as string | |||
~report the acceleration and speed | |||
iteminfo = "Acc/Top Speed: " & field[vhAccel].text & "/" & field[vhTopSpeed].text & "{br}" | |||
~report the climb for aircraft | |||
if (tagis[VehType.Aircraft] <> 0) then | |||
iteminfo = "Climb: " & field[vhClimb].text & "{br}" | |||
endif | |||
~report the toughness and armor | |||
iteminfo &= "Toughness (Armor): " & field[vhTough].text & " (" & field[vhArmor].text & "){br}" | |||
~report the crew size | |||
iteminfo &= "Crew: " & field[vhCrew].text & "{br}" | |||
]]></procedure> | |||
</pre> | |||
===Selecting Vehicles Via Gear Tab=== | ===Selecting Vehicles Via Gear Tab=== | ||
The final step we need to deal with is making vehicles accessible to the user. We'll add vehicles to the "Gear" tab, since vehicles are in many ways more like standard gear in behavior and the "Armory" tab is already pretty packed with material. Our basic plan will be to clone the existing portal and templates for handling gear, then adapt them for use with vehicles. | |||
The first thing we need to do is create the new portal for showing the vehicles in a table. We can copy the existing "grGear" portal and then modify it to suit our purposes. In addition to changing the portal id, we need to change the template ids, the tag expression governing the contents managed, and the various scripts that must specifically reference "vehicles" now. The result should be something similar to the portal below. | |||
<pre> | |||
<portal | |||
id="grVehicle" | |||
style="tblNormal"> | |||
<table_dynamic | |||
component="Gear" | |||
showtemplate="grVehPick" | |||
choosetemplate="grVehThing" | |||
choosesortset="Vehicle" | |||
buytemplate="BuyCash" | |||
selltemplate="SellCash" | |||
descwidth="300"> | |||
<list>component.Vehicle</list> | |||
<titlebar><![CDATA[ | |||
@text = "Select Vehicles to Purchase from the List Below" | |||
]]></titlebar> | |||
<headertitle><![CDATA[ | |||
@text = "Miscellaneous Vehicles" | |||
]]></headertitle> | |||
<additem><![CDATA[ | |||
@text = "Add New Vehicle" | |||
]]></additem> | |||
</table_dynamic> | |||
</portal> | |||
</pre> | |||
The next step is to define the template used for showing vehicles as things. This template is virtually identical to the one for standard gear. So we can clone the "grGrThing" template and adapt for our purposes very easily, resulting in the template shown below. | |||
<pre> | |||
<template | |||
id="grVehThing" | |||
name="Vehicle Thing" | |||
compset="Vehicle" | |||
marginhorz="3" | |||
marginvert="2"> | |||
<portal | |||
id="name" | |||
style="lblNormal"> | |||
<label field="name"> | |||
</label> | |||
</portal> | |||
<portal | |||
id="cost" | |||
style="lblNormal"> | |||
<label> | |||
<labeltext><![CDATA[ | |||
@text = field[grCost].text | |||
]]></labeltext> | |||
</label> | |||
</portal> | |||
<position><![CDATA[ | |||
~set up our dimensions, with a width that we dictate | |||
height = portal[name].height | |||
width = 250 | |||
~if this is a "sizing" calculation, we're done | |||
if (issizing <> 0) then | |||
done | |||
endif | |||
~position the cost portal on the far right | |||
perform portal[cost].alignedge[right,0] | |||
~position the name on the left and let it use all available space | |||
portal[name].left = 0 | |||
portal[name].width = minimum(portal[name].width,portal[cost].left - 10) | |||
]]></position> | |||
</template> | |||
</pre> | |||
We still need to setup a template for showing vehicles as a pick. Vehicles are a special type of gear that lack a variety of standard gear behaviors. As such, we can copy the "grGrPick" template and strip it down to get what we want. For example, vehicles are namable by the user and are always top-level containers. After we strip out the pieces we don't need, we end up with a template that looks like the one below. | |||
<pre> | |||
<template | |||
id="grVehPick" | |||
name="Vehicle Pick" | |||
compset="Vehicle" | |||
marginhorz="3" | |||
marginvert="3"> | |||
<portal | |||
id="name" | |||
style="lblNormal" | |||
showinvalid="yes"> | |||
<label | |||
field="name"> | |||
</label> | |||
</portal> | |||
<portal | |||
id="container" | |||
style="imgNormal"> | |||
<image_literal | |||
image="container.bmp" | |||
istransparent="yes"> | |||
</image_literal> | |||
<mouseinfo mousepos="middle+above"><![CDATA[ | |||
call InfoHolder | |||
]]></mouseinfo> | |||
</portal> | |||
<portal | |||
id="info" | |||
style="actInfo"> | |||
<action | |||
action="info"> | |||
</action> | |||
<mouseinfo mousepos="middle+above"/> | |||
</portal> | |||
<portal | |||
id="delete" | |||
style="actDelete" | |||
tiptext="Click to delete this vehicle"> | |||
<action | |||
action="delete"> | |||
</action> | |||
</portal> | |||
<position><![CDATA[ | |||
~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 | |||
~center the portals vertically | |||
perform portal[info].centervert | |||
perform portal[name].centervert | |||
perform portal[delete].centervert | |||
perform portal[container].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 name on the left and let it use all available space | |||
var limit as number | |||
limit = portal[info].left - portal[container].width - 5 | |||
portal[name].left = 0 | |||
portal[name].width = minimum(portal[name].width,limit) | |||
~show the 'container' icon to the right of the name | |||
portal[container].left = portal[name].right + 5 | |||
~if the gear can't be deleted (i.e. it's been auto-added instead of user-added, | |||
~set the style to indicate that behavior to the user | |||
if (candelete = 0) then | |||
perform portal[name].setstyle[lblAuto] | |||
endif | |||
]]></position> | |||
</template> | |||
</pre> | |||
The final step is to add the table portal to the layout. The current layout contains a single portal, so the logic is trivial. Now that we're adding a new portal, we need to introduce appropriate logic to handle things cleanly. We'll position vehicles beneath standard gear, and we'll always make sure that we leave room to show at least two vehicles. Then we allow the standard gear to use as much of the remaining space is available. If the standard gear doesn't use all the space, we wrap up by having the vehicles use the remaining space. The net result is a layout that looks like the one below. | |||
<pre> | |||
<layout | |||
id="gear"> | |||
<portalref portal="grGear" taborder="10"/> | |||
<portalref portal="grVehicle" taborder="20"/> | |||
<!-- This script sizes and positions the layout and its child visual elements. --> | |||
<position><![CDATA[ | |||
~set all tables to span the full width of the layout | |||
portal[grGear].width = width | |||
portal[grVehicle].width = width | |||
~position the vehicles table at the bottom with a minimum height of 2 items | |||
portal[grVehicle].maxrows = 2 | |||
portal[grVehicle].top = height - portal[grVehicle].height | |||
~position and size the gear table to fill all remaining space | |||
portal[grGear].top = 0 | |||
portal[grGear].height = portal[grVehicle].top - 10 | |||
~position and size the vehicle table to use the remaining space at the bottom | |||
portal[grVehicle].top = portal[grGear].bottom + 10 | |||
portal[grVehicle].height = height - portal[grVehicle].top | |||
]]></position> | |||
</layout> | |||
</pre> | |||
===Adding Basic Vehicles=== | ===Adding Basic Vehicles=== | ||
Now that we've got all of the mechanics in place for basic vehicles, we can define them. Since there can be lots of vehicles, we'll put them in their own data file, which we'll call "thing_vehicles.dat". Either copy an existing data file and gut it of all things or create the file from scratch and setup the appropriate XML basics. | |||
Once the basic file framework is in place, we can begin defining the various vehicles. A few samples are shown below that can be used as a reference. Note that each has the minimum cost specified as the "grCost" field value. Appropriate tags are assigned to reflect the proper behavior and characteristics of each vehicle. | |||
<pre> | |||
<thing | |||
id="vhCarriage" | |||
name="Horse and Carriage" | |||
compset="Vehicle" | |||
description="Description goes here"> | |||
<fieldval field="grCost" value="1000"/> | |||
<fieldval field="vhAccel" value="Half-Pace"/> | |||
<fieldval field="vhTopSpeed" value="Pace+Running"/> | |||
<fieldval field="vhTough" value="10"/> | |||
<fieldval field="vhArmor" value="2"/> | |||
<fieldval field="vhCrew" value="1+3"/> | |||
<fieldval field="vhCost" value="$1-3,000"/> | |||
<usesource source="TimeMedi"/> | |||
<usesource source="TimePowder"/> | |||
<tag group="VehType" tag="Ground"/> | |||
<tag group="VehEra" tag="Civilian"/> | |||
<tag group="thing" tag="holder_top"/> | |||
</thing> | |||
<thing | |||
id="vhSUV" | |||
name="Sport Utility Vehicle" | |||
compset="Vehicle" | |||
description="Description goes here"> | |||
<fieldval field="grCost" value="20000"/> | |||
<fieldval field="vhAccel" value="20"/> | |||
<fieldval field="vhTopSpeed" value="40"/> | |||
<fieldval field="vhTough" value="14"/> | |||
<fieldval field="vhArmor" value="3"/> | |||
<fieldval field="vhCrew" value="1+7"/> | |||
<fieldval field="vhCost" value="$20-60,000"/> | |||
<fieldval field="vhSpecial" value="Luxury Features"/> | |||
<usesource source="TimeModern"/> | |||
<tag group="VehType" tag="Ground"/> | |||
<tag group="VehEra" tag="Civilian"/> | |||
<tag group="Vehicle" tag="AirBags"/> | |||
<tag group="Vehicle" tag="4WD"/> | |||
<tag group="thing" tag="holder_top"/> | |||
</thing> | |||
<thing | |||
id="vhBellJet" | |||
name="Bell Jet Ranger" | |||
compset="Vehicle" | |||
description="Description goes here"> | |||
<fieldval field="grCost" value="830000"/> | |||
<fieldval field="vhAccel" value="20"/> | |||
<fieldval field="vhTopSpeed" value="50"/> | |||
<fieldval field="vhClimb" value="20"/> | |||
<fieldval field="vhTough" value="11"/> | |||
<fieldval field="vhArmor" value="2"/> | |||
<fieldval field="vhCrew" value="2"/> | |||
<fieldval field="vhCost" value="$830,000"/> | |||
<usesource source="TimeModern"/> | |||
<tag group="VehType" tag="Aircraft"/> | |||
<tag group="VehEra" tag="Civilian"/> | |||
<tag group="thing" tag="holder_top"/> | |||
</thing> | |||
</pre> |
Latest revision as of 21:25, 28 January 2009
Context: HL Kit … Authoring Examples … Savage Worlds Walk-Through
Overview
There are two varieties of vehicles defined within the Savage Worlds rulebook. Basic vehicles have all the standard characteristics shared by all vehicles, such as civilian cars. Complex vehicles are outfitted with a variety of weapons that must be handled appropriately and introduce a number of additional wrinkles. So we'll focus on the mechanics associated with basic vehicle in this section.
New Tags
Vehicles behave similarly to weapons and armor within Savage Worlds. As such, we'll be using the same basic approach in implementing them. The first thing we'll need to address is the assortment of tags that will be needed for vehicles. After taking a moment to review how vehicles are handled, there are three different facets of vehicles that we need to keep distinct: the type, the era, and any special characteristics. So we'll create a separate tag group for each of these facets.
We'll start with the vehicle type, of which there are three basic types defined in the core rulebook. These are ground vehicles, aircraft, and boats. Since all of our tags are defined in the file "tags.1st", open that file and locate a suitable spot to insert the new tag group (e.g. after the armor-related tags). We'll make tag group dynamic so that supplements can define new vehicle types, if necessary. We'll use an explicit order, but we'll leave gaps in the ordering so that supplements can insert new vehicle types into the list wherever they want. This yields a tag group definition that looks like the one shown below.
<group id="VehType" dynamic="yes" sequence="explicit"> <value id="Ground" name="Ground Vehicles" order="10"/> <value id="Aircraft" order="20"/> <value id="Boat" name="Boats & Ships" order="30"/> </group>
Next up is the era of the vehicle, and there are four eras addressed in the core rulebook. These are civilian, WWII military, modern military, and futuristic military. We use the same principles as above, allowing for extensibility, and end up with a tag group similar to the one below.
<group id="VehEra" dynamic="yes" sequence="explicit"> <value id="Civilian" order="10"/> <value id="WWII" name="WWII Military" order="20"/> <value id="Modern" name="Modern Military" order="30"/> <value id="Future" name="Futuristic Military" order="40"/> </group>
The final tag group we need to define is all of the various special characteristics that apply to vehicles. We'll handle this exactly like we do for weapons and armor, which yields a tag group that looks like the one below.
<group id="Vehicle"> <value id="Amphib" name="Amphibious"/> <value id="Spacecraft"/> <value id="Atmosphere" name="Atmospheric"/> <value id="Tracked"/> <value id="4WD" name="Four-Wheel Drive"/> <value id="HvyArmor" name="Heavy Armor"/> <value id="Sloped" name="Sloped Armor"/> <value id="FixedGun" name="Fixed Gun"/> <value id="HvyWeapon" name="Heavy Weapon"/> <value id="AirBags" name="Air Bags"/> <value id="Stealth" name="Stealh Paint"/> <value id="AST" name="Advanced Stealth Tech"/> <value id="AMCM" name="Anti-Missile Counter Measures"/> <value id="Stabilizer" name="Stabilizer"/> <value id="ImpStabil" name="Improved Stabilizer"/> <value id="NightVis" name="Night Vision"/> <value id="Infrared" name="Infrared Night Vision"/> </group>
New Sort Set
Since we have a variety of factors for organizing vehicles, we're going to need a new sort set to put them in the proper sequence for display. We'll use a sequence that parallels the organization in the rulebook, so we'll sort first by the vehicle type, then the era, and finally by name. This yields a sort set similar to the one below, which can be defined with other sort sets in the file "control.1st".
<sortset id="Vehicle" name="Vehicles By Type, Era, and Name"> <sortkey isfield="no" id="VehType"/> <sortkey isfield="no" id="VehEra"/> <sortkey isfield="no" id="_Name_"/> </sortset>
New Fields for Vehicles
Vehicles in Savage Worlds have a bunch of fields that need to handled properly. In addition to the fields used in the rulebook, we'll utilize the same mechanism we used for weapons and armor, in which we have a "notes" field where we synthesize all the details for display. Since we're going to be adding vehicles to the "Gear" tab, we can also associated the component with the appropriate panel. This yields an expanded definition for the "Vehicle" component that is defined in the file "equipment.str". The new component should look similar to below, including a suitable script for synthesizing the notes.
<component id="Vehicle" name="Vehicle" panellink="gear" autocompset="no"> <field id="vhAccel" name="Acceleration" type="static" maxlength="10"> </field> <field id="vhTopSpeed" name="Top Speed" type="static" maxlength="15"> </field> <field id="vhClimb" name="Climb" type="static" maxlength="15"> </field> <field id="vhTough" name="Toughness" type="static" maxlength="10"> </field> <field id="vhArmor" name="Armor" type="static" maxlength="10"> </field> <field id="vhCrew" name="Crew" type="static" maxlength="10"> </field> <field id="vhSpecial" name="Special" type="derived" maxlength="200"> </field> <field id="vhNotes" name="Notes" type="derived" maxlength="250"> </field> <eval index="1" phase="Render" priority="1000"><![CDATA[ var special as string ~append any special attributes appropriately (if any) var attribs as string attribs = tagnames[Vehicle.?,", "] if (empty(attribs) = 0) then if (empty(special) = 0) then special &= ", " endif special &= attribs endif ~append any special details for this vehicle if (field[vhSpecial].isempty = 0) then if (empty(special) = 0) then special &= ", " endif special &= field[vhSpecial].text endif ~we've synthesized the notes for the vehicle field[vhNotes].text = special ]]></eval> </component>
Vehicle Cost as a Range
There is a key difference with how vehicles behave from normal gear, though. Many vehicles are given a price range instead of an absolute price. If we want to support this, we need to figure out a way to integrate this different behavior into the way everything is handled for gear. In order to ensure that buy/sell transaction handling all works correctly, we need to retain the use of the "grCost" field and that field must specify an explicit value to be used as the default cost.
Probably the easiest way to handle this is to define a new field in which to display the vehicle cost range. Then we simply need to decide what value we'll use for the explicit "grCost" field. The obvious choices are to always use the lowest price in the range, the highest price in the range, or the mid-point of the range. We'll standardize on the lowest price in the range.
We now need to define the new field in which the cost range will be be given. This can be done with something simple like the field definition below.
<field id="vhCost" name="Cost Range" type="static" maxlength="20"> </field>
Once we've got the new field in place, we need to integrate it into the standard display handling of the cost for gear. This entails using it appropriately within the Finalize script for the "grCost" field. Within this script, if the gear is a vehicle, we need to retrieve the cost for display from our special "vhCost" field instead of the normal "grCost" field. However, we only do this for non-military vehicles. The solution is to add a few lines of code to the script that handle this appropriately, which results in the insertion of the following code immediately after the we check for a military cost.
~if this is a vehicle, pull the cost from the special vehicle field if (tagis[component.Vehicle] <> 0) then @text = field[vhCost].text done endif
There is one remaining detail we need to address. The "vhCost" field can be up to 20 characters long, while the maximum length of the finalized value for the "grCost" field is limited to 10 characters. Consequently, we need to increase the maximum size of the finalized text to accommodate the larger size of the "vhCost" field.
Description Output
Since vehicles have their own custom fields, we need to handle them specially when synthesizing detailed description text for display. So open up the file "procedures.dat" and locate the Descript procedure. The first thing we need to do is specify that the vehicle details are handled in a separate procedure. In the code where each component type is handled separately, add the lines shown below to invoke the proper procedure for vehicles.
elseif (tagis[component.Vehicle] <> 0) then call InfoVeh
Once this is done, we now need to define the appropriate procedure. Scroll down a bit further in the file and add a "InfoVeh" procedure that properly synthesizes the output for the new fields we added for vehicles. We want to keep the format looking similar to the one used in the book, so we'll combine the acceleration and top speed on one line, as well as combining the toughness and armor. We also need to include climb only for aircraft. The new procedure should look similar to the one shown below.
<procedure id="InfoVeh" context="info"><![CDATA[ ~declare variables that are used to communicate with our caller and for temporary use var iteminfo as string ~report the acceleration and speed iteminfo = "Acc/Top Speed: " & field[vhAccel].text & "/" & field[vhTopSpeed].text & "{br}" ~report the climb for aircraft if (tagis[VehType.Aircraft] <> 0) then iteminfo = "Climb: " & field[vhClimb].text & "{br}" endif ~report the toughness and armor iteminfo &= "Toughness (Armor): " & field[vhTough].text & " (" & field[vhArmor].text & "){br}" ~report the crew size iteminfo &= "Crew: " & field[vhCrew].text & "{br}" ]]></procedure>
Selecting Vehicles Via Gear Tab
The final step we need to deal with is making vehicles accessible to the user. We'll add vehicles to the "Gear" tab, since vehicles are in many ways more like standard gear in behavior and the "Armory" tab is already pretty packed with material. Our basic plan will be to clone the existing portal and templates for handling gear, then adapt them for use with vehicles.
The first thing we need to do is create the new portal for showing the vehicles in a table. We can copy the existing "grGear" portal and then modify it to suit our purposes. In addition to changing the portal id, we need to change the template ids, the tag expression governing the contents managed, and the various scripts that must specifically reference "vehicles" now. The result should be something similar to the portal below.
<portal id="grVehicle" style="tblNormal"> <table_dynamic component="Gear" showtemplate="grVehPick" choosetemplate="grVehThing" choosesortset="Vehicle" buytemplate="BuyCash" selltemplate="SellCash" descwidth="300"> <list>component.Vehicle</list> <titlebar><![CDATA[ @text = "Select Vehicles to Purchase from the List Below" ]]></titlebar> <headertitle><![CDATA[ @text = "Miscellaneous Vehicles" ]]></headertitle> <additem><![CDATA[ @text = "Add New Vehicle" ]]></additem> </table_dynamic> </portal>
The next step is to define the template used for showing vehicles as things. This template is virtually identical to the one for standard gear. So we can clone the "grGrThing" template and adapt for our purposes very easily, resulting in the template shown below.
<template id="grVehThing" name="Vehicle Thing" compset="Vehicle" marginhorz="3" marginvert="2"> <portal id="name" style="lblNormal"> <label field="name"> </label> </portal> <portal id="cost" style="lblNormal"> <label> <labeltext><![CDATA[ @text = field[grCost].text ]]></labeltext> </label> </portal> <position><![CDATA[ ~set up our dimensions, with a width that we dictate height = portal[name].height width = 250 ~if this is a "sizing" calculation, we're done if (issizing <> 0) then done endif ~position the cost portal on the far right perform portal[cost].alignedge[right,0] ~position the name on the left and let it use all available space portal[name].left = 0 portal[name].width = minimum(portal[name].width,portal[cost].left - 10) ]]></position> </template>
We still need to setup a template for showing vehicles as a pick. Vehicles are a special type of gear that lack a variety of standard gear behaviors. As such, we can copy the "grGrPick" template and strip it down to get what we want. For example, vehicles are namable by the user and are always top-level containers. After we strip out the pieces we don't need, we end up with a template that looks like the one below.
<template id="grVehPick" name="Vehicle Pick" compset="Vehicle" marginhorz="3" marginvert="3"> <portal id="name" style="lblNormal" showinvalid="yes"> <label field="name"> </label> </portal> <portal id="container" style="imgNormal"> <image_literal image="container.bmp" istransparent="yes"> </image_literal> <mouseinfo mousepos="middle+above"><![CDATA[ call InfoHolder ]]></mouseinfo> </portal> <portal id="info" style="actInfo"> <action action="info"> </action> <mouseinfo mousepos="middle+above"/> </portal> <portal id="delete" style="actDelete" tiptext="Click to delete this vehicle"> <action action="delete"> </action> </portal> <position><![CDATA[ ~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 ~center the portals vertically perform portal[info].centervert perform portal[name].centervert perform portal[delete].centervert perform portal[container].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 name on the left and let it use all available space var limit as number limit = portal[info].left - portal[container].width - 5 portal[name].left = 0 portal[name].width = minimum(portal[name].width,limit) ~show the 'container' icon to the right of the name portal[container].left = portal[name].right + 5 ~if the gear can't be deleted (i.e. it's been auto-added instead of user-added, ~set the style to indicate that behavior to the user if (candelete = 0) then perform portal[name].setstyle[lblAuto] endif ]]></position> </template>
The final step is to add the table portal to the layout. The current layout contains a single portal, so the logic is trivial. Now that we're adding a new portal, we need to introduce appropriate logic to handle things cleanly. We'll position vehicles beneath standard gear, and we'll always make sure that we leave room to show at least two vehicles. Then we allow the standard gear to use as much of the remaining space is available. If the standard gear doesn't use all the space, we wrap up by having the vehicles use the remaining space. The net result is a layout that looks like the one below.
<layout id="gear"> <portalref portal="grGear" taborder="10"/> <portalref portal="grVehicle" taborder="20"/> <!-- This script sizes and positions the layout and its child visual elements. --> <position><![CDATA[ ~set all tables to span the full width of the layout portal[grGear].width = width portal[grVehicle].width = width ~position the vehicles table at the bottom with a minimum height of 2 items portal[grVehicle].maxrows = 2 portal[grVehicle].top = height - portal[grVehicle].height ~position and size the gear table to fill all remaining space portal[grGear].top = 0 portal[grGear].height = portal[grVehicle].top - 10 ~position and size the vehicle table to use the remaining space at the bottom portal[grVehicle].top = portal[grGear].bottom + 10 portal[grVehicle].height = height - portal[grVehicle].top ]]></position> </layout>
Adding Basic Vehicles
Now that we've got all of the mechanics in place for basic vehicles, we can define them. Since there can be lots of vehicles, we'll put them in their own data file, which we'll call "thing_vehicles.dat". Either copy an existing data file and gut it of all things or create the file from scratch and setup the appropriate XML basics.
Once the basic file framework is in place, we can begin defining the various vehicles. A few samples are shown below that can be used as a reference. Note that each has the minimum cost specified as the "grCost" field value. Appropriate tags are assigned to reflect the proper behavior and characteristics of each vehicle.
<thing id="vhCarriage" name="Horse and Carriage" compset="Vehicle" description="Description goes here"> <fieldval field="grCost" value="1000"/> <fieldval field="vhAccel" value="Half-Pace"/> <fieldval field="vhTopSpeed" value="Pace+Running"/> <fieldval field="vhTough" value="10"/> <fieldval field="vhArmor" value="2"/> <fieldval field="vhCrew" value="1+3"/> <fieldval field="vhCost" value="$1-3,000"/> <usesource source="TimeMedi"/> <usesource source="TimePowder"/> <tag group="VehType" tag="Ground"/> <tag group="VehEra" tag="Civilian"/> <tag group="thing" tag="holder_top"/> </thing> <thing id="vhSUV" name="Sport Utility Vehicle" compset="Vehicle" description="Description goes here"> <fieldval field="grCost" value="20000"/> <fieldval field="vhAccel" value="20"/> <fieldval field="vhTopSpeed" value="40"/> <fieldval field="vhTough" value="14"/> <fieldval field="vhArmor" value="3"/> <fieldval field="vhCrew" value="1+7"/> <fieldval field="vhCost" value="$20-60,000"/> <fieldval field="vhSpecial" value="Luxury Features"/> <usesource source="TimeModern"/> <tag group="VehType" tag="Ground"/> <tag group="VehEra" tag="Civilian"/> <tag group="Vehicle" tag="AirBags"/> <tag group="Vehicle" tag="4WD"/> <tag group="thing" tag="holder_top"/> </thing> <thing id="vhBellJet" name="Bell Jet Ranger" compset="Vehicle" description="Description goes here"> <fieldval field="grCost" value="830000"/> <fieldval field="vhAccel" value="20"/> <fieldval field="vhTopSpeed" value="50"/> <fieldval field="vhClimb" value="20"/> <fieldval field="vhTough" value="11"/> <fieldval field="vhArmor" value="2"/> <fieldval field="vhCrew" value="2"/> <fieldval field="vhCost" value="$830,000"/> <usesource source="TimeModern"/> <tag group="VehType" tag="Aircraft"/> <tag group="VehEra" tag="Civilian"/> <tag group="thing" tag="holder_top"/> </thing>