TMD files are plain ASCII that is loaded into the TM engine when a new game is started or a save game restored. Plugins are simply loaded after the main TMD file. The parser for each is exactly the same; in fact, you could rename your saved game with a .TMD extention and load it up as though it were a scenario with no problems.
Before we begin, there is one optional bit of syntax; the name and description of the file. When a new game is begun, the player is given a choice of scenarios and plugins that were found in certain predefined directories (more on this in section The Search Path) to load. When the player selects one of these, a name and description is displayed at the bottom of the screen. These are determined by the first few lines of the .TMD file; if a file begins with a comment, the first line is interpreted as being the name of the file, and every subsequent commented line is the description. In the TMD language, comments begin with a # and continue to the end of the line. The description is ended with the first non-commented line found. For example:
# Test file for TraderMissions # Don't try to run this, as it's not intended to actually work. mainMap <map> { ...
will display "Test file for TraderMissions" as its title and the rest as the description. The 'mainMap' line that comes after is code. However, the following:
# Another test file # This is me testing the parser some more mainMap <map> { ...
will only display "Another test file" as the title, because the 'This is me testing' line comes after the first non-commented line.
If a file has no title or description line, its filename will be used instead.
Here's the file we're going to be discussing. Feel free to type it in if you'd like:
Adam <test> { field test = "Adam"; child dummychild1.add Beth; child dummychild1.add (Carl <test> { field test = "A \"second\" object"; list listtest = { 1, 2, 3, 4}; }); } Beth <test> { field test = "Yet another" + "Object"; child dummychild1.add Adam; }
This file contains three objects, with various fields and children. Obviously, though this looks structurally similar to an actual plugin or scenario file, it probably wouln't do much of anything. So how do you know if it's right?
TraderMissions ships with a program called tmdverify whose sole purpose is to verify that a TMD file is syntactically correct. It'll also do such niceties as making sure you haven't specified a child that doesn't exist. What it won't do for you is check TraderMissions-specific things. For instance, TM requires a gamestate ( reference) object named "gamestate" as well as a number of objects within that gamestate in order to start a universe correctly - tmdverify knows nothing of that.
So what does tmdverify's output look like? If the above was typed correctly, it'll come out something like this:
#Generated by tmdverify #Contents: # Tradermissions savefile #Object 0 Adam <test> { field test = "Adam"; list listtest = {}; child dummychild1.add Beth; child dummychild1.add Carl; } #Object 1 Beth <test> { field test = "Yet anotherObject"; list listtest = {}; child dummychild1.add Adam; } #Object 2 Carl <test> { field test = "A \"second\" object"; list listtest = {}; } #Checking symbols: #Verification completed.
The reason that looks a whole lot like the type of code you just entered is because it is; tmdverify loads what you've written into its repository, and then dumps it back out from its memory. It's as though the file were loaded and then immediately saved. If you see output like this, it means your file was parsed correctly. If not, the error message should tell you what actions to take next. Some of the more common messages are:
This will follow the TMD code and be placed right after the line that says "# Checking Symbols" so make sure to always look at that section. It will tell you what symbol is unresolved (i.e. you've referred to an object that doesn't exist) and what line you're referring to it on.
These are usually printed instead of the output above; they come when you've put something where the parser doesn't expect it. The line the error occurred on will be reported, as well as what the parser was expecting, and what it got instead. Keep in mind that the parser will only look for as long as the character it was expecting was, so the error message might be a little off. For example, if I put "Hello, world" where the parser was expecting a "<" symbol to be, I'd get an error that read "expected '<', got 'H'" because it only read the first character.
Objects are the basic element of any TMD file. Though throughout this section we'll be using test ( reference)objects, there are many other kinds of objects with different children, fields, and lists. A comprehensive list of object types can be found in section Reference.
The following is a bare-bones object declaration:
Foo <test> { }
The "Foo" part is the name of the object - this name is used only internally, and may consist of letters, numbers, and the underscore ('_'). Spaces will be ignored, as mentioned below.
Next is "<test>" - the angle brackets are to set it apart from the name (Foo) and the text within determines what sort of object this is. Different objects have different children and fields, and serve different purposes.
Finally, the opening and closing brackets contain the declaration for the object. In this case we're leaving it empty, which will give the children, fields, and lists of Foo their default values; empty, null (or zero), and empty, respectively.
Note the space between "Foo" and "<test>" and the linefeed after the opening bracket. None of that is necessary; except for the inside of quotes, the parser strips out all whitespace, the above declaration is exactly the same as the following:
Foo<test>{}
Fields are the textual and numeric portions of an object. A ship has fields for things like speed and its name. Fields can either be numeric or a string. When you specify a value for a field, you are overriding whatever was there before; usually the default value, though for plugins you'll be overriding the value in whatever file was loaded before yours. Notice from the examples that you have to end each field declaration with a semicolon.
The more common of the two kinds of fields is the numeric field. Numbers are represented pretty much like you're expect: An optional + or -, some digits, a decimal point and more digits if you want them. All numbers are represented internally as double-precision floating point numbers. Here's an example of an object that has a numeric field:
Damon <test> { field number = 4.5; }
Ships need names, so TMD needs a string field. Strings start with quotation marks; from there you can enter any text you want, using the blackslash to escape quotes. For example:
Edna <test> { field test = "They say I'm a \"spiffy\" gal!"; }
When used, the test field ends up looking internally as: They say I'm a "spiffy" gal!
Backslashes can also be used to escape other backslashes if you'd like them to make it to the engine intact, and newlines if you want to split strings across multiple lines - if you don't escape the newlines, they'll actually end up in the string! (On the other hand, if you want newlines in your string, you can use the escape sequence \n)
Fred <test> { field test = "Everyone says that my catchphrase is entirely\ too long. I, on the other hand, like to think that it suits my\ long-winded philosophy on life."; } Gertrude <test> { field test = "I \nLike \nNewlines\nAnd\n\\Backslashes\\!"; }
There's an easier way to split long strings, though: using a '+' symbol between sets of quotation marks:
Fred <test> { field test = "Everyone says that my catchphrase is entirely" + " too long. I, on the other hand, like to think that it suits my" + " long-winded philosophy on life."; }
The '+' symbol can lead to cleaner looking files, especially if the field is indented. The only caveat is that you have to pay attention to spaces, otherwise words may accidentally run together.
Of course, test objects give us a good way to practice syntax, but aren't very good for showing us what fields are actually useful for. Here's a real object from a plugin (just ignore that 'child' part, we'll get to that in a moment):
superCraft <ship> { field acceleration = 75.0; field angular = 250; field jumptime = 1; field traveltime = 1; field maxfuel = 10000; field maxspeed = 300; field cargospace = 700; field hardpoints = 50; field mass = 500; field shields = 10000; field title = "Ultimate Shuttlecraft"; child image.add colonyShuttleImg; }
Here you can see the use of fields to indicate the acceleration of this kind of ship, among other properties.
Children of an object essentially point to other objects. Whenever an object needs to reference another, it does it through its children. Internally, the children are implemented by a list of pointers to objects, so if you hear a child being referred to as a list, this is why. Try not to confuse them with lists ( below) since that would be rather catastrophic. In most cases, however, the context makes it obvious.
Here's an example of an object with a child:
Holly <test> { field test = "I'm the original Holly"; child dummychild1.add Adam; child dummychild1.add Beth; }
In this case, the dummychild1 child of Holly points to Adam and Beth (in that order). If nothing is specified for a child, that child defaults to simply being empty.
As it stands, every time you want to refer to another object you have to create that object somewhere else in the file. While you're perfectly free to refer to the object before you've created it -- i.e. you could put the "Holly" object anywhere in the file and so long as Adam and Beth were created somewhere everything would still be all right -- it is still an annoying limitation. Luckily, there's an alternate syntax:
Holly <test> { field test = "I'm the new and improved Holly!"; child dummychild1.add(Indigo <test> { field test = "Look, a circular reference!"; child dummychild1.add Holly; child dummychild2.add Indigo; }); }
The open parenthesis after the 'add' statement tells the parser that an object is going to be created right then and there. From then on, it's normal object syntax, then a closing parenthesis and a semicolon. The object created here is in every respect the same as any other object; it can be referred to in other objects, even before it is created. This is simply a syntactic shortcut.
Another note: You might wonder what would happen if you put the two different 'Holly' objects from the same example in the same file, or if you had a plugin where there was also a "Holly" object. Would the parser complain, would there be a crash? Nope - when you specify an object's name (on the line "Holly <test> {") the first thing the parser does is check to see if an object with that name already exists. If so, anything specified afterwards is assumed to refer to the same object. It may seem confusing, so here's an example:
The two objects after one another:
Holly <test> { field test = "I'm the original Holly"; child dummychild1.add Adam; child dummychild1.add Beth; } Holly <test> { field test = "I'm the new and improved Holly!"; child dummychild1.add(Indigo <test> { field test = "Look, a circular reference!"; child dummychild1.add Holly; child dummychild2.add Indigo; }); }
Is, in every way, the same as:
Holly <test> { field test = "I'm the new and improved Holly!"; child dummychild1.add Adam; child dummychild1.add Beth; child dummychild1.add(Indigo <test> { field test = "Look, a circular reference!"; child dummychild1.add Holly; child dummychild2.add Indigo; }); }
This is essentially how plugins are made; children get added onto and fields get overriden (note Holly's 'test' field is the second one encountered). For the sake of those who will have to maintain your scenarios, you'll probably not want to split your objects into more than one section even if it would work, but it's good to know the behavior when you want to write plugins.
While the utility of fields was pretty obvious without having to see an example (though I provided one anyway), children seem a bit less so. So here's a few examples of objects that make extensive use of children:
OurMap <map> { child sectors.add Sol; child sectors.add Orion; child sectors.add Vollox; } 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?"; }
In the first object ("OurMap") the 'sectors' child holds all the sectors to be found on the map. In the Militia object, the 'ships' object holds all the ships (in this case, five MilitiaShuttles) and the 'owner' object points to the owner of the fleet. If you look at the reference for the fleet ( reference) object you'll see that the 'ships' child should contain FrozenShip objects. It's not unusual for children to only hold one type of object; in fact, it's very common. Tmdverify will not point out if you've used the wrong type, since it's syntatically correct to have any object be added to the list, so you'll need to rely heavily on the reference to avoid crashing the game. Most of the time, though, you can tell what sort of object goes with what child simply by context; there's not a whole lot of objects that would logically go in a 'sector' child, for instance.
Finally, here's an example of an object that uses the inline method of creating another object:
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; }
Weapons are one of many types of object that require an 'image' child; since image objects are so simple, you're likely to see this syntax used often with them. In this example we create a BlueLaserImg object (of type <image>, which is what the image child expects) at the same time we define the Laser object. Keep in mind that if you want to use the laser2.png image again for another object, you don't have to define it a second time, you can simply refer to BlueLaserImg.
As somewhat of a lesser-used compliment to the Children as a list of objects, the list type provides a list of numbers.
The syntax for lists is pretty easy:
James <test> { list listtest = { 3,1,3,3,7 }; }
You can put any number of items in a list, though usually the number of items is determined by some other factor. Again, the reference is your friend in these cases.
Whereas a field will be overridden if specified again, lists like children are appended to. So if I had a plugin loaded that read as follows:
James <test> { list listtest = { 1,1,2,3,5 }; }
The final list would contain 3,1,3,3,7,1,1,2,3,5 (in that order). Of course, simply adding onto a list is no fun at all; for this reason there's a 'new' keyword supplied. If I have yet another plugin that's loaded after the scenario and plugin above that looks like this:
James <test> { list listtest = new { 1,2,3,5,7 }; }
Then the final list would only contain 1,2,3,5, and 7.
There are far fewer objects that use lists in TraderMissions, but those that do use them in complicated ways, so it's best to know what to look out for. Here's an example:
Humans <organization> { field title = "Humanity"; list attitudes = { 50.0, 100.0, -200 }; }
I've cut out the vast majority of this object to highlight its use of a list. Here we see the attitudes that this organization has towards other organizations. But how do you know what order that list is in? Who do the numbers even refer to? The organization( reference) documentation states that the list " must be as long and in the same order as the list of organizations in gamestate". Thus, the first item in the list refers to the Humans' feelings toward the first "orgs" child of the gamestate. Lists are usually paired up with children in this way.