Hidden & Dangerous 2 Wikia
Advertisement

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:

Integer Alert Type
1 Close Shooting
2 Enemy shots
4 Hear steps (not working for clients)
8 Action sound (not working for clients)
16 Explosions (not working for clients)
32 Sighted
64 is injured
128 From script - don't know how it works
256 Friendly shots
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.

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

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.

Whenever condition Explanation
_PlayerIsNear(x) x=range(Int)
_EnemyInRange(frame, x) x=range(Int)
_MP_IsTeamMemberInRange(1, x) x=range(Int)
_SignalReceived(x) x=Signal number
_ACTOR_GetState(frame) == x x=actor(object) status(Int)
_MP_IsTeamMemberInRange(1, x) x=range(Int)
_FrameInRange(frame)<x x=range(Int), allowed operators: <,>,<=,>=,==,!=
_MP_IsItemInTeamInventory(1, x, frame) x=itemlist number(Int)
_MP_IsTeamMemberInRange(1, x) x=range(Int)
_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.
command example explanation possible values works on dedi?
HUMAN_Suspend() HUMAN_Suspend(1); Suspends AI until it gets activated again true/false ?
HUMAN_SetOptimized() HUMAN_SetOptimized(1); Suspends AI until it gets activated again true/false ?
HUMAN_DriveNearPlayer() HUMAN_DriveNearPlayer(frame, distance); Suspends AI until it gets activated again true/false ?
HUMAN_Drive() HUMAN_Drive("checkpoint1", speed); Suspends AI until it gets activated again true/false ?
HUMAN_MoveFromFrame() HUMAN_MoveFromFrame(frame, distance); AI moves away from frame true/false ?
HUMAN_MoveToFrame() HUMAN_MoveToFrame(frame, distance); AI moves towards a frame true/false ?
HUMAN_SetAnim() HUMAN_SetAnim("Animation",500,500,Loop); AI performs an animation; 3 values in that order: Time, Time, Loop(True/False) true/false ?
DisableSignals() DisableSignals(1); Turns on/off signals true/false ?
DisableWhenevers() DisableWhenevers(1); Turns on/off all Whenevers true/false ?
DisableAlarms() DisableAlarms(1); Turns on/off alarms true/false ?
SetWhenever() SetWhenever(name, 1); Turns on/off alarms true/false ?
SetWheneverTime() SetWheneverTime("name", time); Turns on/off alarms true/false ?
SetObjectiveStatus() SetObjectiveStatus(1, 2); Turns on/off alarms true/false ?
SendSignal() SendSignal("name", value); Turns on/off sending signals true/false ?
Delay() Delay(5000) Delays Time of human actors true/false ?
SUBTITLES_SetText() SUBTITLES_SetText(05011001); Text subs of human actors true/false ?
SUBTITLES_SetOn() SUBTITLES_SetOn(true); Turns subs on/off human actors true/false ?
MP_EnableSpawnZone() DisableAlarms(1); Turns on/off alarms true/false ?
FRAME_SETAIMODE_Zombie()
FRAME_SETAIMODE_Defensive
FRAME_SETAIMODE_Aggressive
DisableAlarms(1); Turns on/off alarms true/false ?
HUMAN_SETMODE_StandFast
HUMAN_SETMODE_Stand
HUMAN_SETMODE_Crouch
HUMAN_SETMODE_Lie
DisableAlarms(1); Turns on/off alarms true/false ?
HUMAN_SETMODE_Silence
HUMAN_SETMODE_Guard
HUMAN_SETMODE_Walk
HUMAN_SETMODE_Run
DisableAlarms(1); Turns on/off alarms true/false ?
MP_EnableSpawnZone() DisableAlarms(1); Turns on/off alarms true/false ?
HUMAN_TurnAtNearestPlayer() HUMAN_TurnAtNearestPlayer(); Turns to nearest player none ?
HUMAN_TurnAt() HUMAN_TurnAtNearestPlayer(); Turns to nearest player none ?
FRM_GetNearestPlayer() HUMAN_TurnAtNearestPlayer(); Turns to nearest player none ?
HUMAN_Kill() HUMAN_Kill(frame); Example frame ?
FRM_SetOn() FRM_SetOn(frame,true); Enables/Disables sound which must be listed in .bin file frame, true/false ?
HUMAN_Suspend() HUMAN_TurnAtNearestPlayer(); Example true/false ?

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.

State INT Meaning
0 Objective uncompleted
1 Objective done
2 Objective failed
3 Objective failed (Mission failed)
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);

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