Menu

Wiki usage

Creative Commons License
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 License.
Edit Sidebar
Main > MOHAAScriptLanguageAppendixC

MOHAA scripting language, Appendix C ( Script architecture )

Script architecture

All commands used in scripting is written in a script file. This document will explain how a script file is constructed.

Table of contents

On this page...

The *.scr file

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.

Comments

An important part of an *.scr file is its comments.

Line comment

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.

Block comment

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 /*.

Methods

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 ),

The special main method

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.

Contents of your main method

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 ).

A complete example *.scr file

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

Recent Changes Printable View Page History Edit Page [Attributes] [Printable View] [WikiHelp]
Page last modified on January 31, 2005, at 04:09 PM