CGF Mission Scripting
Last update: March 12, 2000, by William. List of changes here .
In CGF, missions scripts are the way to define and start a customized battle.
These mission scripts (.cgf files) define the forces and teams in the battle,
their initial positions and plans, and the objectives.
Below, you'll find explained the idea, structure and commands of a CGF mission. Everything
is explained using the urban_hitman_1.cgf mission as an example.
(Note that you don't need to craft missions yourself to use CGF: there are already plenty of
downloadable missions present on the internet (see the links page).
In addition, simple changes to a mission, such as the changing the skill levels, can be done 'automatically' using a CGF mission launcher (see the downloads page).
Mission Structure
A mission basically consists of five mandatory parts, in the following order:
- mission header
- map description
- force definition
- mission objectives and mission chaining
- team plans
- editing locations
The purpose of the mission header is to provide the player (both in the bot
launcher and in game) with summary information about the mission (in the following order):
- title (the mission stated in a couple of words, for example "urban hitman")
the title should start with a character from [a-zA-Z0-9_.,'() /\-\\!:?] and
continue with characters from [a-zA-Z0-9_.,'() /\-\\!:?+=#@\^|%&]
the title need not be unique (missions are identified by their file name)
- author (the author(s) name(s))
the authors will appear in game on the console in future versions
if you edit the mission, make sure you change or modify the author(s)
to include your name
- description (the mission in a couple of lines)
this description appears in-game in the first 5 seconds, and
when the player presses the help key
make sure that the description fits on 512x384 screens -
not every gamer has high end hardware
you can direct the text over multiple lines by using the '\n'
characters where you want a new line
the description should start with a character from [a-zA-Z0-9_.,'() /\-\\!:?] and
continue with characters from [a-zA-Z0-9_.,'() /\-\\!:?+=#@\^|%&]
- rules (defines specific restrictions for the mission)
currently, the rules solely define the respawn behavior (to be: none)
in future versions, expect to be able to fight missions exactly according
AQ2 TP restrictions, or with slightly rules for weapon load out etc.
again, currently, the only (mandatory) rule (and value) are: respawn = none;
For example, the urban_hitman_1.cgf mission header looks like:
// comments are preceded by '//' such as this line,
// or included between '/*' and '*/'
mission "urban hitman"
{
author
"William van der Sterren";
description
"\nyou're the hitman!\neliminate Sabotage\nhe might be protected :)";
rules
{
respawn = none;
};
If you edit a mission, you should definitely edit the header to include a new mission title, description and author name.
The purpose of the map description is to enable the mission objectives and team plans to
refer to locations and areas. The map description also provides some hints to the CGF AI
on the type of terrain.
In addition, it is a good habit to include some kind of
drawing for documentation purposes.
Consequently, the map description consists of the following parts (in the following order):
- map name (the bsp file's name without '.bsp', for example: actcity2)
CGF needs this to load the required map (and the bot launcher uses this name to
check whether you can run the mission)
- map style (optional hints for the AI)
You can tell the CGF AI more about the map using a combination of the
styles 'open', 'indoor', 'dark' and 'urban'.
The actcity2 map would be described as "style = open, dark, urban;"
whereas museum would be best described as "style = indoor, dark;"
in the future versions of CGF, the AI will adapt its default weapon selection
and patrol characteristics using this info. (I probably could write the algorithm
to extract these characteristics from the route file but prefer to improve other
parts of CGF first).
- map annotation (optional ascii art drawing of the map)
In my experience, it's hard to provide meaningful names (or labels) to grid
locations in a map unless you can refer to directions (North, East, South, West)
and areas or buildings. Therefor, I just make up the directions and draw a
ascii art map of the bsp file.
This annotation helps other mission developers a lot when they want to
modify your mission script
The drawing should start with a character from [a-zA-Z0-9_.,'() /\-\\!:?] and
continue with characters from [a-zA-Z0-9_.,'() /\-\\!:?+=#@\^|%&]
- map locations (list of location definitions, providing meaningful labels to locations in the map)
Because most team instructions (and the map areas) refer to locations, it is important to record
the locations here with recognizable names. Locations refer to absolute Quake2 coordinates, and are
independent of the route file in use (provided the route file contains a nearby
way point for the coordinate).
You define a location by defining it's label and the corresponding x, y and z coordinates as follows:
  location locationname = < x, y , z>;
where locationname is a string (starting with a letter, and containing solely letters, the underscore _, and digits).
You can obtain the coordinates for a specific location by loading up the respective map, moving to
the appropriate location, and typing 'coords' on the console.
Make sure you copy the correct coordinates from the console. CGF doesn't like locations that have
no way point nearby.
- map areas (list of areas definitions, providing meaningful labels to patrol locations, additional
information for future AI extensions, and for mission objectives)
Patrolling is done by visiting map areas (instead of locations). Patrolling areas instead of locations
makes the patrolling less predictable (the team only needs check out the area, instead of visiting the
same location again and again).
Areas were also introduced to make up for a lack of environmental description in Quake2. The AI has
troubles recognizing area structure (entrances, exits, windows) at run-time (too expensive). Areas
can provide the additional information.
Areas also are used to define 'occupation' mission objectives: to meet such an objective, a team member should
be within the area and have been in the area for a specific amount of time without threats engaging
the team in that area or threats visiting that area.
Areas are given a name (just like locations). This area name is used when you want to have teams patrol
a specific list of areas. It's not required but a good habit to start area names with 'area_'.
You can tell the CGF AI more about the area using one of the area
styles 'open' (outdoor open area), 'closed' (indoor or enclosed area, having a few
narrow entrances, exits and/or windows, or 'roof'. Combinations of area styles
are not allowed.
An area needs to contain at least one location.
The size of an area (that is, the way point included in the area) is determined at run-time
using a nifty flood-fill like algorithm. You just need to tell CGF when to stop flood-filling. You do that either by specifying a:
- radius (a max distance to any of the defined locations)
- using the height cut-off property for roofs (the flood-fill won't include way points that are located much higher or lower), or
- (not for newbies) specifying exactly the entrances, exits and windows
Though this may sound complex, you whip up simple area definitions as follows:
  area area_E_UnderBridge // under 'bridge' area at urban
    { style = open;
      locations = E_UnderBridge;
      radius = 100; // max distance to listed locations
   };
or
  area area_NW_SniperRoom // sniper room near parking lot
    { style = roof;
      locations = NW_SniperRoom_Rooftop
      // use automatic height cut off instead of radius
   };
More complex area definitions won't be documented until used by the AI.
To have a team patrol, you need to have at least two areas defined.
To use 'occupy' mission objectives, you need to have an area defined.
For example, the map description of the urban_hitman_1.cgf mission looks like:
// map description is preceded by the header
map "urban"
{
style = open, dark, urban;
annotation = "
^N
W E map by Gerbil!
S
+----------+-----+=============----+------+----+--------------------------+
| low |@@ | | . +------+ | |
| buildings|@@ | | . |## | + |
[removed part of drawing for brevity]
| @@ @@ +------------HH---+------| | |
| #| #| south east high roof | |
+----------------------------------+--------------------------------------+
";
/*
locations
*/
// sniper room locations (north west)
location NW_SniperRoom_Rooftop = <-1346, 634, 280>;
location NW_SniperRoom = <-1273, 879, 316>;
location NW_SniperRoomCorner = <-1391, 800, 316>;
// parking
location W_Parking_TopFloor = <-1337, 268, 432>;
location W_Parking_SndFloor = <-1406, 210, 296>;
// removed a number of locations for brevity
/*
areas (for patrol purposes)
*/
area area_NW_SniperRoom
{ style = roof; // combine using open, closed, roof
locations = NW_SniperRoom_Rooftop;
// use height cut off instead of radius
};
area area_E_UnderBridge
{ style = open; // combine using open, closed, roof
locations = E_UnderBridge;
radius = 100; // max distance to listed locations
};
// removed a number of areas for brevity
};
// map description is followed by force definitions
The purpose of force definitions is to define the opposing forces (how many, their names,
and their composition (teams and members)). Human players (if participating in the mission) need to be
part of a force (and a team within that force).
Mission objectives typically refer to forces, teams and members. Therefor it's important to provide meaningful
names to those forces, teams and members.
To overcome the (default) ActionQuake2 limit of only two opposing teams, a CGF script defines
as many (opposing) forces as required. Each member of a force will regard members of
other forces as threats (and happily take them out). Thus, you can set up fights between, for example, two, three,
four, etc. opposing forces.
CGF also removes the limitation on a single model or skin per AQ2 team. For each force
member, you're free to pick whatever model and skin combination you want (since the bots determine
between friend and foe on force membership, not on skin or mode). The AQ2 damage model
has been changed to solely look at force membership as well.
This should enable you to construct scripts depicting movie scenes, with various models and skins
for the different 'actors'.
A CGF force is composed of one or more teams. A CGF team is not similar to 'standard' AQ2 team
(that would be the CGF force), but instead the smallest unit to act and fight cohesively according to
the script. (Apologies for the confusion - CGF itself started before AQ2 started, and I never fixed this).
A (CGF force) team is either composed of humans (labeled 'players') or bots (labeled 'npc' from 'non-player character).
Mixed teams are not supported.
The urban_hitman_1.cgf mission forces, for example, are composed as follows:
force |
team |
type |
member (model/skin)
|
Warez Dudes |
Clandestine
Management |
npc |
Sabotage
(male/sabotage) |
Warez Dudes |
High Precision |
npc |
Precision-1
(terror/swatsnipe) |
Warez Dudes |
Protection Copy
|
npc |
Protection-1
(terror/terror) |
Warez Dudes |
ProtectionCopy |
npc |
Protection-2
(terror/terror) |
Warez Dudes |
Protection Copy
|
npc |
Protection-3
(terror/terror) |
Warez Dudes |
Protection Copy
|
npc |
Protection-4
(terror/terror) |
Local Law |
Sidekick |
npc |
Joe Automatic
(male/adidas) |
Local Law |
Hitman |
player |
dah player
(male/reservoir) |
In this case, the mission consists of (solely) two opposing forces. Force 'Warez Dudes' contains
three teams and uses two models and three skins. Force 'Local Law' contains two teams (one for the
npc, and another for the player because they need to be in separate teams). 'Local Law' uses a single model but two
different skins.
The mission hosts seven bots (or npc) and a single player. You would add multiple players (in
the case of co-op multi-player against the bots) in the same Hitman team.
A team hosts up to eight npc-s or players.
To define a (new) force, you need to write the following (after the map definitions,
and before mission objectives):
  force "forcename" {
    // team definitions
  };
To define a (new) team within a force, you need to write the following (between
the '{' and '}' of the corresponding force definition):
    team "teamname" {
      // member definitions
    };
A team defines each members as follows:
- member type (either 'npc' or 'player')
'npc' members will be played by bots. 'player' members might be played
by humans (or left 'vacant' in the case of co-op multi-player with insufficient
players).
Note that you cannot mix npc and player members within a single team.
Though you can define skill settings for a player (this makes changing player to npc and v.v.
easier), a skill setting for a player does not affect the player at all.
Current beta's do not support co-op multi-player against bots.
- name (name valid during the mission only)
This name is used to specify mission objectives (elimination of a certain
player or npc might accomplish the mission).
The name defined in the mission doesn't change your AQ2 netname.
- modelname/skinname (model and skin only valid during mission)
This defines the looks of bots and players during the mission. It doesn't
affect damage (AQ2 settings for teamplay by skin/model are ignored, and
damage is based on force membership).
The model/skin defined in the mission doesn't change your AQ2 defaults.
- 'load out', that is, the weapons, ammo and items to start with
Instead of the (handy) AQ2 weapon/item selection menu, the script defines
the load out for you. (You can use future versions of the bot launcher to
modify the load out without seeing the script itself).
Currently, CGF allows you to severely violate the AQ2 TP restrictions on load out:
if you want to equip bots or players with lots of special weapons, huge amounts
of ammo, and almost all items, you can do so.
However, (in the current beta versions) during the mission, you won't be able to
pick up other special weapons or items if you already have one.
You issue a weapon to a team member as follows (between the '{' and '}' of the member definition):
  weapon "weaponname"; // weaponname should match AQ2 name
A weapon comes with the TP default amount of ammo (ignoring the presence of any bandolier)
You can issue additional ammo as follows (between the '{' and '}' of the member definition):
  ammo "ammoname" = count; // ammoname should match AQ2 name
Depending on the corresponding weapon type, you'll receive ammo as individual pieces (shotguns / knifes / sniper bullets), or as complete clips.
You can dress up the team member with items as follows (between the '{' and '}' of the member definition)
  item "itemname"; // itemname should match AQ2 name
An example of loadout, illustrating all available weapons, ammo and items, looks like:
npc "Sabotage" "male/sabotage"
{
// basic AQ2 weapons + ammo
ammo "Pistol Clip" = 1;
weapon "Dual MK23 Pistols";
ammo "Machinegun Magazine" = 2;
weapon "MP5/10 Submachinegun";
ammo "M4 Clip" = 2;
weapon "M4 Assault Rifle";
ammo "12 Gauge Shells" = 7;
weapon "M3 Super 90 Assault Shotgun";
weapon "Handcannon";
ammo "AP Sniper Ammo" = 20;
weapon "Sniper Rifle";
// items
item "Bandolier";
item "Kevlar Vest";
item "Lasersight";
item "Silencer";
item "Stealth Slippers";
// optional pistol
weapon ""Beretta 92F Pistol";
// optional submachineguns
weapon "AK97 Submachinegun";
weapon "MAC-10 Submachinegun";
weapon "UMP45 Submachinegun";
weapon "UZI Submachinegun";
// optional assault rifles
weapon "AK47 Assault Rifle";
weapon "Tommygun";
// optional assault shotgun
weapon "Ithaca Model37 Assault Shotgun";
// optional cannon
weapon "Old-style Cannon";
// optional sniper rifles
weapon "Barrett M82 Sniper Rifle";
weapon "Dragunov Sniper Rifle";
// skill (from 1..5, ignored for 'player')
skill = 3;
};
Note that all load out definitions shall precede the skill definition. Grenades and knives
are not yet supported.
- skill, range [1.0 = weak ... 5.0 = strong] affects npc team members
The skill setting doesn't affect the player member (skill settings for the player are
allowed but ignored), but for the npc, the skill affects:
- aiming accuracy
- turning speed (higher skill bots turn faster)
- M4 muzzle climb handling (higher skill bots compensate better)
- vision threshold (amount of time needed to acquire a new threat (partially) in the bot's
field of vision; higher skill bots acquire faster)
Note that the skill setting should be last line in the member definition and is mandatory for
npc members.
For example, the map description of the urban_hitman_1.cgf mission looks like:
// force definition is preceded by map description
force "Warez Dudes"
{
team "Clandestine Management"
{
npc "Sabotage" "male/sabotage"
{
ammo "Pistol Clip" = 1;
weapon "Dual MK23 Pistols";
item "Kevlar Vest";
skill = 3;
};
};
team "High Precision"
{
npc "Precision-1" "terror/swatsnipe" // sniper team
{
ammo "AP Sniper Ammo" = 10;
weapon "Sniper Rifle";
skill = 2;
};
};
team "Protection Copy"
{
npc "Protection-1" "terror/terror"
{
ammo "Machinegun Magazine" = 2;
weapon "MP5/10 Submachinegun";
item "Kevlar Vest";
item "Lasersight";
skill = 2;
};
npc "Protection-2" "terror/terror"
{
weapon "M3 Super 90 Assault Shotgun";
ammo "12 Gauge Shells" = 7;
item "Kevlar Vest";
skill = 2;
};
// two members removed part for brevity
};
};
force "Local Law"
{
team "Hitman"
{
player "dah player" "male/reservoir"
{
weapon "M3 Super 90 Assault Shotgun";
ammo "12 Gauge Shells" = 7;
item "Kevlar Vest";
};
};
team "Sidekick"
{
npc "Joe Automatic" "male/adidas"
{
weapon "M4 Assault Rifle";
ammo "M4 Clip" = 2;
skill = 2.5;
};
};
};
// force composition is followed by objective definitions
The mission objectives define when a mission will be completed (and the result of
the mission in terms of messages to the player).
Each objective also defines one or more candidate successor missions.
The next mission is automatically selected (at random) from the list of candidate
missions defined by the first objective achieved.
Using these objectives, one can define a ladder of missions: when the player
fails a mission, he is given an easier mission, when the player completes a mission,
he is given a harder mission.
Likewise, one can define a 'campaign' of chained missions.
The CGF mission objectives replace AQ2 TP timelimits and fraglimits.
An objective is achieved as its condition (or logical combination ('and' / 'or' / 'not') of
conditions are met. The first objective met signals the end of the mission,
displays post-mission feedback messages for the players, and selects the subsequent
mission.
Three different kinds of conditions are provided (in addition to the logical combinations):
- time-out
A time-out condition is met when the mission duration (in seconds) exceeds the
specified time. A time-out condition looks like:
  timeout(duration) // duration in seconds
Because the bots might get stuck (and therefor might take forever to complete
the mission), it's a good thing to always define a time-out objective.
- eliminated
An eliminated condition is met when the specified force, team or team member
has been eliminated for longer than a time-out of 7.5 seconds. (Thus the mission
does not end immediately after eliminating that force, team, or team member).
A team member is eliminated when he is killed (either by opponents, suicide or
other wounds). A team is eliminated when all its members have been eliminated.
A force is eliminated when all its teams have been eliminated.
An eliminated condition looks like:
  eliminated("forceteammembername") // force/team/member name
To be able to specify elimination conditions unambiguously, make sure that force
names, team names and members names are all distinct from each other.
- occupied (not yet supported)
An occupied condition is met when the specified force has been occupying the
specified area for more than the specified time, by:
- having at least one (living) force member being present in the area
during that period
- not having any (living) opposing force member being present in the
area during that period
- not being engaged in that area by opposing forces during that period
An occupied condition looks like:
  occupied("forcename", areaname, duration)
Again, the occupied condition is not yet available in the current beta.
- 'and' combination of two conditions: (condition1 && condition2)
An and condition is met when both its sub conditions are met. The C-style 'and' symbol '&&'
is used, and the syntax looks complicated (due the additional required parenthesis),
but nevertheless it is not that hard to
specify a combination of two objectives:
  (eliminated("force1") && eliminated("force2"))
or a combination of three objectives:
  (eliminated("tm1") && (eliminated("tm2") && timeout(200)))
Note that and-, or- and not- conditions can be mixed.
- 'or' combination of two conditions: (condition1 || condition2)
An or condition is met when one or more of its sub conditions are met. The C-style 'or' symbol '||'
is used, and the syntax looks complicated (due the additional required parenthesis),
but nevertheless it is not that hard to
specify a combination of two objectives:
  (eliminated("force1") || eliminated("force2"))
or a combination of three objectives:
  (eliminated("tm1") || (eliminated("tm2") || timeout(200)))
Note that and-, or- and not- conditions can be mixed.
- 'not' inversion of another conditions: !(condition1)
An not condition is met as long as its sub conditions is not met. The C-style 'not' symbol '!'
is used, and the syntax requires a paier of parenthesis.
The not condition is mainly useful in combination with other conditions, for example, when you want
to inform the player about the fact that team1 was eliminated but not team2.
but nevertheless it is not that hard to
specify a combination of two objectives:
  (eliminated("team1") && !(eliminated("team2")))
Note (as shown above) that and-, or- and not-conditions can be mixed.
A CGF objective is composed as follows:
- objective type
There are two types of objectives ('success' and 'failure'). The main difference between
the types objectives is the sound file played upon achieving the objective
(completing respectively failing the mission), and the default message presented
to players among the other forces.
Note that it only makes sense to define objectives for forces that contain a player.
- force to which objective applies
The objective applies to a specific force (and typically one containing a player). This
force is specified by the force name.
- message to be displayed when objective is met
When objective is met (more precisely: is the first objective to be met),
it's message will appear in the post-mission texts for the players. Typically,
this text expresses encouragement (for success objectives) or disappointment
(for failure objectives), and some explanation about the specific objective
achieve: "you successfully took out Sabotage", or "too slow again!".
Make sure that the message fits on 512x384 screens -
not every gamer has high end hardware.
You can direct the text over multiple lines by using the '\n'
characters where you want a new line.
The description should start with a character from [a-zA-Z0-9_.,'() /\-\\!:?] and
continue with characters from [a-zA-Z0-9_.,'() /\-\\!:?+=#@\^|%&]
- objective condition(see above)
This condition defines when the objective is met. The conditions have been
explained in detail above.
- list of successor missions (in case objective is met)
This list contains candidate successor missions (actually, the file names of the
script, including the extension .cgf). When the objective is the first objective
to be met, it will determine the next mission. This next mission is selected
at random from the list of successor mission files.
The list may include the mission itself. The list may include missions for other
maps (but changing maps typically causes a longer delay). A mission file might
be included more than once to increase its chances to be selected.
The list looks like this:
  -> {"urban_hitman_1.cgf", "urban_hitman_2.cgf"};
Note: it is possible to launch a .cfg configuration script instead of a (.cgf) mission.
This allows mission designers to play cut-scenes (demo's) or sound, or perform other
required actions.
For example, the map description of the urban_hitman_1.cgf mission looks like:
// objective definition is preceded by force definition
objectives
{
failure
( "Local Law",
"\nYOU are supposed to be THE hitman!"
)
= eliminated("Local Law")
-> {"urban_hitman_1.cgf", "urban_hitman_2.cgf",
"urban_hitman_3.cgf", "urban_hitman_4.cgf"
};
success
( "Local Law",
"\nwell done!\nyou sure are da HITMAN!"
)
= eliminated("Sabotage")
-> {"urban_hitman_1.cgf", "urban_hitman_2.cgf",
"urban_hitman_3.cgf", "urban_hitman_4.cgf"
};
failure
( "Local Law",
"\nyawn...\nthis is taking way too long"
)
= timeout(300)
-> {"urban_hitman_1.cgf", "urban_hitman_2.cgf",
"urban_hitman_3.cgf", "urban_hitman_4.cgf"
};
};
// objective definitions are followed by team plans
Note the current beta's contain a bug that allows a later completed objective to redefine
the post-mission message.
The team plans provide instructions and behavior for the forces' teams. The team plans
heavily use locations and areas defined in the map section of the script.
Being probably hardest-to-learn part of CGF scripting, I'll provide some more background
info.
High level scripting
The team plans thus provide the 'real' scripted behavior of CGF. Note that the type
of scripting is totally different from Half-Life, Sin, Unreal or SpecOps scripting: here you
issue a number of instructions to teams - not to individual bots or entities.
(Probably the planning stage of Rainbow Six comes close, but though the RB6 operatives
execute the plan with nifty close quarter formations, they seem unable to divert from
the plan in unexpected situations).
As a result, you'll gain the advantage of being able to change team sizes at will:
the script doesn't deal with the individual members, instead the CGF AI does so.
Another advantage is that you now can instruct teams to do fancy procedures such
as bounding overwatch pull back (with suppression fire). Those maneuvers would be almost impossible
to write in the above mentioned scripting systems (with the exception of RB6).
This level of scripting offers me the advantage of improving the AI and team tactics
independently from the scripts. Older scripts will benefit automatically from
AI improvements.
The last advantage is that because the CGF AI deals (dynamically) with combat situations
and selects appropriate team tactics, the combat and missions outcome become less
predictable. The combat is only "sketched", not scripted in detail. Mission scripts
gain replayability.
The main disadvantage is that you lose control over individual members. You won't be
able to do nifty Half-Life style 'tentacle grabs scientist' or 'grunt opens door,
throws detpack, closes door, and ignites detpack' animations (actually, in the last
case, the player is able to kill the grunt early but still the 'rigid script' has him
close the door and ignite the detpack :) ).
Scripting 'Commands'
A team's plan consists of a rather simple sequence of commands, optionally with some
commands how to prepare for exceptional cases (contact with enemy forces).
The plan's commands are issued to the team in sequence. Issuing an instruction
takes a Quake2 server frame, that is, 0.1 second. However, it might take the team
much longer to execute an instruction.
To deal with duration differences between issuing a command and having the team
execute it, a lot of wait command are used. That prevents the team from receiving
the new command before it completes the current command.
For example (the commands themselves will be explained later in detail), in the
following plan (for Sabotage, the target in the hitman mission), almost half the
commands are a kind of 'wait' command:
plan "Clandestine Management"
{
insert at SE_HighBuildingSniperRoom using "column";
wait until ready;
spread towards SE_HighBuildingRoofTopCenter within 1.5;
wait until ready;
// phase
phase "wait for business";
on (contact)
{
abort;
defend towards SE_HighBuildingRoofTopCenter
from SE_HighBuildingSniperRoom;
wait until ready;
continue with phase "wait for business";
};
wait forever;
};
Even the insert command (putting the Clandestine Management team, consisting solely of Sabotage,
into the game) takes some time (especially for larger teams). It is essential to
wait until the team is ready (has completed insertion) before issuing the next command.
There are a few exceptions to the sequential issuing of commands to a team. You can
define specify 'what-to-do-when-something-happens' behavior early in the plan. When
(and only when) that something happens, the team will be issued those special commands
instead.
In the example above, as soon as Sabotage is in position, he is told to react specially
to any enemy contact. The 'special' commands (following 'on (contact)' and between
accolades '{' and '}' are not issued immediately, but only when Sabotage gets in touch
with his attackers.
Instead, after Sabotage learns how to deal with contacts, he is not executing that
behavior but instead receives the next command: he's going to wait forever (until
something exceptional happens).
When something special happens, it is important not to wait for the team to complete
the current command. Instead, it often is an issue of life and death to abort the
current activity and instead act upon the threat. Therefor, the special 'on contact'
sub plan issues an 'abort' command immediately. Upon receiving an abort command,
Sabotage will stop executing his current command, and will be ready for new commands:
some real defensive action.
A final exception to sequential issuing (and execution) of commands is are phases
with a plan and the 'continue' command. When a team should repeatedly receive a
series of commands, or when you want the team to continue again with normal activity
(after dealing with exceptions), you can define a phase in the plan (and label it with a
useful name), and instruct the team to continue with that phase again.
In the example above, Sabotage is issued to continue with phase "wait for business"
when he has completed defending himself. When continuing with that phase, Sabotage
is instructed to deal with contact again, and then waits again 'forever' for 'business'.
Team Behavior
Because a team plan contains commands for the team, it's important to fully understand
how the team in general behaves.
The team members act like very well trained pro's. That means, you need not expect
individual team members to become afraid and stop acting as a reliable team mate.
They've learned to follow orders, and are prepared to risk their life in combat
to have their team prevail. (The team, however, might judge it wiser to pull back
during a combat situation, but, again, individual members won't pullback on their
own).
Because of their training, they don't need a specific leader to guide them. Any
member is as capable of directing his fellow team members when called for.
Depending on member positions, the most appropriate member takes the lead
to execute a command.
You can think of the team members as either:
- being a group of LAN players within the same room, excitedly shouting info
(threats, bandaging, etc.) towards their teammates, or
- being a group of well trained operatives expensively equipped with earplugs and
throat microphones, thus in constant and rapid communication with each other.
They will rapidly communicate the fact that a threat is spotted, and act upon that
while taking into account their positions, spread, tactical advantages, etc.
There's no problem when a team become eliminated: all plan commands for that
team are ignored.
Basic Plan Types
The 'Clandestine Management' / Sabotage plan listed above is actually a quite
detailed and complex plan. Two simpler categories of team plans exist. Thus,
there are three kinds of plans:
- player team plans
This simple plan does just one thing: it inserts the team (containing one
or more players) at a specific location. Such a plan looks like:
plan "Hitman" // player
{
insert at NW_FarCornerOnsRoofTop using "column";
wait until ready;
};
That doesn't look to hard to write, does it?
- patrol / snipe team plans
Another category of simple plan is for teams that 'just' patrol or snipe.
Because the patrol and snipe commands themselves contain permission to
engage at will, there is no need to define any exceptional behavior.
And because the team can spend the complete mission patrolling or sniping,
there's no need for additional commands. Thus, such a plan looks like:
plan "Protection Copy" // team of patrolling bots
{
insert at S_SouthCenterUnderneathRamp using "column";
wait until ready;
patrol (area_S_SouthCenterUnderneathRamp,
area_W_Parking_SndFloor, area_E_AtBridge);
};
Only three commands for many minutes of action!
- detailed plans
Detailed plans typically insert the team, define how to deal with exceptions,
and then instruct the team to perform normal business (wait, move around).
In dealing with exceptions, the script writer has a bit more control over
the team's behavior: attack, defend, advance or pullback upon contact.
The above plan is a good example of a detailed plan. Of course, you might
be able to construct even more detailed and complex plan. However, there
is the proverb that "no plan survives first contact".
Different kinds of commands
To construct scripts, you have different kinds of commands at your disposal. These
commands are discussed in more detail below this list:
- insert command (insert)
put team into the map
- auto-engage commands (patrol, snipe)
have team autonomously scan, (move) and engage
- combat maneuver commands (attack, defend, advance, pullback)
have team explicitly fight or maneuver
- non-combat maneuver commands (moveup, spread, assemble)
have team move to or move into position
- communication commands (signal code, radio)
issue a go-code or an explicit radio (chat) message
- exception behavior (on contact, on code, abort)
define how to handle upon exceptional situations
- flow control (phase, continue with phase)
jump to different phase of plan
- wait commands (wait until ready, wait, wait forever, wait until code)
delay next command for some time
insert command
Purpose:
Insert team into map. The format listed after 'using' denotes the formation - column
should be used.
Example:
  insert at S_SouthCenterUnderneathRamp using "column";
Problems and Hints:
Team need not necessarily be inserted at the start of the mission.
Without being inserted a team cannot receive any instructions (except for wait).
Inserting a team too large to fit the selected location (a small room) crashes CGF.
patrol command
Purpose:
Have team patrol all or a selected list of areas on the map. The team will patrol
continuously for the remainder of the mission (unless given an 'abort' command).
In their patrol, the team will select visit all (specified) areas in a efficient but
varying way. The team will take care to visit the areas in an order that allows them
to visit many areas in a limited time. However, the order is partially random.
To patrol, the team will spread out and move along walls/thru dark areas (where
possible) from area to area. Occasionally, the point man will wait for the team to assemble again,
typically in a cover position. The tail man will check his 'six'.
The patrolling team is prepared to engage threats immediately (and suspend patrolling for
the time of the engagement). Various team tactics are at the team's disposal. After the engagement (because the team took out the
threats, or lost sight of the tracks), the patrol will tend to the visit the engagement
location during their subsequent patrolling activity.
Examples:
  patrol (area_S_SouthCenterUnderneathRamp, area_E_AtBridge);
  patrol;
Problems and Hints:
Script should define at least two areas.
Explicit area list should contain at least two areas.
Team should be able to reach each area to be patrolled (bad route files crash CGF here). If
the map has areas in unreachable sections, you need to specify explicitly which areas to
patrol.
In current version, the patrolling team will tend to engage even in unfavorable
cases (for example, being outgunned 5 to 1). This will change in future versions.
snipe command
Purpose:
Have team snipe from a specific location towards another location. AI will determine
the snipers and spotters in the team (the team will always have at least as many snipers
as spotters). Snipers engage at will and change tactics to normal combat when faced
with nearby threats. Snipers typically move among a couple of useful (AI determined)
sniper positions and cover positions. Spotters spot, briefly engage threats (and communicate threat
position to sniper), and hide.
Examples:
  snipe from SE_HighBuildingRoofTopEdge
        towards S_SouthCenterBuildingTop;
Problems and Hints:
Sniper 'from' location should provide line of sight to 'towards' location.
Currently, snipe AI doesn't deal with nearby threats (by changing tactics).
Spotter likely to move around to silly locations.
attack command
Purpose:
Have team fight offensively against any threats. Team will engage, advance and
pursuit threats when applicable (various team tactics are at the team's disposal).
Team won't pullback. After losing contact, the team will regroup
(perform the required (p)reloading and bandaging) and then is ready for new commands.
Examples:
  attack towards SE_HighBuildingRoofTopCenter;
Problems and Hints:
The 'towards' location is often ignored.
Currently, attack does not execute bounding overwatch advance automatically.
defend command
Purpose:
Have team fight defensively against any threats. Team will engage threats, and pullback
from threats when applicable (various team tactics are at the team's disposal).
Team won't pursuit threats. After losing contact, the team will regroup
(perform the required (p)reloading and bandaging) and then is ready for new commands.
Examples:
  defend towards SE_HighBuildingRoofTopCenter
          from SE_HighBuildingSniperRoom;
Problems and Hints:
If the team pulls back, it will be towards the 'from' location.
Currently, defend does not change into pullback automatically.
advance command
Purpose:
Have team fight move up to a location employing bounding overwatch. In
the presence of threats, front most members will adjust their path to
benefit from cover from enemy fire, whereas tailing members will provide
suppression fire.
When the team meets threats too close to their position and path,
the team will suspend their maneuver to fully engage the targets, and
continue the maneuver afterwards.
Upon arriving at the destination, the team will assemble in formation (the
one specified after 'using' - typically "column") and
then is ready for new commands.
Examples:
  advance towards parkingtopsniper using "column";
Problems and Hints:
CGF crashes when the destination location does not provide sufficient space
to assemble in (in the specified formation).
The AI still has problems due to members blocking each other. The chances
of blocking increase for larger team sizes. Basically, advance works best
for teams of 3 to 5 members.
The current beta contains a silly bug that prevents members from discovering
decent stop positions near edges.
pullback command
Purpose:
Have team fight pullback to a location employing bounding overwatch. In
the presence of threats, tailing members will provide suppression / cover
fire.
Upon arriving at the destination, the team will assemble in formation (the
one specified after 'using' - typically "column") and
then is ready for new commands.
Examples:
  pullback to parkingtopsniper using "column";
Problems and Hints:
Pullback is a command typically used in an 'on (contact)' situation,
when you are sure about the current location of the team (they are not
close to the destination).
CGF crashes when the destination location does not provide sufficient space
to assemble in (in the specified formation).
The current beta contains a silly bug that prevents members from discovering
decent stop positions near edges.
moveup command
Purpose:
Have team move up to a location in a fast, no frills way. Upon arrival,
the team assembles in the specified formation (typically "column"). The
team then is ready for new commands.
Note that the team doesn't respond to threats unless you specify so in
the script (using an 'on (contact)').
Examples:
  moveup to parkingtopsniper using "column";
Problems and Hints:
CGF crashes when the destination location does not provide sufficient space
to assemble in (in the specified formation).
spread command
Purpose:
Have team members take up dispersed defensive positions towards a
specified location. Typically used immediately after a team has
been inserted or has assembled. As soon as all members have taken
up the defensive positions, the team is ready for new commands.
The 'within' duration provides an limit to the 'spread': it specifies
the distance (in travel time) from the original location in which
is searched for good defensive locations. Small durations of two
seconds or less work best.
Note that the team doesn't respond to threats unless you specify so in
the script (using an 'on (contact)').
Examples (Sabotage in urban_hitman_8.cgf):
  insert at C_RoomCorner using "column";
  wait until ready;
  spread towards C_RoomDoorEntrance within 0.5;
  // ... further on-contact behavior etc.
Problems and Hints:
Current beta doesn't provide completely predictable results.
Doesn't work well for large durations.
assemble command
Purpose:
Have team members regroup in specified formation at specified
location. Team members move to the formation in a fast, no frills, way.
Assemble won't be used that often; it makes solely sense to reassemble
a team when you need to have the complete team take up defensive
positions (using the 'spread' command).
Note that the team doesn't respond to threats unless you specify so in
the script (using an 'on (contact)').
Examples:
  assemble at parkingtopsniper using "column";
Problems and Hints:
CGF crashes when the destination location does not provide sufficient space
to assemble in (in the specified formation).
radio command
Purpose:
Have team send radio (actually: teamplay chat) message to all the members (actually: players) in
the team's force (thus the message will reach members in other teams
as well).
Examples:
  radio "a message to all force members";
Problems and Hints:
The radio command is special in that it can be executed while the
team is execution another command. For example, you can have the
team send radio commands while it is executing a defend command.
signal code command
Purpose:
Have team issue some 'go' code to other teams in the force (whether
they are waiting for that code or not). This can be used to
synchronize actions (often: attacks) between multiple teams in
a force.
Examples:
  signal code "Red";
Problems and Hints:
From CGF 0.90 onwards, the player will be able to issue go-codes
as well (a command bound to one of his keys). To reduce the amount
of key binding necessary, it makes sense to restrict the actual
codes used to the following: 'Red', 'Blue', 'Green' and 'Yellow'.
See the 'on (code)' and 'wait for code' commands on how to respond to
signalled codes.
on (contact) { ... } command
Purpose:
Instruct the team in advance on how to deal with the exceptional
'enemy contact' situation. After being instructed, the team can
continue performing other activities. As soon as the 'contact'
is made, the team will start executing the 'on contact' specified
commands.
Note that as soon as the team has executed the 'on contact' instructions,
the team needs to be instructed again with the same or other instructions
to deal with contact. You'll typically define a phase before the
'on contact' behavior, and instruct the team to continue with that
phase again (see the example below).
Note also that you typically want the team to respond immediately
to contact, no matter what the team was doing at the time of contact.
Therefor, you need to have the team abort it's current activity. You
use the 'abort' command for that (see the example).
Each time you define 'on contact' behavior, the previous definition
(if any) is forgotten.
Examples (from Sabotage in urban_hitman_1.cgf):
spread towards SE_HighBuildingRoofTopCenter within 1.5;
wait until ready;
// phase
phase "wait for business";
on (contact)
{
abort;
defend towards SE_HighBuildingRoofTopCenter
from SE_HighBuildingSniperRoom;
wait until ready;
// make sure to reinstruct team before
// finishing dealing with the exception
continue with phase "wait for business";
};
wait forever; // wait for contact
Problems and Hints:
There is no need to define on contact behavior for teams that
patrol or snipe (though you can do so).
on (code "Code") { ... } command
Purpose:
Instruct the team in advance on how to act when upon receiving
the specified go-code.
After being instructed, the team can continue performing other activities.
As soon as the go-code is received is made, the team will start executing the
'on code' specified commands.
Typically, a team is only issued a go-code once, and subsequently, there
is little need to re-instruct the team.
The code itself is issued by another team using the 'signal code' command.
Examples:
  on (code "Red")
    {
      abort;
      // ... activity ...
    };
Problems and Hints:
If the team won't execute any other commands but instead is solely waiting
for the go code, it's better to just use the 'wait for code' command.
abort command
Purpose:
Have team abort any current execution of a command (if any), and
be ready for new commands. Typically used to respond to exceptions
(enemy contact, reception of a go-code).
Examples:
  on (contact)
    {
      abort; // stop executing current command
      // ... new commands ...
    };
Problems and Hints:
There is no use in issueing the abort command unless dealing with
on contact / on code exceptions.
phase (definition) command
Purpose:
Define a specific phase in the team's plan, so they can (at some
time) continue with that phase (again).
A team aborts sequential execution of the commands upon receiving
a 'continue with phase' command. The team will then start executing
the commands listed immediately below the specified phase.
A phase command is not really executed. A team can continue with
a phase without having already 'executed' that phase definition.
Examples (from Sabotage in urban_hitman_1.cgf):
spread towards SE_HighBuildingRoofTopCenter within 1.5;
wait until ready;
// phase
phase "wait for business";
on (contact)
{
abort;
// combat actions ...
// make sure to reinstruct team before
// finishing dealing with the exception
continue with phase "wait for business";
};
wait forever; // wait for contact
Problems and Hints:
It helps to provide useful names for the phase.
continue with phase command
Purpose:
Instruct the team to continue with the commands immediately following
the phase definition as the next commands to execute.
See the 'phase' command above for more info.
wait command
Purpose:
Force a specific delay before issueing the next command. You might
even use the delay command to postpone insertion of the team.
The delay is specified in seconds, with 0.1 second precision.
Examples:
  wait 10;
  wait 0.5;
Problems and Hints:
Use 'wait forever' if you want to wait for a long time.
wait forever command
Purpose:
Force a 'infinite' delay before issueing the next command (that
command won't be issued, actually).
You typically use a wait forever when you want to have a team
'camp' after having positioned and instructed that team to deal
with contact.
Examples:
  wait forever;
Problems and Hints:
Nope. (Actual waiting time is about 20,000 seconds :) ).
wait until ready command
Purpose:
Delay issueing the next command to the team until the team
has completed its current command.
Because the issueing of a command takes 0.1 second and the
execution of a command takes much longer (in general), you
want to wait issueing another command.
Examples (Sabotage in urban_hitman_8.cgf):
  insert at C_RoomCorner using "column";
  wait until ready; // wait until team has been fully inserted
  spread towards C_RoomDoorEntrance within 0.5;
Problems and Hints:
Not issueing the appropriate wait until ready is likely to crash CGF.
wait until code command
Purpose:
Delay issueing the next command to the team until the team
has received a specified go-code.
The go-code will be issued by another team using the signal code
command.
If you need the team to execute other commands while 'waiting'
for the go-code, use the on (code) command.
Examples:
  insert at C_RoomCorner using "column";
  wait until ready; // wait until team has been fully inserted
  // upon receiving code Red, start advance
  wait until code("Red");
  advance towards parkingtopsniper using "column";
Problems and Hints:
See signal code command for more info.
When writing or editing missions, a important part of the time is spent on
defining locations, and getting the appropriate coordinates for them (see here).
To speed up mission development, CGF 0.81 and later versions allow you to define,
view and add locations in-game for both new and existing missions.
You can do so as follows:
- load up AQ2/CGF with a map for which a terrain file (<mapname>.srp, in action/terrain)
is available, but don't run/load a mission!
- (only if you want to add locations to an existing mission)
load the mission (for example, actcity2_survival1.cgf) by typing on the console:
mission read actcity2_survival1.cgf
- just walk to the spot where you need a location, and type (on the console):
location alocationname
And repeat! You can pick any locationname you want (as long as don't contain exotic symbols),
but please make sure the name is unique.
- you can display the locations already created in the map as follows:
terrain show locations
Each location will be highlighted by a stack of three blue balls. These blue balls will be present for
some 120 seconds.
- when you're done adding locations, you can write the modified mission (if you've read a mission),
or mission skeleton (if you started from scratch) to a file (for example, actcity2_newmission.cgf)
in the action/missions directory:
mission write actcity2_newmission.cgf
After saving the mission, you can edit the mission file with your favorite mission editor.