Enemies and AI

With this tutorial you are going to learn how to script the AI for enemies and other NPC's. That includes messages to change the behavior and the perceiving properties of the enemies, and the messages to make them move around in the level. We are going to learn how to calculate AI network to the levels, and we are going to make the enemies switch weapons. Also deathcams and custom animations are going to be covered. You can download the example level from here: AI_scripting.zip

The Statemachine States

One of the basic blocks of the AI scripting in Max Payne is so called statemachine states. They are different states for enemies that define their behavior. The usable statemachine states that define the combat behavior are:

MercCombat - General combat statemachine. The enemy will follow player and perform dodges spontaneously during combat.

MercCombatDefensive - Same as above, but the enemy won't follow the player very far.

MobsterCombat - The same as MercCombat but the enemy won't perform dodges.

MobsterCombatDefensive - The same as MercCombatDefensive, but the enemy won't perform dodges.

MeleeCombat - Combat statemachine for enemies who are using a melee weapon.

CrouchAndShoot - Crouches and shoots. If it looses player from sight, it will start following.

CrouchAndShootStatic - Crouches and shoots. Won't follow player.

StandAndShoot - Stands and shoots. Follows the player if the sight is lost.

StandAndShootStatic - Stands and shoots. Won't follow the player.

WalkAndShoot - Walks towards the player while shooting. Will start running if loses the sight to the player.

CrouchWaitAndShoot - Was used for statemachine testing purposes. Redundant.

CrouchWaitAndShootStatic - Was used for statemachine testing purposes. Redundant.

RunWalkAndShoot - Runs towards the player and once close enough, starts walking and shooting. Redundant.

WalkStopAndShoot - Same as WalkAndShoot but won't shoot while walking. Redundant.

You can get pretty far with the statemachines above, but every now and then you want to do some more specific moves, and they are handled with statemachines as well:

SingleDodgeLeft, -Right, -Forward, -Backward - Performs a dodge and returns to the previous statemachine in the stack.

SingleShootDodgeLeft, -Right, -Forward, -Backward - Performs a shootdodge and returns to the previous statemachine in the stack.

SingleShootAccurately - Shoots once (relatively) accurately, and then returns to the previous statemachine. Quite redundant to be honest, maybe useful in cinematics.

With the statemachines above you can already do some simple combat. Load up the level as it was left after the FSM & DO tutorial, or just download the example level of that tutorial.

Then let's place an enemy into the level. As you might remember, you can place entities to the grid by pressing N in F3 mode. Select  Enemy from the drop-down dialog. The properties of the newly created enemy will pop up. Rename it to e1. You can now also change the type of the enemy from the Entity-tab if you want to. You can also rotate the enemy into the desired orientation by selecting the enemy, pressing Y in F5 mode and moving the mouse. But don't forget that you have to be in pivot rotation mode (A to toggle) in order to rotate any entity.

Now as you've got an enemy in the level, it doesn't do anything by default. It's just standing there in idle statemachine state (more about idle states in a moment). You can open the FSM dialog of the enemy (4 in F5 mode) and change its statemachine state at its Startup into any of the states above with a message called C_SetStateMachine.

Now you can try and export the level and see how it works, although in order for the enemy to be able to follow the player through rooms and around obstacles correctly, we are going to have to calculate the ai-network for the level. You already know how to add your levels to the levels.txt correctly, so add a new block for this level of ours, but make sure it will have the flag EnableAI set as true. This will tell the engine to look for AI network for this level.

The actual calculation of the network happens in the game. Once you've exported the level .LDB, and you have the levels.txt all sorted out, launch the game and type the following to the console:

MaxPayne_GameMode->GM_InitAndCalcAI( my_AI_level );
X_ModeSwitch->S_ModeSwitch(Game);

You will need to do this only once if you don't touch the geometry of your level, as the game will save the AI net to the same directory as where your .LDB file is, with .AI-file extension.

