All commands used in scripting is written in a script file. This document will explain how a script file is constructed.
All scripting commands are put into *.scr files. These are standard text files that can be created in any text editor ( such as Notepad on the Windows platform or VI & Emacs on the UNIX platforms ). These files end with .scr instead of .txt as text files often end with.
This .scr suffix is sometimes a problem on Windows platforms, because most Windows platforms are configured to interpret files ending with .scr as screensaver applications, so if you just double click on an *.scr file in Windows - you get an error message saying: "C:\some_catalog\filename.scr is not a valid WIN32? application.". So you will generally open your script files from the text editor instead of clicking on the file directly.
An important part of an *.scr file is its comments.
Basically: anything placed after 2 '/' characters will be ignored by MOH and can be used by the scripter to place helpful comments and explanations of what is actually happening in the script file, without making the game interpret it as wrongful scripting.
Everything after '/*' will be ignored by MOH. It will ignore everything until it finds '*/'. This means that you can comment out large areas ( spanning multiple lines ) of a script.
Example:
// Pops the first player's helmet off if he's got one... $player[0] pophelmet // I'm so funny /* ...yes, its useless... but I'm a scripter, I can do whatever I want! */
...is the same ( to MOH ) as writing:
$player[0] pophelmet
In C, comments of the following form are often used:
/* * This is a block comment. */
However, in MOH, this form of comment will cause an error, because MOH appears to require the /* to not be at the end of the line. The workaround is to put a space after the /*.
Inside the *.scr file the commands should be placed inside methods. Methods are collections of commands placed within an area of the file starting with a method label ( or method name ), and ending with the keyword end. It looks like this:
a_good_method_label: // No commands in this method, but there COULD be... // ...because this is just an example. end
Note that commands which cause a thread to execute from a specific label are effectively doing a 'goto' to that label; unlike most modern programming languages, MOH's scripts do not truly support the notion of a function or method, so the convenient method behaviour is constructed from combining the label and end commands. Because of this, the following code is valid:
foo: iprintln_noloc "foo" bar: iprintln_noloc "bar" end
Calling "waitthread bar" will cause "bar" to be displayed on the screen, and "waitthread foo" will cause both "foo" and "bar" to be displayed on the screen. "end" is simply a command which instructs the thread to terminate, and in the case where the thread was started due to "waitthread", causes the calling thread to resume execution after the "waitthread" call. The same applies to exec calls, "end" causes a jump back to the statement after the "exec" call -- dcoshea ( edited by Bjarne ),
A main method is a method with the method label named 'main'. It is special because it is the "default" method... Default in this case means that if a command is given to execute the script named my_cool_script.scr like this:
exec my_cool_folder/my_cool_script.scr
...what will actually happen is this:
exec my_cool_folder/my_cool_script.scr::main
So? Well... this is what will happen to the main script connected to your map. If your maps name is my_first_map, and it is a DM map, then MOH will automatically attempt to do this when loading your map:
exec maps/dm/my_first_map.scr
...and so it is necessary for you to have the 'main' method in your maps script file if you actually want it to be executed.
Note: the name "main" does not technically have to be used. Map scripts are executed starting from the first line of the file, regardless of any labels, and in fact no label has to be present, as can be seen in some single-player map scripts that come with the game. The initial thread will simply execute the script until an "end" statement is reached. "exec" also does not care about labels - it also starts executing the script from the first line of the file. An example of a script that uses this functionality is shown in this post on TMT.
So now it is time to build the main method. Very simplified, it looks like this:
main: // Stuff to do before prespawn level waittill prespawn // Stuff to do before spawn level waittill spawn // Stuff to do before roundstart level waittill roundstart // Stuff to do after roundstart end
For this to make any sense, you need to understand at what times the prespawn, spawn and roundstart events occur.
prespawn
Prespawn occurs when the map is loaded, and ready to receive players.
spawn
Spawn occurs when the first player enters the map.
roundstart
Roundstart only occurs in multiplayer objective games, after at least one player has spawned in the map on each side ( at least one Axis and at least one Allied soldier ).
Here is a complete example *.scr file with lots of comments to explain what happens.
// This is just info telling other who made this stuff // and where to get more info, if needed. // // obj_vemork_12 // ARCHITECTURE: Bjarne Grönnevik // SCRIPTING: Bjarne Grönnevik // LINK: http://www.gronnevik.se/rjukanproject/ // VERSION: 1.2 main: // Sets up the text that the players will see // when selecting team and, when pressing the // scoreboard key when playing. setcvar "g_obj_alliedtext1" "Smash the distillers" setcvar "g_obj_alliedtext2" "or clear the factory" setcvar "g_obj_alliedtext3" "from Axis forces" setcvar "g_obj_axistext1" "Defend the heavy water" setcvar "g_obj_axistext2" "production plant from" setcvar "g_obj_axistext3" "the allied assault." // Sets up the picture that the players will see // when pressing the scoreboard key when playing setcvar "g_scoreboardpic" "obj_vemork_12" // Sets up a fog effect $world farplane_color "0.3 0.3 0.3" $world farplane 5500 level waittill prespawn // Loads a lot of data used in MP games, so that there // will be no "Please wait, loading..." messages when // playing :-) exec global/DMprecache.scr // Some scripts use this, you wont... I just put it // in to be sure. level.script = maps/obj/vemork_factory_small.scr // Tells MOH what background music to play exec global/ambient.scr m6l1a // Initialize the exploder subsystem ( used for bombs // and stuff ) thread global/exploder.scr::main level waittill spawn // Axis like the bombs unplanted level.defusing_team = "axis" // Allies will try to plant the bombs level.planting_team = "allies" // How many objectives to blow before winning level.targets_to_destroy = 3 // Default damage of the bomb level.bomb_damage = 200 // Default radius of bomb blast level.bomb_explosion_radius = 2048 // Will a player respawn when dying? 1 or 0 (0=no respawn) level.dmrespawning = 0 // round time limit in minutes level.dmroundlimit = 5 // If time runs out: who will win the match? // set to axis, allies, kills, or draw level.clockside = axis level waittill roundstart // By not setting up the bomb and start the win checks // until after roundstart we prevent a single allied // soldier to enter the map and win the map without // resistance. $distiller_bomb thread global/obj_dm.scr::bomb_thinker // Start the win check thread for allies thread allies_win_check // Start the win check thread for axis $distiller_bomb thread axis_win_timer end /******************************************************** * Allies victory test ( Win by acieving all the * 3 objectives or eradication of opposing force ) */ allies_win_check: while(level.targets_destroyed < level.targets_to_destroy) { waitframe } teamwin allies end /******************************************************** * Axis victory test ( Win by timeout or eradication of * opposing force ) */ axis_win_timer: level waittill axiswin // At the end Axis win end
- Bjarne