Starsiege: An Introduction to Mapping and Scripting
by Orogogus


What Do I Need To Start?
  • A full retail copy of Starsiege
  • The 1.004 patch. Look for it at the Starsiegeplayers patch page.
  • A text editor, for scripting. I use Editpad, myself, but I'm sure there are better ones out there. Try to use one that will show line numbers, Notepad and Wordpad aren't ideal choices.


    Now What?
    Start by creating a shortcut to the Starsiege mission editor. Assuming you have the 1.004 patch installed, this is pretty easy. Just right click on your desktop, choose New, and select Shortcut. For the command line, enter

    C:\Games\Starsiege\Starsiege.exe -me

    (You'll have to change this to reflect the directory that you have Starsiege installed in, of course). Then, for the name, enter something like Mission Editor, and you're done. You can move the shortcut into the Start Menu, into the taskbar (if using Win98), or leave it on the desktop, whatever's most convenient for you.

    What this shortcut does is enable the mission editor. Whenever you run this shortcut, Starsiege will start as normal, except that the Campaign button will be blacked out. Click on the Multiplayer button, and you'll notice that the Join Game button is also blacked out. Click on Create Game, and it will look just like you were setting up a nondedicated server. Choose the mission you want to edit, and click on Create Server in the lower right. When you join the game, the map will start in mission editor mode instead of the first person mode. From here, you can change the terrain map, add new features, and tinker slightly with the script.

    Having said all that, it's best not to do anything with the mission editor just yet -- you can do some serious damage to a map if you don't know what you're doing. Press ALT-F4 to close down Starsiege, mission editor and all, and keep reading for a little bit.


    Messing Around With Files (for the beginner)
    Broadly speaking, any file with a .cs extension in your /Starsiege directory structure can be edited with a text editor. However, as a beginning scripter, the easiest ones to start out on are the ones in /Starsiege/Multiplayer. If you look through this folder, you'll see that most of the files here correspond to the multiplayer missions available in the game.

    Each map consists of three files: a .ted.vol file, a .mis file, and a .cs file.

    The .ted.vol file contains the terrain data, which consists mostly of topographical information (mostly slopes and elevations, as well as terrain textures).

    The .mis file contains the information on the objects in a map. These include drop points, buildings, AI units and certain aspects of the map data.

    The .cs file contains the script itself, and is responsible for most of the tricks and functions that happen behind the scenes on a map.

    The .ted.vol file must be the same on the server and the client machine, or else the two will not synchronize properly, resulting in kicked or confused players when they join your server. Unlike the .ted.vol file, the .mis and .cs files only have to reside on the server, and not on the client. The server will send all the necessary information in these two files to any clients when they join, so anyone can join a map even if you edit the .mis and .cs files.

    A map needs to have at least a .cs file and a .mis file to work, which must have the same name (e.g., DM_Impact.cs and DM_Impact.mis). Note that a map doesn't need its own .ted.vol file. The .ted.vol file for a map is specified in the .mis file, and more than one .mis can share the same .ted.vol file.

    The usual way to start off creating a new script/map is to make a copy of one that's already in the game. Since you probably want to make one that anyone can join and play on, it's best to make a copy of one of the maps in the /Starsiege/Multiplayer folder. To do this, copy and paste the .cs and .mis files corresponding to the map you want to work off of. For the purposes of this tutorial, make a copy of DM_Bloody_Brunch.cs and DM_Bloody_Brunch.mis, and rename them DM_MyMap.cs and DM_MyMap.mis. As implied above, you don't need to copy the .ted.vol file; since DM_MyMap is just a copy of DM_Bloody_Brunch, the .mis file still points to DM_Bloody_Brunch.ted.vol for the terrain data. Later, you'll learn how to change this, and how to use this trick to activate the campaign maps for multiplayer, but for now let's just look at DM_Bloody_Brunch.cs. Open the file in your text editor, and you should see:


    // FILENAME:	DM_Bloody_Brunch.cs
    //
    // AUTHORS:  	Chupie Doll & Youth in Asia
    //------------------------------------------------------------------------------
    
    $missionName = "DM_Bloody_Brunch";
    
    exec("multiplayerStdLib.cs");
    exec("DMstdLib.cs");
    
    function setDefaultMissionOptions()
    {
    	$server::TeamPlay = false;
    	$server::AllowDeathmatch = true;
    	$server::AllowTeamPlay = true;	
    	
    	$server::AllowTeamRed = true;
    	$server::AllowTeamBlue = true;
    	$server::AllowTeamYellow = true;
    	$server::AllowTeamPurple = true;
    }
    
    function onMissionStart()
    {
    	venusSounds();
    }
    
    function onMissionLoad(){
       cdAudioCycle("Watching", "Cyberntx", "Cloudburst"); 
    }
    

    Scripting, Part I
    This is as simple a .cs file as you're likely to find, but there's still a lot for the non-programmer to grasp, so let's go through it line by line to teach the basics.


    - Comments:

    // FILENAME:	DM_Bloody_Brunch.cs
    //
    // AUTHORS:  	Chupie Doll & Youth in Asia
    //------------------------------------------------------------------------------
    
    The first few lines are comments, and have no effect on the way the script runs. They're just there for the benefit of anyone reading the .cs file. To make a comment, just put two forward slashes into the script. Everything on the line after the forward slashes will be a comment only and ignored when the script runs. Thus, if you type:

    // The rain in Spain stays mainly on the plain

    the entire line is commented, but if you type:

    $counter1 = 23; // AI counter

    then only the part that says AI counter is a comment. $counter1 = 23; still runs.


    - Statements, Semicolons and Variables

    $missionName = "DM_Bloody_Brunch";
    
    This is an example of a statement. Everything in a script is done with statements, whether it's setting a variable (as in this example), using a function to teleport a vehicle on the field, or calculating the distance between two objects on a map. It's not true that everything in the script is a statement, though. Broadly speaking, the contents of a script can be divided into statements, structure and comments. And since programming isn't my field, I'm just going to say that you can tell it's a statement if it ends in a semicolon. A semicolon does for a statement what a period does for a sentence -- namely, it ends it. While it's easier for everyone to read if you put each statement on its own line, you can pile them all onto one line and it will still (usually) work just the same as long as all the semicolons are in place. The take-home message? Return characters don't close a statement, semicolons do.

    This line is also an example of how to set a variable. In this case, you're setting the variable $missionName to "DM_Bloody_Brunch". This means that whenever you ask the computer what the value of $missionName is, it will tell you "DM_Bloody_Brunch". Use quotation marks around the value whenever you're dealing with a text value, but don't use them if you're working with a numerical value (this isn't strictly accurate, but is easier to remember than the long explanation).

    You can spot a variable because it starts with either a dollar sign ($) or a percent sign (%). Thus, the following are all examples of variables:

    $counter
    %i
    %numberOfTargetsLeft

    The difference between the two is that a dollar sign indicates a global variable, while a percent sign indicates a local variable. Global variables can be accessed anywhere in a script once they've been set, but local variables can only be used inside the function they're created in. There will be a longer explanation later, but it's worth remembering that now. Another thing to remember without understanding (for now) is that you always use one equals sign to set a variable, but use two equals signs together when comparing the value of a variable.

    Besides the dollar or percent sign, variables should be made of letters and numbers only, with no odd symbols. There's probably a limit on how long a variable name can be, but it's fairly large, and since programmers are notoriously lazy typists, they tend to go out of their way to make their variable names as short as possible, so this will probably never come up.


    - Calling a file
    exec("multiplayerStdLib.cs");
    exec("DMstdLib.cs");
    
    exec("filename.cs"); is a statement that lets one script run (or "call") another one. By including this line, it's exactly as if you copied everything in filename.cs and pasted it where exec("filename.cs"); is. So if filename.cs contained:
    %a = 1;
    %b = 2;
    %c = 3;
    
    Then a second .cs file that contained:
    %c = 6;
    %d = 7;
    %e = 8;
    %f = 9;
    exec("filename.cs");
    
    would (or should) be exactly the same as one file that contained:
    %c = 6;
    %d = 7;
    %e = 8;
    %f = 9;
    %a = 1;
    %b = 2;
    %c = 3;
    
    Something worth noting here is that if you set a variable twice, the second time will override the override the first. Thus, the value of %c in the above example is 3, and not 6.

    Another thing is that in the original file, multiplayerStdLib.cs and DMstdLib.cs are the two files being called. These two are standard libraries, containing all sorts of functions useful for multiplayer or deathmatch maps, and were created especially to be called from other .cs files. They're a convenient way to avoid having to type or copy the same functions into every script you make, so that you just need to include a line like

    exec("MyStdLib.cs");

    to include your standard library in your map. This is especially useful if you're creating a series of maps using a similar or identical script (e.g., hide and seek, find and retrieve, racing, etc.).


    - Functions!
    function setDefaultMissionOptions()
    {
    	$server::TeamPlay = false;
    	$server::AllowDeathmatch = true;
    	$server::AllowTeamPlay = true;	
    	
    	$server::AllowTeamRed = true;
    	$server::AllowTeamBlue = true;
    	$server::AllowTeamYellow = true;
    	$server::AllowTeamPurple = true;
    }
    
    This is an example of how to define a function. The biggest clue would probably be the word "function" at the front, but a complete definition would probably be something like:

    function functionName(%arg1, %arg2...)
    {
      statements;
    }
    
    Start with the word function, and then follow with the name of the function followed by a set of parentheses. Everything between the curly brackets underneath is the body of the function, where everything actually happens. A function without anything in its body won't do much

    What's in the parentheses is important -- these are the function's arguments, or the external variables that you can send into the function. A function can have no arguments at all, in which case you just use an empty set of parentheses, but let's see what they do. An example of this is just a little bit further down in the script:

    cdAudioCycle("Watching", "Cyberntx", "Cloudburst");

    where there are three arguments (which are always separated by commas): "Watching", "Cyberntx", and "Cloudburst". This statement runs the cdAudioCycle() function using these three values. The function's definition might look something like this:

    function cdAudioCycle(%track1, %track2, %track3)

    What happens when the function starts is that the value of %track1 is automatically "Watching", %track2 is "Cyberntx", and %track3 is "Cloudburst." It's worth going into some more detail on variables at this point.

    I described the two kinds of variables earlier, %local and $global variables. Once a global variable is defined, it can be accessed anywhere in the script, but a local variable only works inside the function it's defined in. So if you have a few functions, like so:

    function firstFunction()
    {
      %a = 24;
    }
    
    function secondFunction()
    {
      %b = "Catfish";
    }
    
    function thirdFunction(%c)
    {
      %d = %c;
    }
    
    The variable %a will only have a meaning inside firstFunction() -- if you try to use %a in secondFunction(), it won't give you the right value, 24, and will probably just give you a blank if you try to use it. However, if you were to change firstFunction() so that it read
    function firstFunction()
    {
      %a = 24;
      thirdFunction(%a);
    }
    
    then inside thirdFunction(), %d would end up equal to 24. Make sure you understand why:

    FirstFunction() sets %a to 24, and then passes a as an argument into thirdFunction(). Thirdfunction() has an argument, %c, in its definition. When thirdFunction() starts, %c is automatically set to %a, which is already equal to 24. And when you type %d = %c, then that gets set to 24, too. The full line of links goes like %d = %c = %a = 24. However, if you wrote %d = %a; in thirdFunction(), that wouldn't work, because %a only exists in firstFunction(). The only reason the value transfers into thirdFunction() is because there's the argument, %c, in the definition. It's like having a free preset variable when the function begins, or a bunch of free variables, depending on how many arguments you use.


    - Etc.
    function onMissionStart()
    {
      venusSounds();
    }
    
    function onMissionLoad(){
      cdAudioCycle("Watching", "Cyberntx", "Cloudburst"); 
    }
    
    There's not much going over here. venusSounds() is an example of a function without arguments, it doesn't need any extra variables to do its thing.

    The term I'll use for executing or running a function, by the way, is "calling a function."

    The function onMissionLoad() is a special function that runs as soon as the mission loads. In this case, cdAudioCycle() gets the CD tracks going as soon as the map is ready.

    The function onMissionLoad() is another special function, but this one doesn't run until the first player joins the map. Here, venusSounds() is a function to start the bubbly Venus sounds going at random intervals. You can see this in action if you start any of Dynamix's maps: the CD audio will kick in as soon as you join, while you're still in the briefing room, but the terrain noises don't start until you actually get on the map and start playing.















    1