Jump to content
Lethib

Creating a player / ai interaction?

Recommended Posts

Hi everyone, 

 

I am actually making a mission set in the WW2. The player play paratroopers and are dropped around the objectiv at night, their unit is scattered and i would like to use the clickers (see exemple) to help them distinguish each others and regroup. Some of the paratroopers are ai and i would like the ai to use and respond to the clicker sound as well. (I know this exists as a script already, but only for ACE, and i'm not shure that ai reacts to it)

 

To summarize : 

  • the (player) paratrooper seeing someone or earing a noise can do one click. If a friendly unit is nearby (30m for exemple),  it will respond with two clicks, move to the player location and have a addaction to join the players group.
  • Idealy,  i'd like the ai to take the initiative of the click if the player is getting closer (15m or so). If the player responds (two clicks), the ai will also move to the player location and join the player group

 

I usually manage to use parts of scripts here and there to get stuffs done, but i seem to have reeched my limits 😃

 

so far, i have :

initPlayerLocal.sqf

this addAction ["1 Click", 
    { 
       { null = execVM "clicker.sqf" } remoteExec ["call", 0]; 
    }, 
    nil, 
    1.5, 
    false, 
    false, 
    "", 
    ""];

this addAction ["2 Click", 
      { null = execVM "clicker2.sqf" } remoteExec ["call", 0];
       }, 
    nil, 
    1.5, 
    false, 
    false, 
    "", 
    ""];

 

CLICKER.SQF

_caller = this
		private _list =  _caller nearEntities ["CAManBase",30] select {side _x == WEST && !(group _x isEqualTo group _caller) && !isPlayer _x };
_sounds = ["sound1","sound2","sound3"];
	
sleep 1;
	_caller say3D clicker;

sleep 3;
	{ [_x,clicker2] remoteExec ["say3D"]; sleep 2; [_x,getPos _caller] remoteExec ["move",_x]; [_x,["Join the unit", {this join (_this select 1); [_x,(_sounds select ([0,(count _sounds)-1] call BIS_fnc_randomInt))] remoteExec ["say3D"] },[],1.5,true,true,"","_this distance _target < 3"]] remoteExec ["addAction",2]; } forEach _list;


	               
 

 

CLICKER2.SQF

_caller = this

_caller say3D clicker2
_caller setVariable ["answering",TRUE,TRUE];
sleep 3;
_caller setVariable {"answering",FALSE,TRUE];

 

AI INIT

_sounds = ["sound1","sound2","sound3"];

_unit spawn {
  params ["_unit","_timer"];
  while { alive _unit && lifeState _unit != "incapacitated" && units _unit isEqualTo [_unit] } do {
    sleep (30 + random 120);
    if !(alive _unit && lifeState _unit != "incapacitated" && units _unit isEqualTo [_unit]) exitWith {}; // something can be different after sleep
    [_unit,clicker] remoteExec ["say3D"];
    _timer = diag_tickTime;
    waitUntil { allPlayers findIf {_x distance _unit < 30 && (_x getVariable ["answering",FALSE])} >-1 or diag_tickTime > _timer + 3 };
    if (diag_tickTime <= _timer + 3) then {
      _leader = (allPlayers select {_x distance _unit < 30 && (_x getVariable ["answering",FALSE])} select 0);
      [_unit] move getPosATL _leader;   // because at least one close player has answered by clicker2 (variable answering in addAction of player). See also joinSilent
      [_unit,["Join the unit",{this join (_this select 1); [_x,(_sounds select ([0,(count _sounds)-1] call BIS_fnc_randomInt))] remoteExec ["say3D"]}, 
    nil, 
    1.5, 
    true, 
    true, 
    "", 
    "_this distance _target < 4"]] remoteExec ["addAction",2];
    };
  };
};

 

Problems are:

  • I'm afraid the mission performances will drop if all the ai constantly check for distance with my group
  • I mainly have trouble selecting the proper units. In this code, all the "man" type objects around the player will respond to the click. I'd like it to be only the friendly, not player units.

 

 

Share this post


Link to post
Share on other sites