Once the level has loaded up and your PC has done all the crunching, you can view the AI network by pressing F7. Or view the AI nodes by pressing F8 (Of course, only in developer mode with developer keys enabled).

You can try to combat against the enemy now. Notice how it can navigate from one room to another without problems.

Perceiving properties

Note how the enemy can hear you if you start shooting even if it can't see you. We can change the perceiving properties of the enemies by using the following statemachine states:

Idle - This is the default statemachine. The enemy can hear and see the player.

IdleUntilEnemySeen - In this statemachine the enemy can't hear the player at all. Very useful for setting up ambushes.

Nonreactive - In this statemachine the enemy won't perceive the player at all.

To get an enemy into any of these statemachines, use once again the C_SetStateMachine -message. And when the enemy perceives the player, it actually means it will trigger its OnActivate event, thus you can add whatever messages to the OnActivate -message tab of the enemy to make various things happen. Most commonly to make the enemy to react to the player somehow (Switch into a combat statemachine). In all the combat statemachines that were listed before, the enemies have the same hearing and seeing properties as in Idle statemachine, thus they will also trigger the OnActivate event once the player is perceived. 

Notice though that the enemy won't re-trigger its OnActivate event when perceiving player multiple times for as long as it has not forgotten the player (30 seconds from the last sight), or as long as its statemachine hasn't been re-switched into Idle, Idleuntilenemyseen or Nonreactive. 

Some examples, let's say you want the enemy not to be able to hear player. You do this by adding the following to the Startup tab:

This->C_SetStateMachine( idleuntilenemyseen);

And to the OnActivate tab:

This->C_SetStateMachine( mobstercombat );

You can also stack the statemachines, so if you want an enemy to do a shootdodge backwards when it perceives you, and then start chasing you, you can do it by sending these messages at OnActivate:

this->C_SetStateMachine(mobstercombat);
this->C_SetStateMachine(singleshootdodgebackward);

What this does is that it first sets the statemachine into MobsterCombat, and immediately after that into SingleShootDodgeBackward. The statemachines are stacked, so now the enemy has these two statesmachines stacked on top of the IdleUntilEnemySeen. The statemachines are stacked in the order of execution, so the enemy will in practice go into SingleShootDodgeBackward statemachine first (which ends up on top of the stack), and once it's been executed, it will revert to the previous statemachine in the stack, which is MobsterCombat. Play around with it. You can stack multiple dodges and shootdodges in there if you wish.

Here's still some more statemachines you should find useful:

StandAndIdle - Same as idle but faces the player at all times, and won't cause the re-trigger of OnActivate event.

Crouch - Same as idle, but crouching, and won't cause the re-trigger of OnActivate event.

Delay500ms - A statemachine that forces the enemy into "idle" state for half a second and then reverts to the previous statemachine in the stack.

Delay1s - Same as above but lasts one second.

Delay2s - Same as above but lasts two seconds.

More advanced actions

Let's next try and add a little animation and some sound for the enemy once it spots you. To the StartUp tab, put these messages:

This->C_SetStateMachine( idleuntilenemyseen );
This->C_RemoveAllWeapons( Deserteagle );
This->C_PickupWeapon( Pumpshotgun );
This->C_PickupAmmo( Pumpshotgun, 3);

To the OnActivate tab replace the existing messages with the following:

This->C_SetStateMachine( mobstercombat );
This->C_SetStateMachine( delay1s );
This->C_SetIdle(1, false);
This->A_Play3DSound(enemy, mobster_alert,head);

And to its OnDeath tab:

This->A_StopAll3DSounds(Head);
This->CAM_AnimateParented( Death_03 );

Now you can export the level and try it out. The enemy will initially have a pumpshotgun at hand, and when it sees the player, it will shout an alert, mess with its weapon for a moment, and then start to shoot at you. Once it has shot you three times with a pumpshotgun, it will switch to deserteagle.

Here's some explanations about the new messages in Startup tab:

C_RemoveAllWeapons( deserteagle ); - Removes all the weapons from the character, but gives it the weapon defined in the parameters, plus infinite ammo for that weapon. With this message, the weapon will just suddenly appear into the hand of the enemy, so it's mostly useful just for the startup of the level. The valid parameters are baseballbat, beretta, berettadual, deserteagle, grenade, ingram, ingramdual, jackhammer, m79, molotov, mp5, pumpshotgun, sawedshotgun, sniper, leadpipe and empty. See data\database\weapons\.

C_PickupWeapon( Pumpshotgun ); - Gives the enemy a pumpshotgun in addition to the desert eagle. With this message, the enemy will take the weapon out of his pocket with an animation, so it can be sent to an enemy in the middle of a combat situation if that is desired. Or you can for example have an enemy with empty weapon at startup, and send this message to the enemy as it spots the player. The enemy doesn't get any ammo with this message, and it won't use the weapon it has if it doesn't have any ammo. The valid parameters are the same as above.

C_PickupAmmo( Pumpshotgun, 3 ); - Gives the enemy three shells for the pumpshotgun. Once again the valid  weapon names are the same as above.

Whenever an enemy has multiple weapons, it will always use the most powerful ones first, and if it depletes all the ammo, switch to the second best weapon, etc. The priority list can be seen from data\database\weaponid.h

What happens in the OnActivate event is that the very first message sets the statemachine into MobsterCombat, and the second message immediately stacks another statemachine (Delay1s) up on top of it, thus causing the enemy to idle for 1 additional second after perceiving the player. This additional second is in there just so that we can animate the enemy at idle instead of making it start the combat immediately.

With C_SetIdle we change the idle animation to be "1" instead of the default "0". The idle animation "1" is a reaction animation to the player for all common mobsters. The "false" flag in there means that the animation won't be looping. If we set it to looping, the enemy would restart doing this animation every time it went idle. As it's false, the enemy will execute the animation only once, and after that it's idle animation will be whatever it previously was.

There's basically 5 regular slots for idle animations per character, plus 5 more slots to be used if the character is wounded. You can make use for all 10 slots in MaxED by using C_SetIdle and C_SetWoundedAnimations messages. The default animations are:

Slot 0 - The default standing animation

Slot 1 - Reaction to Max

Slot 2 - Aim (handy for ambushes)

Slot 3 - Get up from sitting

Slot 4 - Using something

To change the animation between any of these you do it with C_Setdle, as you noticed. In the parameters you define the actual slot, and whether you want the animation to loop or not. Note that some animations are set to not loop within themselves.

In order to use the additional 5 slots, you set the slot itself with C_SetIdle, and then force the enemy into wounded state with C_SetWoundedAnimations( true );. Don't forget to disable the wounded animations when you want the enemy to move though.

Slot 0 wounded - The wounded standing animation

Slot 1 wounded - Custom action, varies between skins. Usually something enemies do while idling bored

Slot 2 wounded - Aiming while crouched

Slot 3 wounded - Sitting

Slot 4 wounded - Talking

All the custom idle animations are defined in the skeleton and skin scripts.

A_Play3DSound obviously triggers an alert sound from the enemy's head. Since we are playing the sound through a character, we need to define the bone. The most obvious bone in this case to be used is head. As you know, you can see all the sounds you have at your disposal from the sound script files, but here's a brief list of the most useful enemy sounds:

junkie_alert - Junkie shouting

junkie_noise - Junkie making noise

junkie_talk - Junkie having a conversation with himself

killersuit_alert - Killersuit spotting Max

whistle - Enemy whistling

whistle_quiet - Same, but bit more quiet

mobster_alert - A regular mobster spotting Max

mobster_group_alert - A mobster group spotting Max

mobster_alert_known - A mobster who knows Max spotting him

mobster_group_alert_known - A mobster group who knows Max spotting him

mobster_noise - Mobsters making noise, used to warn the player

mobster_group_noise - A mobster group making noise

merc_alert - A merc getting alerted

merc_group_alert - A merc group getting alerted

merc_noise - Merc making noise

merc_report - Essentially the same as above

police_alert - Police getting alerted

police_noise - Police making noise

The messages in the OnDeath tab:

A_StopAll3DSounds - Stops the enemy playing any sounds, basically ensures that the enemy won't shout its alert in case player kills him immediately with a headshot. 

This->CAM_AnimateParented( Death_03 ); - Runs a deathcam for the enemy. Basically you can use Death_01, Death_02 or Death_03 here The camera paths are defined in data\database\camerapaths\camerapaths.txt.

Movement messages

There are four messages you can use to command the enemies move around the levels:

C_GoTo - Moves to a waypoint.

C_GoToAndShoot  - Moves to a waypoint while shooting at the targetcharacter (usually the player).

C_GoToPlayer - Moves towards the player.

C_Patrol  - Patrols between a maximum of three waypoints.

Let's try and make the enemy in our test level to run to a waypoint when the glass window in there breaks. First add a waypoint entity somewhere into the level. There doesn't need to be a direct line between the enemy and the waypoint, for as long as there is a valid AI network in the level, the enemy can find its way to the waypoint no matter where it is (As long as it is also possible for the enemy to move to that location). Name the waypoint as w1.

To the DO_BulletCollides tab of the breaking glass, add:

::room2::e1->C_GoToAndShoot(::startroom::w1, 1);

What this does is that it commands the enemy to move to the defined wayppoint (while shooting) with a speed of 1. That is the maximum speed and means running, the other option is walking, which would be 0.5. Pay attention to the object hierarchies in this message though, as they obviously might not be the same in your level.

Once it reaches the waypoint it will basically start behaving according to its statemachine. In this case, since the enemy would be in Mobstercombat, it will start shooting and chasing the player. If it was for example in CrouchAndShootStatic, it would start crouching and shooting, and wouldn't chase the player. Notice that enemies do obey the movement messages even if their statemachine states that they should be static.

Now try out changing the C_GoToAndShoot message into:

::room2::e1->C_GoTo(::startroom::w1, 1);

The parameters are the same. One major difference with C_GoTo over C_GoToAndShoot is that if the enemy perceives the player while moving, it will immediately start acting according to its statemachine. For example, if in combat statemachine, it will stop moving, forget about the waypoint and start shooting.

