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

```
//Default Script for AI

Block
{
Frame me; FRM\_GetMyFrame(me);
SetAlarmType(1,1);
SetAlarmType(2,1);
SetAlarmType(4,1);
SetAlarmType(8,1);
SetAlarmType(16,1);
SetAlarmType(32,1);
SetAlarmType(64,1);
SetAlarmType(128,1);
SetAlarmType(256,1);
SetAlarmType(512,1);
}

//------------------------------------------------------------------------------------------

OnDeath(){
EndScript();
}

OnAlarm(){
EndScript();
}

OnAlarmDone(){
HUMAN\_SetAlarm(false);
goto END;
}

//------------------------------------------------------------------------------------------

goto END;

Label END:

```

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(door1, 1);

SetActorState(crate, 0); // 0: crate visible 2: crate hidden`
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

`Frame frame1; FRM_FindFrame(frame1, "object1");

SetActorState(frame1, 1);`

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:

`Frame me; FRM_GetMyFrame(me);`
Usually the frame is called "this" or "me" but that's up to you.

Example

`frame me; FRM_GetMyFrame(me);

FRM_SwitchFaceTexture(me, "e_f024");`

## 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

`Block {

Frame Soldier1; FRM_FindFrame(Soldier1,"01_E01");

Frame Soldier1; FRM_FindFrame(Soldier1,"01_E12");

frame Soldier3; FRM_FindFrame(Soldier1,"01_E03");

Frame Obj1; FRM_FindFrame(Obj1,"Objective1");

Frame me; FRM_GetMyFrame(me);

Frame Documents;

}`

## OnAlarm()

Used for human AI.

Following alert types are available:

 512 | Sights corpse

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.

`Integer AIEvent = 0;

Integer counter = 0;`

`OnAlarm() {

AIEvent =_GetAlarmType();`

`If((AIEvent == 1) OR (AIEvent == 64))

{

HUMAN_SETMODE_Lie();

}

If(AIEvent == 512)

{

counter = counter + 1;

If(counter==2)

{

EndScript();

}

goto End;

}

EndScript();

}`

`Label End:`

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.

## 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

`Whenever name ((_PlayerIsNear(20)) AND (_FrameIsNear(tank, 50)))

{

...

}`

### 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!

`SetWheneverTime("PIR", 5000);`

`Whenever "PIR" (_PlayerIsNear(20))

{

...

}`

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.

 \_PlayerInBox(frame) | As box you could use a scaled dummy

## If/Else

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

`Integer a = 0;`

`a=_RandomInt(5); //Creates a random integer number of the first 5 integers either 0,1,2,3 or 4.`

`If(a==0)

{ ... }

If(a==1)

{ ... }

If(a==2)

{ ... }

Else { ... }`

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

`Label 1:

[code]

GoTo 1;`
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:

`GoSub 1;

HUMAN_SetAnim("%%turnright", 300, 300, false);

GoTo End;`

`Label 1:

Delay(5000);

HUMAN_SetAnim("%%turnleft", 300, 300, false);

Return;`

`Label End:`

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:

`OnSignal(3) {

goto L4_a;

}`

`label L4_a:

If (_PlayerInRange(9) )

{

HUMAN_Move("AlBert_2");

goto L4_b;

} else {

HUMAN_TurnAtNearestPlayer();

delay(150);

goto L4_a;

}`

`label L4_b:

If (_PlayerInRange(9) )

{

HUMAN_Move("AlBert_3");

goto L4_c;

} else {

HUMAN_TurnAtNearestPlayer();

delay(150);

goto L4_b;

}`

`label L4_c:

If (_PlayerInRange(9) )

{

HUMAN_Move("AlBert_4");

goto end;

} else {

HUMAN_TurnAtNearestPlayer();

delay(150);

goto L4_c;

}`

`Label end:`

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 / Game Value

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

`SendSignal(frame, x);

Whenever name (_SignalReceived(x)) { ... }

OnSignal(1) { ... }`

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

 4 | Objective hidden

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

```

## 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;

```