If you're in MP, the problem is more difficult than it appears. you need to understand locality for making the addAction work.

As first rule, don't script in init field of the playable units (the code will run at each JIP and you will add an addAction per player)

So, scripting something for player should be done in initPlayerLocal.sqf

The say3D is a good idea for distance but it's an AG EL command. That means only the player firing the addAction will ear the click (if clicker or clicker2 is a valid sound). You need to remoteExec on...  allplayers within a distance. That's no too difficult.

On the other hand your eventHandler "soundPlayed" will not work for evident reason if you read the BIKI.

Of course, an AI will not ear anything, you just have to consider its distance to the player as condition to do something.

 

AI can also play3D (then click) along with the condition you want. addAction has no sense on them but the code stays OK the say3D is fired on server (where the AI is) and you must remoteExec it as well for the surrounding players.

 

No problem with performance

Friendly units are selectable with side or bis_fnc_friendlySides

 

  • Like 1

Share this post


Link to post
Share on other sites

Hi Pierremgi, thanks for your reply!

 

Ok so I will create a initPlayerLocal instead of changing the init of the playable units (it's easier that way 😃 )

I added the remoteExec on the say3D where i forgot it.

 

And you are right, I misread the eventhandler, I was thinking that you could simply add the name of your sound.. I'll delete it and just add some time before the unit moves toward the player. It's frustrating though because it would have been more logical for the ai to wait for the player to answer the click before moving..

 

About the addaction, the objectiv is for the player to be able to add the unit to his group, i think it should work. 

 

I ended up selecting units that are not in my group and that are west side like you suggested but i'm not really confident about the whole clicker.sqf code and mostly about this :

_list = [position _caller nearObjects ["man", 50]

forEach _list
if!(_x in units group player) && (side _x == east) then
 {_x say3D clicker2; 
                   sleep2;
  _x move getPosATL player;
  _x addAction
}

 

Finally the main issue is that the two scripts are not exclusive : if i use the clicker and that the friendly units around me answer and move to my position, won't they trigger the second script once they get to 15m from my group? How do I make sure it doesn't happens? I was thinking maybe of a line in the clicker.sqf script that would erase the init field of the selected units, but I don't know if this is possible.

 

 

 

Share this post


Link to post
Share on other sites
5 hours ago, Lethib said:

 It's frustrating though because it would have been more logical for the ai to wait for the player to answer the click before moving..

 

Just a condition question. If I'm right, player is answering with the 2click addaction. So, you can pass a variable (temporary if needed) as condition. As you're in MP, broadcast it each time you change the value.
Example:

start in initPlayerLocal with:

player setVariable ["answering",FALSE,TRUE];  // true (2nd boolean) is for broadcasting the variable of this player, everywhere

then in 2click addAction:

_caller (or player as you want) setVariable ["answering",TRUE,TRUE];

sleep 3; (or a waitUntil but be sure that can ends)

_caller setVariable {"answering",FALSE,TRUE];

 

5 hours ago, Lethib said:

I ended up selecting units that are not in my group and that are west side like you suggested but i'm not really confident about the whole clicker.sqf code and mostly about this :


_list = [position _caller nearObjects ["man", 50]

forEach _list
if!(_x in units group player) && (side _x == east) then
 {_x say3D clicker2; 
                   sleep2;
  _x move getPosATL player;
  _x addAction
}

 

 

With "man" you could "call" rabbits and snakes. Use "CAManBase" instead. On the other hand, you have a side filter... no problem

Use also nearEntities , faster.

private _list = _caller nearEntities ["CAManBase",50] select {side _x == EAST && !(group _x isEqualTo group _caller) && !isPlayer _x };  // players can do what they want

{ [_x,clicker2] remoteExec ["say3D"]; sleep 2; [_x,getPos _caller] remoteExec ["move",_x]; } forEach _list;

 

That's for some details. This code can be written inside an addAction, that means for players who have the initiative (addAction).

For AIs, you need to randomize some code, your scenario must have some behaviors:

Ai's (ungrouped, alone)  for click? That means clicking from time to time (when? at night only? periodically? are they static or moving? how many concerned?)

 

Example for AI: _unit

 

_unit spawn {
  params ["_unit","_timer"];
  while { alive _unit && lifeState _unit != "incapacitated" && units _unit isEqualTo [_unit] } do {
    sleep (30 + random 120);
    if !(alive _unit && lifeState _unit != "incapacitated" && units _unit isEqualTo [_unit]) exitWith {}; // something can be different after sleep
    [_unit,clicker] remoteExec ["say3D"];
    _timer = diag_tickTime;
    waitUntil { allPlayers findIf {_x distance _unit < 50 && (_x getVariable ["answering",FALSE])} >-1 or diag_tickTime > _timer + 3 };
    if (diag_tickTime <= _timer + 3) then {
      _leader = (allPlayers select {_x distance _unit < 50 && (_x getVariable ["answering",FALSE])} select 0);
      [_unit] join _leader;   // because at least one close player has answered by clicker2 (variable answering in addAction of player). See also joinSilent
    };
  };
};

 

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Alright, thanks again Pierremgi!

 

I added your code to the script with a few changes, I hope it didn't mess it up 😉

I will try this out and let you know if everything went well 😃

 

As for the AI, it is set in squads (4 or 5 jumping out of as many planes) of 8 men. So they are grouped but also get scattered when they get to the ground. We often find them running around in pairs. And the mission happens at night. 

 

I'm also trying to add some random sounds when meeting up with the AI (in the addactions), some "Boy am i glad to see you!", "Were the hell are we?"...

 

 

 

 

Share this post


Link to post
Share on other sites

Hello again!

So I have been testing the script and I have a few questions to try and solve some problems.

 

In clicker.sqf, when calling the file, i get an error message saying forEach _list |#|' error invalid number in expression. How do I solve this? I could'nt find any solutions that works..

 

Also in clicker.sqf, I  had problems with the remoteExec so I removed it. Is it ok since I call the entire clicker.sqf via a remoteExec in the player addaction? It now looks like this and it works fine (except for the error), but I didn't test it in MP :

 

CLICKER.SQF :

private _list =  player nearEntities ["CAManBase",50] select {side _x == resistance && !(group _x isEqualTo group player) && !isPlayer _x }; 
player say3D "signal"; sleep 2; 
{ _x say3D "reponse"; sleep 2; _x move getPos player; } forEach _list;

I did as you suggested and removed "_caller" , but I am afraid that "player" will refer to any player and not the one actually doing the action. Thinking about it, it isn't that much of a problem as long as the players stay in group. 

 

Finally, the clicker2.sqf seems to be working fine but I have some issues with the init field of the AI. I removed the addaction "join group" from the clicker.sqf and put it directly in the AI init because using clicker.sqf multiple times resulted in the addactions stacking up on them.

The AI init now looks like this, and I have been waiting next to them to try and hear them use the clicker but I don't think it is working.. (the timer is set to 2 + random 1 to test it more quickly)

 

AI INIT :

[this,["Join the unit",{[(_this select 0)] joinSilent (group (_this select 1)); [(_this select 0),(selectRandom ["sound1","sound2","sound3"])] remoteExec ["say3D"]},   
    nil,   
    1.5,   
    true,   
    true,   
    "",   
    "_this distance _target < 4"]] remoteExec ["addAction",2];  
  
_unit spawn {  
  params ["_unit","_timer"];  
  while { alive _unit && lifeState _unit != "incapacitated" && units _unit isEqualTo [_unit] } do {  
    sleep (2 + random 1);  
    if !(alive _unit && lifeState _unit != "incapacitated" && units _unit isEqualTo [_unit]) exitWith {};     [_unit,"signal"] remoteExec ["say3D"];  
    _timer = diag_tickTime;  
    waitUntil { allPlayers findIf {_x distance _unit < 30 && (_x getVariable ["answering",FALSE])} >-1 or diag_tickTime > _timer + 3 };  
    if (diag_tickTime <= _timer + 3) then {  
      _leader = (allPlayers select {_x distance _unit < 50 && (_x getVariable ["answering",FALSE])} select 0);  
      [_unit] move getPosATL _leader;        
    };  
  };  
};

 

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×