So if you now shoot the glass before the enemy has noticed you, it will start moving to the waypoint until it sees you. If it won't see you, it will reach the waypoint and stand there, adopting the orientation from the waypoint. If you howerever shoot the glass while the enemy is already shooting at you, it won't basically do seemingly nothing (The enemy starts to move, but at the same time perceives  you immediately, and thus won't actually move anywhere. Unless of course you are hiding...) And basically if you want to make an enemy to run to a waypoint even if the player is around, all you have to do is to "blindfold" it by using Nonreactive statemachine.

Next try changing the movement message into:

::room2::e1->C_GoToPlayer(1);

The parameter indicates the movement speed. This makes the enemy, obviously, run to the player, and once it spots the player, act according to the statemachine it is in. Basically all the NPC helpers there was in Max Payne applied this movement message, and it is also useful in some combat scripts.

Here's an example of C_Patrol message:

::room2::e1->C_Patrol( ::room2::w1, ::room2::w2, ::room2::w3, 0.5 );

So you define three waypoints and the movement speed. Two of the defined waypoints can be the same, if you want to patrol between two waypoints. Also in C_Patrol the enemy will start acting according to the statemachine if it perceives the player. You can try and make the enemy to patrol at its startup between some waypoints.

AI net manipulation

Sometimes you might have to manipulate the AI network a bit, for example if you don't want to have an AI network somewhere on the floor, or if you want to create an AI network on the air, or if the AI network is not dense enough.

First of all you can control the density of the AI net for any given room from the properties of the room. Select a room in F5 mode, press enter, and select the Statistics tab.

The number you can see there on the AI net density slider is meters per AI node. You can try changing the value, and then re-calculating the AI network and view the results with F7 and F8 in the game.

Another way of manipulation is something we already applied when we did the door in FSM & DO tutorial; AI Blocks. AI blocks are objects that are using a material from the AI_node_collision_nodraw category. What they do is, that the engine takes them into account as geometry when it calculates the AI network, even though they aren't drawn or collided into when playing the game.  As you might remember, we created AI blocks by the door so that AI net wouldn't be created to the locations where the door would be once opened. Basically in the AI net calculation process, the AI blocks are considered as just another walls, and obviously no AI net will be passing through them.

 You can also build platforms in the air so that AI net will be created into mid-air, if that is desired. Try for example building a wedge and you'll see how it affects the AI net.

Notice how the AI net visualization shows lines pointing downwards from the upper parts of the wedge. They are indicating that an enemy can jump down from those locations. If the wedge would be higher, the AI net wouldn't allow the enemies to jump down from too high. Building AI net into air is useful sometimes because the AI net calculation doesn't take any dynamic objects into account at all. You can, for example, build an AI net inside elevators with AI blocks.

When you experiment with this, don't forget that the game strips out AI nets when there is no way for any enemy to reach that place. For example no AI net is created on top of shelves, unless there is an enemy or a waypoint there (Where enemy could be teleported). Likewise, if there are no enemies or waypoints in your level, no AI net will be created into the level at all.

Team combat

Making a combat with several enemies involved might sometimes be a bit tricky if you don't build the structure of the FSM-script properly. Next we are going to suggest a way of doing team combat properly, and explain how to do death cinematic for the last dying enemy of a bunch.

The whole idea here is that when any of the enemies spots the player, all the others should notice this as well. It is very easy to go wrong here by making all the enemies to send messages to all the other enemies. As was already explained in the FSM & DO tutorial, it is always better to gather all the messages into one location that will then send the required messages to every entity involved from there. So once again:

Imagine the joy of debugging an FSM structure as shown on the right. But as the left-hand picture is trying to illustrate, we are going to use a sort of a FSM relay with a custom string which sends all the messages to the involved enemies when its custom event is triggered. And obviously it is triggered as any of the enemies perceives the player.

Before adding any actual enemies, let's first copy/paste one more room for our level, and add a door between. Rename the room as room3. Then let's put some decoration into the room for our enemies to take cover. I'm simply using crates for this tutorial, although I assume you will come up with something more interesting in your real levels.

The next time you export the level, don't forget to recalculate the AI net since the geometry of the level has changed. Usually the game lets you know if the AI net is not valid, but in some cases it can just crash. Also it can't detect it if the furniture inside the room has changed, just if the room itself or the room count has changed.

Then first create one dummy object (object with dummy texture) into the new room, and turn it dynamic. This will function as the relay FSM illustrated in the FSM structure pictures before, and also as a timer for our combat script. We are using a DO instead of a Floating FSM Name it as, say, script, set its Cont. upd. flag on, and add two states to it; enabled (default), and disabled. Also add custom strings called start and initialize.

Then add one enemy into the room. Rename it as e1, and for its Startup tab add:

this->C_SetStateMachine(nonreactive);
this->C_RemoveAllWeapons(berettadual);
this->C_PickupWeapon(pumpshotgun);
this->C_PickupAmmo(pumpshotgun, 3);

And to its OnActivate tab:

::room3::script->FSM_Send(start);

Now we have the first enemy in, which we can use as a sort of a template for the others. It is nonreactive from the level start, it's got a dual beretta and pumpshotgun with it. It's gonna trigger a custom event called start for the script DO when it perceives the player.

Next. copy/paste the enemy into three (in F5 mode), and place them around as you wish. You can change the enemy types from their properties to get some variation. Also you should vary their weaponry somewhat. And let's also make two of the enemies to run for cover. For that, we are going to have to add some waypoints into the room. Where should you add them depends wholly on where do you want the enemies to run and how do you want them to behave after that. For this example, we'll make e1 to just follow the player, e2 will run for cover and tries to stay there, moving back and forth between two waypoints, and e3 will run for cover for a while and run out as soon as e1 is killed. So let's set up waypoints like this:

Obviously, e1 won't need any waypoints. There's two waypoints for e2 so that we can make it run between them from time to time; totally static targets are just too easy to shoot. And one waypoint for e3 since it won't be spending much time in its cover anyway.

But it is now, the enemies would be just standing around with their weapons, without being able to even perceive the player. They are standing as nonreactive initially because we don't want them to hear the player too soon, from behind the door. But we do want them to become aware once the player opens the door. So, let's do that next. First, add a message to the door opening animation (pay attention to which one of the opening animations, if the door opens both ways):

::room3::script->fsm_Send( initialize );

So it's going to trigger this custom event for the script DO as it is opened. If your door is such a door that it closes automatically, do not forget to add logics there that will send the message only for the first time the door is opened.

Next open up the FSM dialog of the script DO, and for the custom event initialize, add messages:

In other words, once the door is opened, all the enemies will go into idle, and one of the enemies will walk to the player. Essentially, since the enemies are going to start a combat as soon as the spot the player, it will work so that one of the enemies will turn towards the player, or if the player manages to hide before he is spotted, this same enemy will walk to the door and towards the player to "investigate" what is going on.

Then to the actual reactions for when they perceive the player. First of all, we are going to need couple of timers for making the e2 to run between the two waypoints. So add two animations to the script DO:

And then to the custom even start, add these messages to the state-specific message list for enabled state:

this->FSM_Switch(disabled);
::room3::e1->C_SetStateMachine(mobstercombat);
::room3::e2->C_SetStateMachine(crouchandshootstatic);
::room3::e3->C_SetStateMachine(crouchandshootstatic);
::room3::e1->A_Play3DSound(enemy, mobster_group_alert, head);
::room3::e1->C_SetIdle(1, false);
::room3::e1->C_SetStateMachine(delay500ms);
::room3::e3->C_GoToAndShoot(::room3::e3w1, 1);
this->DO_Animate(e2_timer1);

The first message switches the state to disabled, so that the start custom event getting triggered won't have any effect after it's been triggered once. Then it sets the enemies into combat statemachines. It makes e1 shout an alert, and mess with its weapon for half a second. e3 will run to its cover while shooting at the player, and because of having crouchandshootstatic statemachine set, it will stay there then as well. Lastly, it will start the first timer animation that we just added in.

And notice that since e1 is going to shout something, we should add little something to it's OnDeath tab:

this->A_StopAll3DSounds(head);

Then to the messages that the animation keyframes of the script DO should trigger:

   

As you can see, the movement messages for e2 are sent from here, because they have to be re-sent over and over until e2 dies. And as you can see, the two timers start up each others at the end keyframes. We still have to make sure the animation does end when e2 dies, or otherwise the object would keep animating until the end of the level, causing some overhead. So to the OnDeath tab of e2, add:

::room3::script->DO_StopAnimation();

And still to make e3 come out of the cover as e1 dies, to the OnDeath tab of e1, add:

::room3::e3->C_SetStateMachine(mobstercombat);
::room3::e3->C_GoToPlayer(1);

There, now you can export the level and try it out!

Once you are through with testing and seen that everything works as is supposed to, let's still add logics to show a cinematic death cam of the last person who dies. First add a Floating FSM into the room, name it as death_counter, add states 1 (default), 2 and 3 to it, and add a custom strings called add1, add2 and add3 to it.

Then to the OnDeath of e1, add:

::room3::death_counter->FSM_Send(add1);

To the OnDeath of e2, add:

::room3::death_counter->FSM_Send(add2);

To the OnDeath of e3, add:

::room3::death_counter->FSM_Send(add3);

So each one of the enemies will trigger their own custom event to the death_counter. Now make it so that each time any of these events gets triggered, the custom FSM will change its state to the next one. Basically upon the custom event add1, when on state 1, send this->FSM_Switch(2);. When on state 2, send this->FSM_Switch(3); etc... This is from the FSM dump of the death_counter:

Send on "FSM_Send(add1)"
Send on "1"
this->FSM_Switch(2);
Send on "2"
this->FSM_Switch(3);

Send on "FSM_Send(add2)"
Send on "1"
this->FSM_Switch(2);
Send on "2"
this->FSM_Switch(3);

Send on "FSM_Send(add3)"
Send on "1"
this->FSM_Switch(2);
Send on "2"
this->FSM_Switch(3);

So basically just add all the six FSM_Switch messages to the correct state-specific message lists. Take a look at the example level if it's hard to figure out.

Then to the add1 custom event when on state 3, add:

::room3::e1->CAM_AnimateParented(death_01);

This means if e1 sends custom event add1 to the death_counter when it's in state 3 (=both other enemies have been killed already), it will trigger the death cam for e1. As you propably guessed already, you add similar message to the other custom events as well.

To the add2 custom event when on state 3, add:

::room3::e2->CAM_AnimateParented(death_01);

To the add3 custom event when on state 3, add:

::room3::e3->CAM_AnimateParented(death_01);

And there you have it. Export the level and try it out.

Debugging combat scripts

By now you propably noticed that it's sometimes easy to forget something when making combat scripts, and when it happens that your enemies aren't behaving as you intented them to, there are few methods to debug your combat scripts.

One  method is to print an XML dump if the FSMs; While in move mode in MaxED, select Dump FSM's as XML from the Mode commands dialog. You can investigate the FSM messages that way for errors.

Or in some cases it is useful to dump the information of a given character to the console in order to see which statemachine it is in and various other useful information:

::room2::e1->c_dump();

You can scroll the console list with Ctrl+arrow keys. If you can't remember the name of the enemy, you can get it printed into the console by typing MaxPayne_GameMode->GM_DrawTarget(1); and then aiming at the character.

Few other useful commands

There are some other commands that are pretty simple to use and you might find them useful. I'll explain them through simple examples:

P_CreateProjectileToBone( enemy_painkiller, 1, gun ); - The first parameter is the projectile to create, the second parameter is the number of projectiles to create, and the third parameter is the bone where to create the projectile. This message typically used at the OnDeath tab of an enemy, so that it will drop something when it dies. You can see the valid projectiles to be used in here from data\database\projectiles\. Basically use anything with enemy_ prefix.

C_SetTargetCharacter( ::Room3::NPC1 ); - This will make a character to consider someone else as its enemy than player. So basically you can make enemies shoot at each others. As a parameter you can define any character in the level, or then simply player.

C_ForceUpdate( 15 ); - Typically the engine doesn't handle the behaviour of any enemy unless the enemy is in the view (or has been a while back), or some movement message has been sent to an enemy. So sometimes you might need to use this message to force the engine to start handling some enemy, in order for it to be able to see the player, etc... The parameter is the time in seconds how long the engine will keep updating the character even if it keeps staying out of the view.

C_Teleport( ::Room2::w1 ); - Is used to teleport enemies to the waypoints. It teleports the message receiver to the waypoint defined in the parameters.

C_SendSpecial(); - This is typically sent to the activator from a character collide trigger. It is used to make something happen when a specific enemy touches a trigger. You propably noticed the mysterious event called OnSpecial in the message dialog of the characters. When C_SendSpecial(); is sent to the activator, it will trigger the OnSpecial event for the character, thus if you for example want a character A to die as it touches a trigger, you send this to the activator, and from the OnSpecial tab of A, you send this->c_sethealth(0); and also in most cases disable the trigger as it's of no use anymore.