AI behaviour depending on player actions

From The DarkMod Wiki
Revision as of 19:41, 2 January 2013 by Grayman (talk | contribs)
Jump to navigationJump to search

I am going to describe the setup of a mission, in which the AI is neutral to the player as default. The AI only gets hostile to the player, if the player is seen performing an illegal action. Such actions include: Being seen in an area where the player isn't supposed to be, stealing loot, or carrying a weapon.

The first thing you have to do is to put all affected AI on one team. Let's just take team 1. So set the spawnarg "team" "1" on all affected AI and make sure non-affected AI are on a different team. Create a target_setrelation entity and set the following spawnarg on it: "rel 1,0" "0". This will cause the AI on team 1 to see the player (team 0) as neutral (0). Target this entity from a worldspawn brush or trigger it via the command

 sys.trigger($nameof_target_setrelation);

included in your main() script function.

Being seen in an area where you aren't supposed to be

The first thing you need to do is to make use of the zone system. This approach is described here. This system allows you to call functions when the player passes from one zone to another. Here we'll make use of the call_on_entry script function. The following has to be applied to all allowed/disallowed zones neighboring each other. On the info_location entities belonging to the zones set the spawnarg "call_on_entry" "functionname", where functionname is:

setNeutralZone on allowed zones setHostileZone on disallowed zones

The functions used here have to have the form void functionname(entity entityname). The entity is the zone the player enters. As we don't need it here, but need the basic behavior for the weapon-based AI-player relation discussed below, they just call other functions with the same names without Zone at the end and no arguments passed. What they do is trigger the target_setrelation entities from the next section named ...

Caught stealing

This makes use of some triggers. You'll need two target_setrelation entities and one trigger_relay entity.

On the first target_setrelation entity set the spawnarg "rel 1,0" "-1". On the second, set "rel 1,0" "0". Let the trigger_relay target the second target_setrelation entity and set the spawnarg "delay" "T" on the trigger_relay, where T specifies the timespan in which the player is detected as hostile after stealing something (see description below).

Now let every item that the player is not allowed to take target both the first target_setrelation and the trigger_relay entity. The next thing you need to do is to set up a hidden objective with the content "If one AI of team 1 goes to alert state 5". In the success script field enter isHostile. This function will then destroy the second target_setrelation entity, so the AI stays hostile to the player.

Note: alert state 5 is the highest alert state and means that the player was seen by an AI.

What does the setup do?

Every time you steal something the AI from team 1 will be set hostile to you for a certain amount of time. After that period they will become neutral to you again, unless they see you within this amount of time. If so, they'll stay hostile. As you can see the amount of time (called T above) has an impact on how difficult it is for the player to get away with disallowed actions. Setting it to a higher value makes it more difficult and vice versa.

Being seen carrying a weapon

You'll need the following script:

 void CheckWeapon()
 {
       do
       {
               if ($player1.getCurrentWeapon() == 'atdm:weapon_unarmed')
               {
                       setNeutral();
               }
               else
               {
                       setHostile();
               }
               sys.wait(1);
       } while(true); 
 }

This function is started from the main method via

 thread CheckWeapon();

The functions setNeutral and setHostile are the same as above. The script sets the AI to hostile as long as you carry a weapon. Again, being seen by any hostile AI will cause the hostility to be permanent.

What is missing

There are other situations in which the player could be seen as a threat. For example if he is seen attempting to pick a door or opening one that is normally locked by a key the player isn't suppose to have. Also, the player could be seen as a threat when discovered carrying a body.

In addition, it is also not very realistic that all the AI in Team 1 will become your enemies once one AI has seen you doing something you're not allowed to do, especially since you have the possibility of killing him and hiding him without being seen.

Another point is that there is a delay on how long you can be detected after stealing something. Applying such a behavior to the other DONT'S may be useful.

This needs fixing and I will add solutions once I have them.

Summary

Now that we have all the pieces together, let's sum up how to set up the complete behavior. (We are starting from zero here; nothing has been done so far).

We need:

Two target_setrelation entities called makeHostile and makeNeutral

One trigger_relay entity called relay

A script file called mymapname.script, where mymapname is the name of your map.

An invisible objective "If one AI of team 1 get to alert level 5".


On all affected AI set "team" "1". Non-affected AI go to another team.

On makeHostile set "rel 1,0" "-1".

On makeNeutral set "rel 1,0" "0".

On relay set "target" "makeNeutral" and "delay" "1".

On the objective put stayHostile in the success script field.

On all objects that are not allowed to be taken by the player set "target" "makeHostile" and "target0" "relay".

On all info_location entities belonging to zones where the player is not allowed to be, set "call_on_entry" "setHostileZone".

Do the same for all other info_location entities but use the method "setNeutralZone".

Hint: The last two steps can be restricted to zones neighboring zones of the other kind.

On any worldspawn set "target" "makeNeutral".


In the script file include

 void setHostileZone(entity zone) {
   setHostile();
 }
 void setNeutralZone(entity zone) {
   setNeutral();
 }
 void setHostile() {
   sys.trigger($makeHostile);
 }
 void setNeutral() {
   sys.trigger($makeNeutral);
 }
 void stayHostile() {
   setHostile();
   $makeNeutral.remove();
 }
 void CheckWeapon() {
   do {
     if ($player1.getCurrentWeapon() == "atdm:weapon_unarmed") {
       setNeutral();
     }
     else {
       setHostile();
     }
     sys.wait(1);
   } while(true);
 }
 void main() {
   thread CheckWeapon();
 }

That's it. If you want to make the detection time for stealing things difficulty-dependent, add the following spawnargs on relay:

"diff_1_change_0" "delay" "diff_1_arg_0" "1.5" "diff_2_change_0" "delay" "diff_2_arg_0" "2.0"

Of course, you can use other times than mentioned here.

--Obsttorte (talk) 11:18, 2 January 2013 (EST)