Scripting

Introduction
Scripting is one of the most important parts of modifying or creating a complete new co-operative mission. It's like a programming language but just with a neat amount of commands and math operators. You decide here how human AI is supposed to act or when to spawn, you set up objectives and many other things are handled by the usage of scripts.

This page is trying to get you a rough overview about the most important script sections and commands. Just try to peruse some of the scripts in Hidden & Dangerous 2 and together with this page you soon will understand scripting very well. On the first view it may look complicated but in general it's basic math. Take your time, it's worth it! Only with the right scripting your co-op mission can become brilliant.

Tools
For editing scripts it's advisable to use Notepad++. If you have installed it just right-click on any script file (.scr) and select "Edit with Notepad++". To assign every .scr file to Notepad++ please do following: (for Windows)


 * 1) Open Default Programs by clicking the Start button
 * 2) Click on Default Programs and choose Associate a file type with a program
 * 3) Scroll down for .scr file type and select the notepad++.exe (by default: C:\Program Files (x86)\Notepad++\notepad++.exe)

Easier way
 * 1) Open Notepad++
 * 2) Go to Settings Preferences
 * 3) Select "File Association" tab
 * 4) Select customize, enter "scr" and click on the right-arrow-button. Close.

When you are working with scripts, you should always keep following in mind:
 * Each script is read from top to bottom.
 * Each script is linked with an object (human actor, dummy, ..) which must exist in the .bin files, either actors.bin or scene2.bin.
 * You can link more then one object to the same script.
 * Which object is linked to which script is stored inside MPscripts.dta within your Missions folder.
 * If your script seems to have no effect it may because it is wrongly linked or because of an syntax error (e.g. you have forgotten to close a bracket)
 * Commands are not case-sensitive but using some capitals can make your script look much clearer. (compare getactorstate with GetActorState)
 * Commands which have only the impact to turn something on/off you can either write "true/false" or "1/0". e.g "DisableSignals(1);" equals "DisableSignals(True);"
 * Scripts are read by the server host only. So only the host needs the script files and not the clients.

Structure
Most scripts are reffering to human actors (enemies or allied soldiers). In that case following sections are usually present (not necessarily) OnAlarm OnAlarmDone OnDeath Also used for destructible objects like radios OnHit

Comments
As in every programming language you can comment lines which then are not read by the interpreter (HD2). For the scripting language everything behind "//" in one line is getting commented.

Example SetActorState(crate, 0) is read, everything else is commented.

Bind frame
If you want to interact with another object in your script, (must be existent in .bin files) you have to bind in the wanted object as a frame. Usually bound frames are written in the beginning of scripts and within block elements.

Example

Explanation We define an object (here:object1) as a frame (here:frame1) which now can be used in the following code. Using the object name directly does not work. SetActorState(object1, 1);

To get the object of your current script, use: Usually the frame is called "this" or "me" but that's up to you. Example

Block element
You can write several commands inside the Block{} element to load them simultaneously. As a logical consequence, Delay commands are not read inside block elements. However you should do that only if it really makes sense. Loading several commands simultaneously in several scripts might cause problems.

Example

OnAlarm
Used for human AI.

Following alert types are available:

By default all alarm types are activated. SetAlarmType(x, y) command sets alarm on or off. e.g SetAlarmType(4, True);

If you need to enable more then one alarm types at once you can simply count the numbers together. e.g SetAlarmType(12, True); Alarm types 4 and 8 are set true. (4 + 8 = 12)

Alarm types are usually defined at the beginning of the script.

To enable/disable all alarms by once you can either write SetAlarmType(1023, True); / SetAlarmType(1023, False); (1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512 = 1023) or DisableAlarms(False); / DisableAlarms(True);

GetAlarmType reads the recent alarm type and can be stored as any integer variable.

Explanation If our soldier gets alerted the script will first get the Alarm type and saves it in the "AIEvent" variable, which we previously defined as an Integer. Next comes an If query. If the soldier was closely shot (Alarm Type 1) or injured (Alarm Type 64) he will lay down and goes to the next free line in the code, here: first EndScript; But if he spotts a corpse (maybe on patrol) the variable "counter" will become 0 + 1 = 1. Now the pointer goes to label End, where it waits for another input. If the soldier spots another corpse the counter will add +1: 1 + 1 = 2. Now the If argument is true (counter==2) and the soldiers turn to the nearest player and the Script ends. (second EndScript;) NOTE: Do not add movement codes into this section as the human AI will not get into shooting mode until EndScript; is load.

OnAlarmDone
Used for human AI.

OnDeath
Used commonly for human AI but also for destructible objects like vehicles, radios or barrels which can be destroyed.

OnHit
Tells what to do if human AI/object gets hit.

Whenever
Commands in braces are loaded if the condition/s are met. If you need more then one condition just bind them with "and" or if only one of many conditions is needed use "or". Example

SetWhenever
You can turn Whenevers on/off by using SetWhenever(name, True/False)

SetWheneverTime
Defines the time a Whenever event is checked. e.g SetWheneverTime("name", 5000) checks every 5 seconds if the condition is met. Put quotes infront and behind the Whenever names, otherwise it won't work!

Whenevers can be negated. To do so you have to add an exclamation mark infront of the condition. e.g _EnemyInRange(frame, x) becomes negated !_EnemyInRange(frame, x) Now condition is met if an enemy is not in range.

If/Else
Another great tool to get things done are the If/Else arguments. Let's use an example to explain.

If you compare a value with others use double equal signs ("==") And if you just want to assign a value use "=". Don't add annotations directly behind If arguments. (If(a==0) //comment { ... }) Instead use a new line before or after.

If/Else arguments can be used anywhere in the script. The Else part can be skipped. In that case the following line are used as "Else" part.

Example: ... If(a==4) { ... } EndScript; ...

If a is not 4 the script jumps to the next free line of code. Here: EndScript

Most important commands
Note: For a list of all commands have a look here.

GoTo / Label
To jump to a specific part of code in your script, you can use GoTo / Label commands. Each label must be given a name (even a simple number works), followed by a colon. e.g. "Label 1:"

GoTo GoTo's are used to jump to the defined label.

For example, if you want to jump to Label 1, write "GoTo 1;" Of course this label must exist, otherwise the script is broken and won't work prober.

If you want to loop a specific part of code, just add "GoTo 1;" at the end of "Label 1:" e.g It's going to repeat the implemented code until the scripts gets another input e.g from Signals, Alarm, Whenever events, ...

GoSub like "GoTo" but label ends with "Return;" which brings the script pointer back to the next line after GoSub.

Now it's difficult to explain in words but very easy with an example:

Explanation First line says the script to jump to Label 1 where it waits 5 seconds (5000 miliseconds) then the AI performs an animation (here: turning left) The Return command tells the scripts to go back to the GoSub line, meaning it will read the next line: Here the AI performs an animation again (turning right) and jumps to Label End where the script waits for further instructions.

At the end of many scripts is a label defined called End or TheEnd without any code followed, jump there if your Script shall wait for another input.

Another more complex example with if/else contained:

example taken from co_arctic1/r_arc1a_rebel.scr

Animations/SetMode/SetAiMode
HUMAN_SetAnim("%%zabradli1", 500, 500, true); HUMAN_SETMODE_Run; HUMAN_SETAIMODE_Defensive;

Signal
To interact between scripts signals can be used. You can send signals to another script with following command line: "SendSignal(frame, x);" x stands for the signal number.

For the receiving script you can either use an Whenever event or the "OnSignal(x)" command.

Game Value
SaveGameValue(1, 200); LoadGameValue unsuspend_range = _LoadGameValue(1);

Objectives
Setting objectives works with "SetObjectiveStatus(2, 4);" where the first integer represents the number of objective and the second the state of the objective. Objective number The first objective in your mpmaplist.txt code, will be assigned as 1. Second objective as 2 and so on.

Subtitles
Commonly used if objectives are done or on mission start. SUBTITLES_SetOn(true); SUBTITLES_SetText(07991406);		//"OBJECTIVE ACCOMPLISHED: ENEMY GARRISON ELIMINATED." Delay(3500); SUBTITLES_SetOn(false);

ActorState
SetActorState(vysilacka, 1);

Speech & Sound
FRAME get_player; SUBTITLES_SetOn(true); FRM_GetNearestPlayer(vysilacka, get_player); FRM_MorphSpeechDelayed(get_player, 07991610, 3, 10);	//"SAS unit reporting, we have captured the oasis not far from Abn-Sin-Oan!" FRM_MorphSpeechDelayed(faker, 07991611, 3, 10);	//"Roger that! Congratulations!" SUBTITLES_SetText(07991405);			//"OBJECTIVE ACCOMPLISHED: HQ INFORMED." Delay(2100); SUBTITLES_SetOn(false);

frame sound1;		frm_findframe(sound1,"S_poryv1"); frame sound2;		frm_findframe(sound2,"S_poryv"); frame sound3;		frm_findframe(sound3,"S_palmy_poryv01"); frame sound4;		frm_findframe(sound4,"S_palmy_poryv02"); frame sound5;		frm_findframe(sound5,"S_palmy_poryv03"); frame sound6;		frm_findframe(sound6,"S_palmy_poryv04"); integer rnd; integer rnd_pause; label loop: rnd= _randomint(2) +1; rnd_pause= _randomint(13900) +18000; if (rnd==1) {FRM_SetOn(sound1,true);} if (rnd==2) {FRM_SetOn(sound2,true);} delay (2000); FRM_SetOn(sound3, true); FRM_SetOn(sound4, true); FRM_SetOn(sound5, true); FRM_SetOn(sound6, true); delay(rnd_pause); goto loop;