Knowing how the language is put together and how to make changes is all well and good, but it doesn't really give the budding plugin author a place to really begin. That's what this section is for; we'll begin with the bare minimum required to get a scenario up and running and eventually progress until we've created a full-fledged scenario that illustrates all of TraderMissions' features in addition to pointing them out to the novice. That's right; this tutorial will put together most of the "How to Play TraderMissions" tutorial that ships with the game! There's a great deal of code up ahead, and we'll be giving a general idea of what it's used for, but if you want details you'll need to consult the reference. To help with that, each time a different object type is mentioned for the first time, a link will be provided to its section of the reference.
Every time we make a change to an object, I'll print the entire object's definition. This is to prevent me from saying "Now find your ship and add this line" which, while easier for me, would probably annoy you. Of course, you probably wouldn't be pleased to have to look through a huge definition for new or changed items, so any re-printing of an object's definition will have a comment at the end of the line (specifically "#NEW") to indicate added or changed items. Lines to be deleted will simply be commented out.
If you're going to test the scenario we're creating as we go along, which I reccommend you do, then you'll probably want to save it in your .tradermissions directory so that TM can find it. Be sure to name it something like 'mytutorial.tmd' so you know it from the original tutorial (and, more importantly, so the game itself doesn't get confused!)
We'll begin with the name and description of the scenario:
# The Tutorial Tutorial # # If you're new to TraderMissions, here's the scenario you'll want to try # first. To do so, select it from the listbox, hit 'add' and then press # "Launch Game"
The first line of the file, if commented, is the name of the scenario as displayed in the 'available' listbox. (Note that we've changed it from the original tutorial's title "How to Play TraderMissions". If we hadn't, there would have been two such scenarios on our list!) Any commented lines after that are the description, which will end with the first non-commented line.
Next, we will create the basic gamestate( reference), and work backwards from there:
gamestate <gamestate> { child map.add OurMap; child curSector.add Sol; child orgs.add Player; child orgs.add Humans; child playerOrg.add Player; child hud.add hud; child activeship.add PlayerShip; field money = 1000; field day = 1; }
Note the name of the gamestate object (conveniently named "gamestate"). Normally, you can name the objects whatever you wish, but TraderMissions must be able to find the gamestate to know where to start, so it must always be named "gamestate".
The game has to be able to keep track of every sector in the universe; it does this via a Map object ( reference). You can have more than one map object in a file if you want, though only one can be active at a time (namely, whichever is specified last in the gamestate's 'map' child)
OurMap <map> { child sectors.add Sol; }
This looks small because it's the bare minimum: one sector. If there are more sectors in the universe, they too must be added to the 'sectors' child.
Sectors ( reference) are where the action is; though you're likely to spend time on planets as well, you're far less likely to get blown up on them. For the purposes of the 'bare minimum' tutorial, we have only one sector, without any planets. It's a lonely, lonely sector:
Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; }
The mapx and mapy fields determine this sector's location on the map screen. The title
You'll notice this particular idiom a great deal - the field "title" will be the name of the object so far as the player is concerned. But why bother using a 'title' field when TM could just use the name of the object? The main purpose of such a title field is to allow characters that aren't allowed in the .TMD language. For example, we wanted to call the Sol system "The Solar System". If TM used only object names instead of the title to display this to the player, it wouldn't be possible to put spaces in it.is how it will be displayed to the player, and the description offers more information. Last on the list, the owners of the sector:
Organizations( reference) are what own things, simply put. You need a minimum of one (the one the player belongs to) though of course it wouldn't be very interesting if the player owned everything. So while it's technically not the bare minimum, we're going to have two organizations:
Humans <organization> { field title = "Humanity"; list attitudes = { 50.0, 100.0 }; } Player <organization> { field title = "You"; list attitudes = { 0.0, 0.0 }; }
If we were going to do something with Humans that required the least bit of interaction, we'd have to flesh out their description quite a bit. But as it is, the only things required is their title and their feelings toward all the other organizations (only you, at this point. An organization's feelings toward itself don't count). The player organization, on the other hand, never needs more than outlined here.
This is an easy one; it describes the default state of all the player's displays:
hud <hudstates> { field console = 1.0; field navbox = 1.0; field radar = 1.0; field status = 1.0; field target = 1.0; }
This isn't a terribly powerful option, since there's really no reason not to have all the displays up. Still, it's available.
Finally, we need to create the player's ship itself. First we're going to create the basic kind of ship that this is (I.e. is it a Cargo ship? A Frigate?), then we'll make the actual instance.
Generic ship types are known as Ship ( reference) objects, and they look like this:
Shuttlecraft <ship> { field acceleration = 50.0; field angular = 190.0; field maxspeed = 175; field shields = 110; field title = "Shuttlecraft"; child image.add(ShuttlecraftImage <image> { field filename = "shuttleGreen.bmp"; }); }
There's some things we've left out (Jump times, for instance) but since there's only one sector that's hardly necessary.
A Shuttlecraft can be for more than just one person, though; we need a way to discern between the type of a ship and a specific instance of a ship. The author did too, so he invented the FrozenShip ( reference):
PlayerShip <frozenship> { child ship.add Shuttlecraft; }
There are other options here if you'd like the ship to start out somewhere other than in the center of the sector, or moving in a certain direction, but for the player's initial ship, all you need is the 'ship' child to say what kind of ship this specific ship is.
That's it! If you've typed in all the sections, feel free to save it in your .tradermissions directory and give it a whirl! It's nothing special; just you wandering around in an empty sector, but it's a working scenario! If you'd rather type it in all at once, here's a full listing:
# The Tutorial Tutorial # # If you're new to TraderMissions, here's the scenario you'll want to try # first. To do so, select it from the listbox, hit 'add' and then press # "Launch Game" gamestate <gamestate> { child map.add OurMap; child curSector.add Sol; child activeship.add PlayerShip; child hud.add hud; child orgs.add Player; child orgs.add Humans; child playerOrg.add Player; field money = 1000; field day = 1; } OurMap <map> { child sectors.add Sol; } Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; } Humans <organization> { field title = "Humanity"; list attitudes = { 50.0, 100.0 }; } Player <organization> { field title = "You"; list attitudes = { 0.0, 0.0 }; } hud <hudstates> { field console = 1.0; field navbox = 1.0; field radar = 1.0; field status = 1.0; field target = 1.0; } Shuttlecraft <ship> { field acceleration = 50.0; field angular = 190.0; field maxspeed = 175; field shields = 110; field title = "Shuttlecraft"; child image.add(ShuttlecraftImage <image> { field filename = "shuttleGreen.bmp"; }); } PlayerShip <frozenship> { child ship.add Shuttlecraft; }
Of course, what you've just done is to create the least interesting TraderMissions scenario of all time. But that's the bare minimum, and it's a good framework to get started with.
Now we're going to make things more interesting by putting in another sector that the player can jump to:
Orion <sector> { field mapx = 100.0; field mapy = 50.0; field title = "Orion"; field description = "The Orion system was the first to be colonized "+ "by humankind. Though other far more habitable systems were later "+ "discovered, Orion remains one of the more poplulated colonies in " + "existance."; child owner.add Humans; child links.add Sol; }
The key to linking sectors together is, unsurprisingly, in the 'links' child, which details every sector to which this sector has links. Of course, in order for this to work at all you'll have to update the Sol sector as well:
Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity." field description = "It's a lonely, lonely place."; child owner.add Humans; child links.add Orion; # NEW! }
And don't forget: the map object has to have a reference to every sector:
OurMap <map> { child sectors.add Sol; child sectors.add Orion; # NEW! }
The above is enough to put the other sector on the map, but you still can't jump to it - your ship has no fuel! That, and a number of other fields, are a requirement if you're planning on going anywhere. It's a fairly easy task, however:
Shuttlecraft <ship> { field acceleration = 50.0; field angular = 190.0; field maxspeed = 175; field shields = 110; field maxfuel = 300.0; # NEW field traveltime = 2.0; # NEW field jumpdist = 200.0; # NEW field jumptime = 3.0; #NEW field title = "Shuttlecraft"; child image.add(ShuttlecraftImage <image> { field filename = "shuttleGreen.bmp"; }); }
Now your ship is at least capable of holding fuel, though you're likely to notice if you try to jump anywhere that you still don't have any. This is one of the crucial distinctions between ships and frozenships: while ships describe an abstract ship type with certain properties (The most fuel a shuttlecraft can hold, for instance, is 300), frozenships describe a specific ship (below, for example, we say that a specific ship has 300 units of fuel - we're filling it up for the lucky player):
PlayerShip <frozenship> { child ship.add Shuttlecraft; field fuel = 300.0; # NEW }
Now, finally, you can jump between sectors. As you can see, there's a great deal of variety between them :)
There are two problems with the scenario as it currently stands. First, it's still pretty boring. And second, if you jump around long enough, you're going to run out of fuel. These are problems we can fix by introducing one of the staples of a TM scenario: Planets ( reference).
Earth <planet> { field x = 0.0; field y = 0.0; field landdist = 200; field title = "Earth"; field describe = "Earth. Humanity began its existance here, "+ "and even though it has since spread across the galaxy, Earth remains " + "the most influential planet in human space. Even among the colonies, " + "Earth is regarded with the utmost respect. Hundreds of thousands of "+ "people make the pilgrimage to 'Old Earth', and some even stay."; child image.add(EarthImage <image> { field filename = "planetoid.png"; }); }
This is the bare minimum example for a planet. Every planet allows for refueling, and the option will appear if the player needs fuel and can pay for it. Of course, in order to be able to see a planet (let alone land on it), you have to put it in the sector:
Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; child links.add Orion; child planets.add Earth; # NEW }
By putting in a planet which can refuel the player, we've postponed the inevitable out-of-fuel situation. Of course, the player will eventually run out of money to buy the fuel, and jumping between one inhabited sector and another uninhabited sector isn't a lot more fun than being stranded anyway. So we're going to set up an honest-to-goodness trading route, where the player can bring food to Hunter in the Orion sector, and can then bring back products to Earth. The first step in doing so, of course, is to create the products:
Food <cargo> { field title = "Food Supplies"; field baseprice = 100; } Alloy <cargo> { field title = "Ndterium Alloy"; field baseprice = 300; } SpareParts <cargo> { field title = "Spare Parts"; field baseprice = 150; }
Of course, the cargo ( reference) doesn't do us a lot of good unless we can put it on a planet. And it wouldn't be very interesting if every cargo was the same price. To allow variability, a 'frozencargo' ( reference) object was created. Similarly to the relationship between ships and frozenShips, a frozenCargo is a specific instance of a kind of Cargo. For example, let's put each sort of cargo on Earth:
Earth <planet> { field x = 0.0; field y = 0.0; field landdist = 200; field title = "Earth"; field describe = "Earth. Humanity began its existance here, "+ "and even though it has since spread across the galaxy, Earth remains " + "the most influential planet in human space. Even among the colonies, " + "Earth is regarded with the utmost respect. Hundreds of thousands of "+ "people make the pilgrimage to 'Old Earth', and some even stay."; child image.add(EarthImage <image> { field filename = "planetoid.png"; }); # NEW ITEMS START HERE child port.add(CheapFood <frozencargo> { child parent.add Food; field price = -50; }); child port.add(ExpensiveAlloy <frozencargo> { child parent.add Alloy; field price = 125; }); child port.add(OrdinarySpareParts <frozencargo> { child parent.add SpareParts; field price = 0; }); }
If you start up TraderMissions with this scenario, you'll see a 'Trade Goods' button and the three types of cargo we've created. Their prices vary according to the 'price' field of the frozencargo. So whereas the cargo determines the base price of a kind of cargo, the frozencargo sets the actual price.
We're going to take a break here from putting cargo on planets, because you've no doubt noticed something when you try to buy some of the items. Though you have the money, you don't have any cargo space! It's time we changed that:
Shuttlecraft <ship> { field acceleration = 50.0; field angular = 190.0; field maxspeed = 175; field shields = 110; field maxfuel = 300.0; field traveltime = 2.0; field jumpdist = 200.0; field jumptime = 3.0; field cargospace = 50.0; # NEW field title = "Shuttlecraft"; child image.add(ShuttlecraftImage <image> { field filename = "shuttleGreen.bmp"; }); }
Now you should be able to load up on as many items as you can afford or carry.
As the smart-ass heading reminds us, we've still got to have somewhere to drop the food supplies off and pickup alloy or spare parts. This, of course, will require another planet:
Hunter <planet> { field x = 250.0; field y = 100.0; field landdist = 150; field title = "Hunter"; field describe = "The vast majority of Hunter's population lives in "+ "one central dome on the planet. With no atmosphere and harsh "+ "daily radiation-storms, it is nearly impossible to grow food here, "+ "and so the colony relies on imported food to support its population "+ "and mining industries. The mining industry is the high point of "+ "Hunter's economy, however, as it is the most advanced site in human "+ "space."; child image.add(RockImage <image> { field filename = "moon.png"; }); child port.add(ExpensiveFood <frozencargo> { child parent.add Food; field price = 100; }); child port.add(CheapAlloy <frozencargo> { child parent.add Alloy; field price = -125; }); child port.add(CheapSpareParts <frozencargo> { child parent.add SpareParts; field price = -50; }); }
And, of course, we have to place the planet somewhere in the universe:
Orion <sector> { field mapx = 100.0; field mapy = 50.0; field title = "Orion"; field description = "The Orion system was the first to be colonized "+ "by humankind. Though other far more habitable systems were later "+ "discovered, Orion remains one of the more poplulated colonies in " + "existance."; child owner.add Humans; child links.add Sol; child planets.add Hunter; # NEW }
Of course, all that money is no good if you don't have anything to spend it on! Fortunately, there's more to planets than just refueling and trading items; you can also buy addons to make your current ship more powerful.
Addons ( reference) come in two flavors; addons that change the stats of your ship, and addons that are actually weapons. We'll cover both, starting with the stats-adding sort:
FuelTank <addon> { field title = "Additional Fuel Tank"; field describe = "Getting stranded in the middle of nowhere is never "+ "fun. Make sure it doesn't happen to you! This fuel tank will give "+ "you enough fuel for one additional jump - a jump that could mean "+ "the difference between completing your mission or begging a ride "+ "back home!"; field cost = 2500; field type = 0; field mass = 25; child item.add(MoreFuel <ship> { field maxFuel = 100; }); }
All addons, regardless of whether they're weapons or stats-based addons, look like the above. The crucial differences between the two is the 'type' field, which will be 0 for a stats-based addon and 1 for a weapon, and the 'item' child, which in the case of a weapon will be, well, a weapon. And in the case of a stats addon, it'll be a ship.
Yes, a ship. Bear with me; as odd as it sounds, it makes sense. What happens behind the scenes is this: when you buy a stats-based addon, TraderMissions goes through and adds every stat of the ship you specify to the stats of the player's ship. By default, ship stats are all zero, so you only need to specify those stats you want to change. In this case, the maxFuel of the ship will be increased by 100. Some stats are better if decreased, so negative values are fine. You can even have addons with tradeoffs (for example, an addon that gives more fuel at the expense of cargo space) Just be careful that the stats of the player's ship never drop below zero - there aren't safeguards for this, and strange things will happen as a result.
Essentially, addons allow you to change any stat of a ship, and so allow you to present the player with quite a few customization options.
Before we put this addon and the one we're about to make on a planet, we're going to make it so you can actually buy and use them. Every addon can require two things: Mass and hardpoints. Mass is specially-reserved areas of ships for addons. Hardpoints are external mount points for you to put things like lasers on. Of course, since we never said the Shuttlecraft that the player's flying around had either of these, the player wouldn't be able to buy any addon requiring them. So here's the necessary changes:
Shuttlecraft <ship> { field acceleration = 50.0; field angular = 190.0; field maxspeed = 175; field shields = 110; field maxfuel = 300.0; field traveltime = 2.0; field jumpdist = 200.0; field jumptime = 3.0; field cargospace = 50.0; field hardpoints = 2; # NEW field mass = 40; # NEW field maxrof = 0.15; # NEW field title = "Shuttlecraft"; child image.add(ShuttlecraftImage <image> { field filename = "shuttle.bmp"; }); }
The maxrof field is for this ship's weapon systems; it's just a bit of padding between the shots, otherwise if you had 4 lasers and shot them off, they'd all be spawned at the exact same time in the exact same place with the exact same momentum - they'd overlap completely on the screen!
Of course, there's no challenge in flying over to another sector and then going back. The real thrill of trading through space comes with the ever-present fear of pirate attacks. How are you going to defend yourself? With one of these!
Laser <weapon> { field title = "Basic Laser"; child image.add(BlueLaserImg <image> { field filename="laser2.png"; }); field ttl = 2; field topspeed = 250; field damage = 10; field rof = 0.5; field primary = 1; }
Of course, I said earlier that weapons( reference) were a kind of addon; and they are. You create a separate addon object that then points to this as its 'item'. The main reason for this is so that weapons' price can vary from place to place:
LaserAddon <addon> { field title = "Basic Laser"; field describe = "The Laser is a technology so old that it pre-dates "+ "the Colonization. It has been significantly revised and upgraded, "+ "of course, but the underlying technology remains unchanged."; field cost = 1200; field type = 1; field hardpoints = 1; field mass = 10; child item.add Laser; }
All that remains, of course, is to put them on a planet. Since Hunter is a pretty high-industry place, let's put both the addons there:
Hunter <planet> { field x = 250.0; field y = 100.0; field landdist = 150; field title = "Hunter"; field describe = "The vast majority of Hunter's population lives in "+ "one central dome on the planet. With no atmosphere and harsh "+ "daily radiation-storms, it is nearly impossible to grow food here, "+ "and so the colony relies on imported food to support its population "+ "and mining industries. The mining industry is the high point of "+ "Hunter's economy, however, as it is the most advanced site in human "+ "space."; child image.add(RockImage <image> { field filename = "moon.png"; }); child port.add(ExpensiveFood <frozencargo> { child parent.add Food; field price = 100; }); child port.add(CheapAlloy <frozencargo> { child parent.add Alloy; field price = -125; }); child port.add(CheapSpareParts <frozencargo> { child parent.add SpareParts; field price = -50; }); child upgrades.add FuelTank; # NEW child upgrades.add LaserAddon; # NEW }
Of course, what we created there was an example of a primary weapon. While firing lasers all day is a nice leisurely activity, we need a little more deadliness. In fact, what we need is... a homing missile!
Missile <weapon> { child image.add(missileImg <image> { field filename = "missile.png"; }); field ttl = 5; field topspeed = 300; field damage = 30; field primary = 0; field isAmmo = 1; field acceleration = 600; field angular = 360; field seeking = 1; } MissileAddon <addon> { field title = "Seeking Missile"; field describe = "This missile isn't the fastest or most " + "punch-packing in existance, but it'll get the job done without "+ "you having to aim."; field cost = 50; field type = 1; field hardpoints = 0; field mass = 1; child item.add Missile; }
There you go. Things to notice include:
So, essentially, we've created something useless because we can't use it directly. Then again, one of the benefits of being the author is that we can add in anything we want:
MissileLauncher <weapon> { field title = "Seeking Missile"; field rof = 3.0; field primary = 0; field isAmmo = 0; child ammo.add Missile; } MissileLauncherAddon <addon> { field title = "Seeking Missile Launcher"; field describe = "This is the slowest launcher for Seeking "+ "Missiles on the market. It's also the cheapest."; field cost = 350; field type = 1; field hardpoints = 1; field mass = 15; child item MissileLauncher; }
You'll see that the "title" field is the same for both the Missile and its launcher. When you go to buy the weapon, the title and description of the addon is what's displayed, not that of the weapon itself. But when you select a secondary weapon, its title (or the title of its launcher, if appropriate) is displayed. So you want the titles in-store to indicate that it's a launcher, but when you're out cruising around you probably don't care and can omit it.
Now for the "Visible result" stage, wherein we put these on Hunter:
Hunter <planet> { field x = 250.0; field y = 100.0; field landdist = 150; field title = "Hunter"; field describe = "The vast majority of Hunter's population lives in "+ "one central dome on the planet. With no atmosphere and harsh "+ "daily radiation-storms, it is nearly impossible to grow food here, "+ "and so the colony relies on imported food to support its population "+ "and mining industries. The mining industry is the high point of "+ "Hunter's economy, however, as it is the most advanced site in human "+ "space."; child image.add(RockImage <image> { field filename = "moon.png"; }); child port.add(ExpensiveFood <frozencargo> { child parent.add Food; field price = 100; }); child port.add(CheapAlloy <frozencargo> { child parent.add Alloy; field price = -125; }); child port.add(CheapSpareParts <frozencargo> { child parent.add SpareParts; field price = -50; }); child upgrades.add FuelTank; child upgrades.add LaserAddon; child upgrades.add MissileLauncherAddon; # NEW child upgrades.add MissileAddon; # NEW }
Not all secondary weapons need to have ammo, of course. It's purely an option for the scenario designer; if you want infinite ammo, just make weapons that don't require any :)
While you can save up money to buy lasers with which to wreak vengeance upon your enemies, there's not a whole lot about the fact that you only have a certain number of hardpoints. And while you could write an addon to give the player more hardpoints, this still doesn't change the fact that the ship still looks the same. So let's let the player do a little upgrading:
BigCargoHauler <ship> { field maxrof = 0.15; field acceleration = 30.0; field angular = 100.0; field jumptime = 4.0; field jumpdist = 300.0; field traveltime = 3.0; field maxfuel = 600.0; field maxspeed = 100.0; field cargospace = 150.0; field hardpoints = 5; field mass = 100; field shields = 300; field title = "Big Cargo Hauler"; child image.add ( cargoImg <image> { field filename="cargo.bmp"; } ); }
Ahhh - this ship may not be fast, but it's got a lot more defense, far more room for expansion, and can haul cargo like nobody's business. It will add some much-needed variety. Of course, since we'll be selling a specific ship here, we have to go into further detail by using a frozenship:
BigCargoHaulerForSale <frozenship> { child ship.add BigCargoHauler; field fuel = 600.0; field cost = 5000; }
Using a frozenship allows you to sell stock BigCargoHaulers in one place, and then sell BigCargoHaulers somewhere else with, say, lasers already built in. You can also vary the price from region to region.
And finally, as usual, we have to put them on a planet so people can buy them. Since Hunter got the addons, let's put the shipyards on Earth:
Earth <planet> { field x = 0.0; field y = 0.0; field landdist = 200; field title = "Earth"; field describe = "Earth. Humanity began its existance here, "+ "and even though it has since spread across the galaxy, Earth remains " + "the most influential planet in human space. Even among the colonies, " + "Earth is regarded with the utmost respect. Hundreds of thousands of "+ "people make the pilgrimage to 'Old Earth', and some even stay."; child image.add(EarthImage <image> { field filename = "planetoid.png"; }); child port.add(CheapFood <frozencargo> { child parent.add Food; field price = -50; }); child port.add(ExpensiveAlloy <frozencargo> { child parent.add Alloy; field price = 125; }); child port.add(OrdinarySpareParts <frozencargo> { child parent.add SpareParts; field price = 0; }); child shipyard.add BigCargoHaulerForSale; # NEW }
It's as easy as that - the player can buy a brand new Big Cargo Hauler and haul stuff around.
So if you've been flying around, you've probably been wondering where everyone else is. I mean, a civilization capable of spaceflight isn't going to just send one guy out. So let's add some scenery to the universe.
Both Earth and Hunter have quite a nice trade route going there; if there's going to be other people, let's put them on the trade route as well. We start by defining the ships themselves. They are, unsurprisingly, frozenShips:
PeacefulTrader <frozenship> { child ship.add BigCargoHauler; field title = "Trading Fleet"; }
Those are the only required fields of a ship you're putting into a fleet. Of course, if you want to put lasers on them, you can do that too. For now, though, we'll stick with just a basic fleet ( reference), out to make a buck:
PeacefulTraders <fleet> { child ships.add PeacefulTrader; child ships.add PeacefulTrader; child ships.add PeacefulTrader; child owner.add Humans; field type = 0.0; field reaction = 1.0; field greeting = "Hello!"; }
Note that we add three of the same frozenships to the fleet; that's fine, the fleet will just have three ships that are exactly the same.
Finally, we have to stick the ships somewhere. This listing will be a bit long, since we're putting them in the Orion sector and in the solar system:
Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; child links.add Orion; child planets.add Earth; child fleets.add PeacefulTraders; # NEW list fleetChance = { 0.7 }; # NEW field busyness = 0.1; # NEW } Orion <sector> { field mapx = 100.0; field mapy = 50.0; field title = "Orion"; field description = "The Orion system was the first to be colonized "+ "by humankind. Though other far more habitable systems were later "+ "discovered, Orion remains one of the more poplulated colonies in " + "existance."; child owner.add Humans; child links.add Sol; child planets.add Hunter; child fleets.add PeacefulTraders; # NEW list fleetChance = { 0.6 }; # NEW field busyness = 0.09; # NEW }
We're not quite done just yet. If you fire your scenario up, you'll see the ships all right, but what if you hail them? Okay, you'll get our canned greeting. Now try begging for fuel. You don't need fuel, you say? Run around a bit until you do. If you hail then and beg for fuel, you'll get it, but you won't get a message! That's because we never set one up for humanity:
Humans <organization> { field title = "Humanity"; list attitudes = { 50.0, 100.0 }; # NEW ITEMS START HERE field hitPenalty = 10; field friendlyFireMult = 0.5; field hostile = -50.0; field fuelCutoff = 0; field helpCutoff = 50; field heroCutoff = 150; field villainCutoff = -500; field planetCutoff = -300; field okFuelMsg = "We shall refuel you immediately"; field okHelpMsg = "We're on our way!"; field rejectMsg = "No, I don't think so."; }
As you can tell, we did a bit more than just add a few messages in. We've also added the cutoffs for how the humans will treat you based on how much you've ticked them off. This is necessary now that you can buy the means to do so right on Hunter.
Of course, you've no doubt been doing some target practice, just to make sure that your weapons systems and your cutoffs are working. But those traders just pick up and get the hell out of dodge if you so much as look at them funny, so they're no challenge. Let's add a bit of law and order, shall we?
MilitiaShuttle <frozenship> { child ship.add Shuttlecraft; child addons.add LaserAddon; field title = "Militia Shuttlecraft"; } Militia <fleet> { child ships.add MilitiaShuttle; child ships.add MilitiaShuttle; child ships.add MilitiaShuttle; child ships.add MilitiaShuttle; child ships.add MilitiaShuttle; child owner.add Humans; field type = 1.0; field reaction = 2.0; field greeting = "Yes, citizen?"; }
And let's just put them around Earth. Hunter's too far out of the way for a police presense:
Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; child links.add Orion; child planets.add Earth; child fleets.add PeacefulTraders; child fleets.add Militia; # NEW list fleetChance = { 0.7, 1.0 }; field busyness = 0.1; }
Of course, it's not exactly a very nice thing to do, running around and beating up on Earth's defenders. What we need is a legitamate enemy!
Aliens <organization> { field title = "The Aliens"; list attitudes = { -200, -200, 100 }; field hitPenalty = 25; field friendlyFireMult = 0.3; field hostile = -50.0; field fuelCutoff = 50; field helpCutoff = 150; field heroCutoff = 500; field villainCutoff = -250; field planetCutoff = -300; field okFuelMsg = "You will be assisted... for now."; field okHelpMsg = "Very well."; field rejectMsg = "We enjoy your suffering, human!"; }
Now that we have our menacing alien presense, we need to add it to the gamestate:
gamestate <gamestate> { child map.add OurMap; child curSector.add Sol; child activeship.add PlayerShip; child hud.add hud; child orgs.add Player; child orgs.add Humans; child orgs.add Aliens; # NEW child playerOrg.add Player; field money = 1000; field day = 1; }
But wait! You'll notice that the Aliens' attitudes list has three entries in it; one for you, one for the humans, and one for themselves. But all the other organizations don't have a default feeling for the aliens. Bad Things will happen if we don't fix that:
Humans <organization> { field title = "Humanity"; list attitudes = { 50.0, 100.0, -200 }; # NEW field hitPenalty = 10; field friendlyFireMult = 0.5; field hostile = -50.0; field fuelCutoff = 0; field helpCutoff = 50; field heroCutoff = 150; field villainCutoff = -500; field planetCutoff = -300; field okFuelMsg = "We shall refuel you immediately"; field okHelpMsg = "We're on our way!"; field rejectMsg = "No, I don't think so."; } Player <organization> { field title = "You"; list attitudes = { 0.0, 0.0, 0.0 }; # NEW }
Okay, we have a working evil organization, but no actual fighting going on. That's easy enough to fix. First off, let's make some ships for them and a fleet for the ships:
AlienShip <ship> { field maxrof = 0.15; field acceleration = 25.0; field angular = 120.0; field jumptime = 5.0; field jumpdist = 250.0; field traveltime = 3.0; field maxfuel = 400.0; field maxspeed = 200.0; field cargospace = 75.0; field hardpoints = 3; field mass = 50; field shields = 120; field title = "Alien Cruiser"; child image.add(alienCruiserImg <image> { field filename = "alien.png"; }); } AlienOffensive <frozenship> { child ship.add AlienShip; child addons.add LaserAddon; child addons.add LaserAddon; field title = "Alien Cruiser"; } AlienFleet <fleet> { child ships.add AlienOffensive; child ships.add AlienOffensive; child owner.add Aliens; field type = 1.0; field reaction = 2.0; field greeting = "Speak!"; }
Now all we're missing is a sector to put them in!
Vollox <sector> { field mapx = 0.0; field mapy = 150.0; field title = "Vollox"; field description = "The Great Age of Colonization came to a terrible "+ "end when scout ships entered the Vollox sector. Only one of a dozen "+ "returned from there, and tensions between humankind and the aliens "+ "have been breaking ever since."; child owner.add Aliens; child links.add Sol; child fleets.add Militia; child fleets.add AlienFleet; list fleetChance = { 0.3, 0.6 }; field busyness = 0.1; }
And the obligatory adding the sector to the map and linking it to another sector:
OurMap <map> { child sectors.add Sol; child sectors.add Orion; child sectors.add Vollox; # NEW } Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; child links.add Orion; child links.add Vollox; # NEW child planets.add Earth; child fleets.add PeacefulTraders; child fleets.add Militia; list fleetChance = { 0.7, 1.0 }; field busyness = 0.1; }
There you go - all set. You can fly off to the north and watch the fireworks; just be careful not to get caught in the middle of the crossfire!
So now that you've got someone else to pick on, you might want to enable the player to redeem reputation lost due to accidentally hijacking a cargo ship every now and then. But you'll notice that no matter how much you beat up on the aliens, the humans don't like you more (and vice versa). Why is this? The each organization has two children set up for this, an 'allies' child and an 'enemies' child. Allies can't damage each other's reputation (if you made the player and the humans allies, the player could blow up humans all day and suffer no reputation damage at all) and share reputation damages; if you hit a ship, its owner will be angry at you and so will the owner's allies! Enemies, on the other hand, have the opposite reputation effect happen to them. So if you hit a human ship, your reputation with them would go down but it would go up with the aliens (Subject, of course, to the gullability field). Here's how you set up an enemies list:
Aliens <organization> { field title = "The Aliens"; list attitudes = { -500, -500, 100 }; field hitPenalty = 25; field friendlyFireMult = 0.3; field hostile = -50.0; field gullability = 0.5; # NEW field fuelCutoff = 50; field helpCutoff = 150; field heroCutoff = 500; field villainCutoff = -250; field planetCutoff = -300; field okFuelMsg = "You will be assisted... for now."; field okHelpMsg = "Very well."; field rejectMsg = "We enjoy your suffering, human!"; child enemies.add Humans; # NEW } Humans <organization> { field title = "Humanity"; list attitudes = { 50.0, 100.0, -200 }; field hitPenalty = 10; field friendlyFireMult = 0.5; field hostile = -50.0; field gullability = 0.6; # NEW field fuelCutoff = 0; field helpCutoff = 50; field heroCutoff = 150; field villainCutoff = -500; field planetCutoff = -300; field okFuelMsg = "We shall refuel you immediately"; field okHelpMsg = "We're on our way!"; field rejectMsg = "No, I don't think so."; child enemies.add Aliens; # NEW }
Now you've got a proper hate-hate relationship going!
There are more possible types of missions than this tutorial can hold - this is because it's easy to string together a mission based on any number of things; there's enormous flexibility. Each Mission is comprised of a collection of two things: Conditions and Actions. Actions( reference) are things that happen; a message popping up, you getting money for completing a mission, dozens of enemy ships being spawned at your immediate location -- these are all things Actions can cause. Conditions( reference) are also exactly what they sound like - they determine things, whether or not it's getting the mission in the first place, or when that mission will end.
We're going to start out this example with your basic Fedex quest - bring some cargo from point A to point B. While it would seem natural to use the inline-style declarations for missions, there are many conditions and actions you can reuse. We'll start with a condition:
FreeCargo10Prereq <condition> { field freeCargo = 10; }
This ensures we have enough cargo for the mission. Since there are a great number of missions that could use 10 units of cargo, this is a condition we'd like to refer to often. As pointed out before, even if it was declared as part of an individual mission (as UniqueFedex will be later) other missions could refer to it; leaving it out by itself in this case just makes it more obvious when we're scanning our scenario for conditions we use often.
Secondly, let's make some actions:
Give10Food <action> { child giveCargo.add(MissionFood <frozencargo> { child parent.add Food; field amount = 10; field missionRelated = 1; }); } Take10Food <action> { child takeCargo.add MissionFood; field message = "The 10 units of food are unloaded from your ship"; } Reward1500 <action> { field money = 1500; field endMission = 1; field message = "You are rewarded 1500 credits for your trouble"; }
Things of note here include the "missionRelated" flag for the MissionFood object - any cargo you give your player (except the kind you're giving as a reward) should be missionRelated; this will ensure that the player can't get rid of it.
All of these are fairly generic conditions and actions, which we can put together to make our mission, but they don't have to be. We could have combined the Take10Food action and Reward1500 action and given a more specific message ("You can hardly hear the dockmaster talking to you over the noise and hubbub of the food supplies being unloaded. You finally understand his intent when he presses 1500 credits into your hand. Rewarded at last!") and indeed, for missions as specific as the one we're creating here, that's exactly what you'll want to do; generally, each mission only gets one message upon being completed so the user might be confused into thinking that somehow two missions took place. However, for our current simple purposes this will do.
The place for the first action is pretty obvious; it's an action that will be taken when the mission is first accepted. But the other two don't come about until the end of the mission. How, then, to ensure that those happen? Well, conditions have a child called "action" which, as you might imagine, occurs if the condition is true. Now, it doesn't always happen; in prerequisites, the action child is ignored. But everywhere else it's evaluated and executed. Since this is also fairly unique to the mission, we're going to be rolling it in with the ordinary mission( reference) declaration:
FedexToHunter <mission> { field title = "Deliver 10 units of Food Supplies to Hunter in the Orion system"; field longdesc = "Hunter is always in need of extra food, and though "+ "pliots usually make money taking it there on their own, there are "+ "people on Hunter willing to pay to ensure that they get fed "+ "first."; child prereqs.add(UniqueFedexToHunter <condition> { child neverHadTheseMissions.add FedexToHunter; }); child prereqs.add FreeCargo10Prereq; child preparation.add Give10Food; child conditions.add(EndingFedexToHunter <condition> { child playerAt.add Hunter; child action.add Take10Food; child action.add Reward1500; }); }
As you can see, the code ties all the conditions together. Using generic conditions and actions allows us to easily make another mission where you need to take 10 food supplies to another planet, but that flexibility costs us better descriptions. Usually you'll use generic components for generic missions, and specific components for the missions the player is more likely to be interested in (i.e. those contributing to the plot).
The only other mission in the tutorial is the one where you scout the Vollox system. This mission is even easier:
ScoutVollox <mission> { field title = "Scout the Vollox System"; field longdesc = "Automated probes can only tell us so much; we need "+ "a fully equipped ship to go out there and find out the latest "+ "news."; child prereqs.add(UniqueScoutVollox <condition> { child neverHadTheseMissions.add ScoutVollox; }); child conditions.add(EndingScoutVollox <condition> { child playerAt.add Vollox; child action.add Reward1500; }); }
All we do is make sure the player hasn't taken this mission before, and once they jump into the Vollox system, they've completed the mission.
The final thing to do is to add the missions to the planets where they belong:
Earth <planet> { field x = 0.0; field y = 0.0; field landdist = 200; field title = "Earth"; field describe = "Earth. Humanity began its existance here, "+ "and even though it has since spread across the galaxy, Earth remains " + "the most influential planet in human space. Even among the colonies, " + "Earth is regarded with the utmost respect. Hundreds of thousands of "+ "people make the pilgrimage to 'Old Earth', and some even stay."; child image.add(EarthImage <image> { field filename = "planetoid.png"; }); child port.add(CheapFood <frozencargo> { child parent.add Food; field price = -50; }); child port.add(ExpensiveAlloy <frozencargo> { child parent.add Alloy; field price = 125; }); child port.add(OrdinarySpareParts <frozencargo> { child parent.add SpareParts; field price = 0; }); child shipyard.add BigCargoHaulerForSale; child missions.add FedexToHunter; # NEW } Hunter <planet> { field x = 250.0; field y = 100.0; field landdist = 150; field title = "Hunter"; field describe = "The vast majority of Hunter's population lives in "+ "one central dome on the planet. With no atmosphere and harsh "+ "daily radiation-storms, it is nearly impossible to grow food here, "+ "and so the colony relies on imported food to support its population "+ "and mining industries. The mining industry is the high point of "+ "Hunter's economy, however, as it is the most advanced site in human "+ "space."; child image.add(RockImage <image> { field filename = "moon.png"; }); child port.add(ExpensiveFood <frozencargo> { child parent.add Food; field price = 100; }); child port.add(CheapAlloy <frozencargo> { child parent.add Alloy; field price = -125; }); child port.add(CheapSpareParts <frozencargo> { child parent.add SpareParts; field price = -50; }); child upgrades.add FuelTank; child upgrades.add LaserAddon; child upgrades.add MissileLauncherAddon; child upgrades.add MissileAddon; child missions.add ScoutVollox; # NEW }
If you played the TraderMissions tutorial, you'll no doubt note that there's a bit of difference between it and what you've created so far - namely, there's no tutorial! That's what this section will rectify -- the tutorials are essentially really big missions, so we just need to add a few, and we're done! The missions themselves are actually quite simple, but very wordy so this section looks more complicated than it actually is.
The tutorial starts off with some informative text for the player, and ends when the player lands on Earth. We accomplish this by giving the mission the instant the player enters the Sol sector, which will be when the game starts:
Tutorial <mission> { field title = "TraderMissions Tutorial"; field longdesc = "Fly your ship around to get the hang of spaceflight.\n"+ "Left/Right arrows: Rotate counterclockwise/clockwise\n"+ "Up/Down arrows: Accelerate forwards/backwards\n"+ "-/+ (keypad): Zoom radar out/in\n"+ "'l': Land on planet.\n" + "escape: Return to main menu"; child prereqs.add(UniqueTutorial <condition> { child neverHadTheseMissions.add Tutorial; }); child preparation.add(TutorialIntro <action> { field message = " Welcome to TraderMissions! The first thing "+ "you'll need to learn is how to fly around. The left and right(*) "+ "arrow keys rotate your ship counterclockwise and clockwise, "+ "respectively. The up key will thrust in the direction "+ "that you're pointing. Note that you have inertia! Unless you "+ "move otherwise, you'll keep going when you thrust in a particular "+ "direction. Finally, the down key will thrust opposite to the "+ "direction that you're pointing.\n\n" + " If you get lost, you can use the keypad '+' and "+ "'-' keys to zoom in and out on the radar. The planet is the blue "+ "dot.\n\n" + " When you're ready to move on with the tutorial, fly to the "+ "planet and land on it using the 'l' key. You don't have to be "+ "right on top of it to land, but you do have to be reasonably "+ "close.\n\n" + " If at any point you want to return to the main menu, hit the "+ "escape key.\n\n" + " Happy flying!\n\n\n\n"+ "(*) This tutorial is assuming that you're using the default key "+ "setup. If you've changed it, you'll have to remember what key goes "+ "with what function."; }); child conditions.add(TutorialFinishTest <condition> { child playerAt.add Earth; child action.add(TutorialFinish <action> { field endMission = 1; field message = " Welcome to Earth! Now that you have the "+ "hang of space, we're going to introduce things you can do "+ "on planets.\n\n" + "REFUEL: It won't be displayed now, but if you have less "+ "than a full tank of fuel, there will be a 'refuel' button "+ "to allow you to fill up. If you don't have enough money "+ "to fill up, the button will instead fill up as much as "+ "you can afford.\n\n" + "TRADE GOODS: This is the area where you buy items. It's "+ "typically a good way to make money - the only thing you "+ "need to make sure of is that you know somewhere else where "+ "you can sell it for more than you bought it!\n\n" + "VISIT SHIPYARD: Once you've saved up enough money, you can "+ "buy a new ship. The shipyard tells you all the details about "+ "a ship, but be sure to shop around, since prices can vary!\n\n" + "MISSIONS: This is where a lot of the excitement takes place. "+ "You can get missions to do a number of things in ordinary "+ "scenarios - in this tutorial, there is only one available. "+ "To take a mission, simply click on the mission you want and "+ "then click the 'Take this mission' button.\n\n"+ " When you're done examining the other areas of the planet, "+ "go to the missions and accept the delivery mission to Hunter. "+ "If you're going to buy cargo as well to increase your profit "+ "margins (Hint: food!) be sure to leave 10 units free, otherwise "+ "you won't see the mission offered.\n\n" + "Once you've accepted the mission, click 'Leave Earth' to go "+ "back out into space."; }); }); }
As you can see, this is essentially just a wordier version of our earlier "Scout Vollox" mission, only with a planet as the destination rather than a sector.
Up until this point, however, the only place the player could get missions has been going up to the planet and asking for them. How do you simply assign a mission to a player? It's simple: Both planets and sectors have children which, if you put a mission in them, will simply give them to the player (so long as they qualify). Here's how the one for the sector works:
Sol <sector> { field mapx = 0.0; field mapy = 0.0; field title = "The Solar System"; field description = "The home sector of humanity."; child owner.add Humans; child links.add Orion; child links.add Vollox; child planets.add Earth; child fleets.add PeacefulTraders; child fleets.add Militia; list fleetChance = { 0.7, 1.0 }; field busyness = 0.1; child special.add Tutorial; # NEW }
Playing the tutorial, you've probably noticed that it used the two missions that we created earlier, only with a lot more wording. There's additional conditions to deal with it all, so the missions have been re-worked. The delivery mission to hunter now includes a message when you take off as well as when you land:
FedexToHunter <mission> { field title = "Deliver 10 units of Food Supplies to Hunter in the Orion" + " system."; field longdesc = "Hunter is always in need of extra food, and though "+ "pliots usually make money taking it there on their own, there are "+ "people on Hunter willing to pay to ensure that they get fed "+ "first."; child prereqs.add(UniqueFedexToHunter <condition> { child neverHadTheseMissions.add FedexToHunter; }); child prereqs.add FreeCargo10Prereq; child preparation.add Give10Food; ## child conditions.add(EndingFedexToHunter <condition> { ## child playerAt.add Hunter; ## child action.add Take10Food; ## child action.add Reward1500; ## }); # NEW ITEMS START HERE child conditions.add(FedexToHunterMessageTest <condition> { child playerAt.add Sol; child action.add(FedexToHunterMessage <action> { field message = " Naturally, you may be asking yourself where "+ "the Orion system is, and how you're going to get there. "+ "You're currently in the Sol system, and no matter how long or "+ "far you fly your ship, you'll never actually reach any other "+ "system, due to the large distances involved. That's where "+ "your ship's ability to jump between sectors comes in handy.\n\n" + " The first step in jumping is to set a destination: Once you've "+ "read these instructions, pull up "+ "the map by pressing the 'm' key, and then select 'Orion'. "+ "You can double-click Orion or just hit the 'select' button "+ "to add it to your route. That done, press the 'done' button "+ "to return to space.\n\n"+ " Once you've chosen a destination, you just need to press and "+ "hold the 'j' button. A countdown will be displayed and your "+ "ship will gradually fade to white. When the countdown reaches "+ "zero, you'll be transported to your new sector."+ " When you've arrived in Orion, find Hunter (it should be to the "+ "upper right) and land on it to complete the mission."; }); }); child conditions.add(EndingFedexToHunter <condition> { child playerAt.add Hunter; child action.add(FedexToHunterEnd <action> { child takeCargo.add MissionFood; field money = 1500; field endMission = 1; field message = " Congratulations on finding Hunter! "+ " If you bought food on Earth, here is the place to "+ "sell it - it's worth quite a bit more here. In addition, there "+ "is a new option here: 'Buy Parts'. This allows you to buy "+ "addons or weapons for your ship. Spend your newfound money on "+ "weaponry, because you're going to need it soon. Once you've "+ "stocked up, go to the 'missions' screen and take the one "+ "offered there."; }); }); }
Since the ending conditions of the mission have been reworked so extensively, I've commented out the old ones and just made new ones entirely.
The next step of the tutorial takes place when the player takes the Vollox scouting mission. This is almost exactly the same as it was before, except with a whole lot more text. Like the fedex mission previously, the old conditions have been commented out:
ScoutVollox <mission> { field title = "Scout the Vollox System"; field longdesc = "Automated probes can only tell us so much; we need "+ "a fully equipped ship to go out there and find out the latest "+ "news."; child prereqs.add(UniqueScoutVollox <condition> { child neverHadTheseMissions.add ScoutVollox; }); ## child conditions.add(EndingScoutVollox <condition> { ## child playerAt.add Vollox; ## child action.add Reward1500; ## }); # NEW ITEMS START HERE child conditions.add(ScoutVolloxMessageTest <condition> { child playerAt.add Orion; child action.add(ScoutVolloxMessage <action> { field message = " Plotting a course to a sector that's not " + "immediately next to you is the same as going to any other "+ "sector. When you pull up the map this time, select the "+ "'unknown' sector: This is actually Vollox, and it's listed "+ "as unknown because you haven't been there yet. (Orion was "+ "marked as visited for the tutorial to make life easier; "+ "by default places you haven't visited will be 'unknown') "+ "When you double-click this sector or press the 'select' "+ "button, the computer will calculate a route for you. When "+ "you jump between sectors, the next item on your route will "+ "become your new destination. So you don't have to keep "+ "pulling up the map for every single jump -- you only need it "+ "when you're changing your destination."; }); }); child conditions.add(ScoutVolloxMessageTest2 <condition> { child playerAt.add Sol; child action.add(ScoutVolloxMesage2 <action> { field message = " Since you're about to jump into the thick of "+ "things, now might be a good time to save your game. You can "+ "save your game any time that you're not on a planet or looking "+ "at some message dialog like this one. To do you, you press "+ "escape to go to the main menu, then press the 'Save Game' "+ "button. That's all! To get back to your game in progress, you "+ "press the 'resume game' button. If you die or come back later, "+ "the 'Load Game' button is how you can load your game back up. "+ "It will be saved under the name you gave when you started."; }); }); child conditions.add(EndingScoutVollox <condition> { child playerAt.add Vollox; child action.add(ScoutVolloxEnd <action> { field endMission = 1; field money = 1500; field message = " Congratulations! You've reached the end "+ "of the tutorial. You've also completed the mission just by "+ "arriving in Vollox. Of course, once you close this dialog, "+ "you'll probably notice that there are people here who aren't "+ "thrilled by your presense. That's why the remainder of this "+ "tutorial is dedicated to:\n\n"+ "COMBAT:\n" + " Using your primary weapons is easy: Just point your ship "+ "and press the space bar to fire. So long as you're holding "+ "the space bar down, all your primary weapons will fire as "+ "rapidly as they can. Some will only fire in the direction "+ "you're pointed, others will fire in the direction of your "+ "target.\n\n"+ " Targeting is easy - to cycle between all ships in the sector "+ "for your target, press the 'tab' key. Your target's name and "+ "shields will be displayed on your HUD, and if the target is "+ "onscreen it will be outlined. It will also be a different "+ "color on your radar. If you're under attack, you can press "+ "'r' to target the nearest ship attacking you.\n\n" + " Secondary weapons are slightly more difficult to use. "+ "unlike primary weapons which can all fire at once, you must "+ "choose an individual secondary weapon to fire. To cycle "+ "between your weapons, you can press the 'w' key. Your currently "+ "chosen weapon is displayed in your HUD along with how much ammo "+ "you have for it if it's a weapon that requires ammunition. "+ "Some secondary weapons require a target and will not fire unless "+ "you have chosen one (homing weapons in particular do this). "+ "Once you are ready to fire your chosen weapon, you can press the "+ "left control key to do so.\n\n" + " That's the last of the tutorial. I hope you enjoy "+ "TraderMissions! For more information on how to play, consult the "+ "Tradermissions Fleet Manual. Until then, happy hunting!"; }); }); }
And that's it! You have build, from nothing at all, a complete tutorial. You now know how to do most everything that the TraderMissions engine is capable of, and could go out right now and create a fairly detailed scenario.
If, however, you want to know all the details, then read on: the next section will cover the features of the engine that weren't present in the tutorial: