A Beginner's Guide to Scripting
Scripts will give extra power and flexibility to the way your Dark Mod Fan Mission works and let you add new effects as well as simplifying some tasks that would be complicated in mapping.
This first page of the Beginner's Guide is an introduction to scripting: how to set it up and get it working and gives a simple basic understanding of essentials ready to start writing your own useful Dark Mod scripts.
Introduction
This is a simplified guide for beginners to dark mod scripting. It makes only two assumptions:
- You have done some mapping so know what is meant by 'entity', spawnarg, trigger, target, etc.
- You have some interest in programming and so have at least dabbled in another language and have some idea what variables (x=99), conditions (IF A=B), loops (FOR N=1 TO 10), and subroutines, procedures or functions are (GOSUB, PROC, CALL) and so on. These are common to all programming languages and only the syntax and terms and way used need be learned to start scripting.
This guide is likely to omit advanced material so do not think it fully comprehensive.
I'll describe how to write short scripts demonstrating all of the above basic essentials by example so starting with a blank sheet you will at the end have working scripts and understand how you can use them in your maps. In addition there is a keyword reference index so you can quickly review or find new controls and commands to include and to extend your scripting ability.
Learn by Doing
You can just read any parts of the following you want, especially if you already have some knowledge of scripting but a complete beginner should at least study it all. But to get the best out of it you ought to build the test maps and scripts as you go along. They only take a few minutes and you will absorb more and be confident you understand because you can see that you, yourself, have got them working. You can also modify the scripts to see the effects and some of them will be of use to copy and paste into scripts you can use in your missions.
Create a Test Map
Create a test map or use one you have. Any name or size but for this guide I will assume you have built a reasonably big one-room map named testScripts.map
Creating a Script File
Using a plain text editor create a blank file with the same name as your map, eg, testScripts.script and save it in the same folder as your map.
Script File Format
If you are familiar with 'C'-like languages then you can skim over the next few sections to #Sending Text to the Console to Test and Debug
Instruction Lines
Script code consists of lines of instructions which are carried out in order:
Do this Do That Do Something else
Each instruction line can spread over more than one text line in the file like so:
Do this Do That Do Something else
So because the end of the instruction line is not necessarily the end of the text line, each instruction line must end in a semi-colon to denote its end:
Do this; Do That; Do Something else;
In practice, most instruction lines fit on one text line anyway.
Grouping Structure
Almost all instruction lines are grouped together as functions, conditional structures, loops, etc. each within curly brackets with a preceding header. These must NOT have a semi-colon after them like the instruction lines.
It is crucial that there be an opening curly bracket and a closing bracket always in pairs for each group so common practice is to indent text (typically a tab space) within these groups to make it easier to read:
header { Do this; Do That; Do Something else; }
Note the semi-colons at the end of the instruction lines but NOT after the header or curly brackets.
Technically it does not matter how they are placed on the text lines so the above could be written like this and still work:
header{ Do this; Do That; Do more; Do Something else;}
Most commonly, groups are written like this with the first curly bracket at the end of the header line:
header{ Do this; Do That; Do Something else; }
You'll see that a lot when studying other scripts but in this tutorial I'll be putting the curly brackets on their own lines for clarity.
Groups can be placed inside groups, and more nested inside those so long as every group starts with a header and left curly bracket and ends with a curly bracket:
header { Do this; Do That; Do Something else; AnotherHeaderInsideTheFirstGroup { Do even more; Do it better; } Do a bit more; }
Functions
Almost all scripting is within functions which are group structures with a header name followed by curly brackets containing the instruction lines (as described in the preceding sections.)
You can have many different scripts and functions in one script file. Some may work together as part of one combined task(see later) while others may be totally unrelated and do a completely different job in your map. So you can add more scripts to your script file at any time to do other tasks. The only thing common is that they are all used by the one map with the same name as the script file. If you want to use any of them with another map you just copy and paste and maybe edit them in another script file with the same name as that other map.
The function header is always preceded by its type name and followed by two normal brackets containing any data (arguments) separated by commas needed to be sent to the function. Notice that you do not add a semi-colon ; at the end - semi-colons are generally only to mark the end of lines of instruction. This is how the function header is set out...
type FunctionHeaderName(arg1, arg2, arg3.....)
If no arguments are needed then use empty brackets:
type FunctionHeaderName()
The 'type' is the type of value returned by the function, eg, number, string of characters, etc. (more later.) If there is no value being returned then use 'void' instead thus...
void FunctionHeaderName()
So a typical function would look like:
void FunctionHeaderName() { Do this; Do That; Do Something else; }
Sending Text to the Console to Test and Debug
This is the first real script and is useful to check that your script is actually working at all and also to show real information that will help you test that it is working correctly.
Creating a Function
Firstly, as described in the previous sections, you need to create a function group structure to put the script in. We can give it almost any sensible header name. Let's call it 'SendToConsole'. So type the following into your script file and save it...
void SendToConsole() { }
- What you might call a 'command' in other languages is similar to what in Dark Mod scripting is called a 'script event'.
- The command or script event to send text to the console is 'print'.
- In scripting these script events always need to be associated with an entity.
- Event and entity are written together with the entity first, then a period (full stop) then the event, ie, entityname.event.
- If there is no specific entity like with 'print' then the sys entity is used.
So in this case to send text to the console we need sys.print.
- The data (the text we want to print in this case) to be used by the event follows it within ordinary brackets () ie, entityname.event(data).
- We can print numbers or we can print strings of characters within double quote marks.
- Use \n within double quotes to force a linefeed, eg, "Line1\nLine2"
- Use the plus sign to combine different strings and numbers and variables, eg, ("\n"+x+" green bottles")
Here is the instruction line...
sys.print("\n\nYour script is running");
Remember the semi-colon on the end. The two "\n\n" are not crucial - just to do a couple of linefeeds so its easier to spot our line amongst the other console stuff. Type it into your script file within the curly brackets of the empty function you already created and save it. It should look like this...
void SendToConsole() { sys.print("\n\nYour script is running"); }
Running the Function
On its own the function will do nothing. There are several ways to get it to start working. This is how we'll do it here:
- In your map, create a target_tdm_callscriptfunction entity.
- Give it the property/value: call SendToConsole (Function names are case-sensitive so sendtoconsole won't work - it must be exactly how you typed it in the script file.)
You need to trigger that any way you like; we'll make a lever and target the above:
- Create a model: models/darkmod/mechanical/switches/switch_rotate_lever.ase
- Change its classname to atdm:mover_lever
- Give it the property/values:
- rotate 0 0 -45
- target <name of target_tdm_callscriptfunction entity>
Try the map; frob the lever then check the console to see if it has printed the debug test text.
Troubleshooting
Note: To reload your script after changes, reload the map. If you use the command reloadScript in the console it exits the map anyway.
If you can't see the script output text in the console then enter 'clear' and try again. If no text then carefully recheck your work with the preceding sections. Some things you can try to debug:
- Any relevant error message in the console?
- Is your script file actually in the same folder as your map?
- Is it precisely the same name as your map?
- Does it really' have the suffix .script and not .txt or .script.txt?
- Add a light to your map and target it with your lever to make sure your lever is working.
Variables: Handling Values and Text
Briefly, variables are labels (names) used to store and represent values or text. I'll only summarise two types here for now but for more details on scripting variables see Scripting basics#Syntax
- Numbers are stored as floating point decimals eg, 99. 25 or 1.23 and so on.
- Text is stored as strings of characters "The cat sat on the mat"
- Both must be created by declaring it in the script on an earlier line before they are actually used.
- Only one variable can be in each declaration.
- The variable names can be any legal alpha characters you like, eg, x, test, a, myVariable.
Number variables are created with the keyword 'float' and text with 'string' as in the following examples:
float x; string s; string testString; float testValue;
You can then give them any value or text you wish and vary them at any time (which is why they are called variables of course.) You do this with the following format (spacing optional):
x=99; testString="Hello World"; testValue = 1234.55555;
You can combine the original creating declaration and assign their contents in one instruction:
float x = 99; string testString="Hello World"; float testValue = 1234.55555;
If you type those in at the top of the script file (not inside a function) then all script functions can share them. These are called global variables
Normally you will type them at the top of and within a function so they are only used by that function and will not conflict with other functions even if they use the same names (eg, x can be 99 in one function and x can be 7.5 in another without conflict.)
Try these out and experiment by adding to the previous script as follows. Note the variables are created inside the function and they are created first.
void SendToConsole() { float x = 99; string testString="Hello World"; float testValue = 1234.55555; sys.print("\n\nYour script is running"); sys.print("\n x="+x); sys.print("\n x multiplied by x = "+x*x); sys.print("\n testString = "+testString); }
Comments, Remarks
You can (and should) add comments to your scripts where necessary to help explain and clarify the script both for others and for yourself at some future time when you might not remember exactly. Two ways to add a comment:
Use a double slash // followed by your text comment on one line and that line will be ignored by the script - it is purely human information. Example:
//Script to give the player magic boots //That let the player fall slowly and run faster. //Written by Fidcal
You can also enclose multiple lines within /* and */ if you prefer. Example:
/*Script to give the player magic boots That let the player fall slowly and run faster. Written by Fidcal*/
That concludes this introduction to scripting. On the next page we shall begin writing scripts that do useful tasks in your maps...