Jump to content
Sign in to follow this  
igneous01

some pseudo help with creating AI

Recommended Posts

So, im working on my platoon script right now and need some ideas for scripting a semi intelligent ai commander that can issue orders to his platoon for engagements.

Im currently working on the stealth/ambush aspect,

where if the timeofday is night, and platoon is within 400m of target location, the platoon commander will order the squads to go into stealth, hold fire, maintain formation with platoon, and then send 1 squad to recon the area. then after enemy strength has been assessed, the commander would issue the other squads to move into suitable positions for an ambush (selectbestplaces?) and wait until all squads are behindcover (findcover) or in a good spot to engage, then engage the area.

complicated? yes, so far i only have it working to the point where a recon squad is sent out to scout the area (hopefully without being spotted)

what im trying to figure out is:

1. How would I script a check for cover and ambush positions using findcover and selectbestplaces that are within the target area?

2. Is there a way I can check for LOS to the target area?

3. Some help or ideas on how to order the platoon commander to move squads according to enemy positions for best engagement?

and it also seems selectbestplaces is not working for me, I want my platoon commander to look for the closest hill near him, so he can move to it and observe the area.

im using this:

_Hills = selectBestPlaces [[getpos _PlatoonCmd select 0, getpos _PlatoonCmd select 1],500,"(1 - forest) * (1 + hills) * (1 - sea)", 50 ,5];
{
	_dist = _PlatoonCmd distance (_x select 0);
	if (_dist < 250) then {
		_Stratloc = _x select 0;
		_mg = createmarker ["Marker1", _Stratloc];
		"Marker1" setMarkerType "DOT";
	};
} foreach _Hills;

except no marker is ever created (this is inside a loop that updates every 10 seconds too)

and one last thing:

should I be using an FSM for the platoon commander for checking conditions like these? Ive ran into a problem where the main loop keeps executing the stealth procedure over and over again, meaning that a random squad keeps being selected to scout the area after every loop.

And ideas or theories to go about doing some of this?

Share this post


Link to post
Share on other sites

This works:

_Hills = selectBestPlaces [getPos player,500,"(1 - forest) * (1 + hills) * (1 - sea)", 50 ,20];   
{
   _dist = player distance (_x select 0);    
   if (_dist < 250) then {
       _Stratloc = _x select 0;
       _mg = createmarker [format ["mrk_%1", floor random 99999], _Stratloc]; 
       _mg setMarkerShape "ICON";
       _mg setMarkerType "DOT";
   };   
} foreach _Hills;

Although it does not seem to pick hills very good. This is probably the issue (From BIKI):

To create a marker you need to define at least the following three settings:

_markerstr = createMarker["markername",[_Xpos,_Ypos]];

_markerstr setMarkerShape "ICON";

"markername" setMarkerType "DOT";

I thought about making a project like this. However, couldn't find a (non-ridicilous) way to make units go from A to B with running out to random engagements it should avoid.

2. AFAIK, there is not yet a practical way to check LOS to a target area.

3. Check for height and vegetation/cover advantages surrounding target location. A way might be to check a half circle a certain distance around the target for vegetation and height advantages from your squad's point of view.

Also be careful of the moon, from my unscientific gameplay sessions with AI during night, I found they perform better when you are lit by the moon. However, to check for this you'll probably need a table and some functions.

Edited by Muzzleflash

Share this post


Link to post
Share on other sites

1: findcover command is not working in latest official patch.

you can use a combination of boundingbox and nearestObjects maybe, and then calculate direction to enemy or area and select the side of the object found in opposite direction.

2: Calculating LOS, can be done by pushing a invicible object forward from position to object and checking if any other object touches it, but its a time consuming and heavy way of doing it, several posts about it on forums, i think CarlGustaffa had a very good version, but again, he warns of massive usage of it as its heavy in use.

note, be aware that simply pushing the object forward will not do, as you also need to push it in the Z direction, (uphill, downhill).

3: this one will be somewhat script heavy, but i would not use any selectbestplaces etc commands, simply go for range and direction spaced out in whatever formation you decide, for example tanks behind inf squads, mg and AT teams on flanks and in center etc....

very doable, but would require you to script for alot of variables, like enemy here or enemy there, enemy what or enemy whatnot, amount of enemy, are they firing, did someone get spotted before all is ready and so on....

the issue you have with additional squads being sent out to recon is most likely because you dont have collected the first squad and waited until its task where done or dead etc.

hard to say wittout looking at your whole script.

I was intrigued by your post, and have been fiddling with a similar idea before, but never got around to try and realize it.

Edited by Demonized

Share this post


Link to post
Share on other sites

hmm, your quite right, it really doesnt pick hills very well, i guess it must be a limitation with certain map configs not being defined as hills, even tho it is elevated enough to be a ridge.

@demonized - strange, i didnt realize findcover isnt working, because the ai now seem to take more cover in engagements? i suppose the fsm was updated, but the command was not. I shall see what bounding box can do for me

glad to see im not the only one whos been thinking of this - if only highcommand had some ai compatability.

in case you guys are interested, heres the rough script:

// PLATOON COMMAND SCRIPT

private ["_PlatoonCmd", "_SquadLeaders", "_groups", "_task", "_logics", "_t", "_logicCenter", "_logicGroup"];

_groups = []; // array that stores all groups synched to commander

_logics = []; // array of logics that will be used as formation slots

_PlatoonCmd = _this select 0; // The platoon commander

_task = _this select 1;

_SquadLeaders = synchronizedObjects _PlatoonCmd; // Array that stores squad leaders of groups

_logicCenter = createCenter sideLogic;

_logicGroup = createGroup sideLogic;

{_groups set [count _groups, group _x]} foreach _SquadLeaders; // Adding groups into array

//_tasks = _this select 2;

// Relative position function

_relativePosition = {

private ["_dist", "_offdir", "_leadVehicle", "_planePos", "_compassDir", "_newX", "_newY", "_newZ", "_newPos"];

_leadVehicle = _this select 0; // the object another object is offset from.

_offdir = _this select 1; // direction for newPos in relation to leadvehicle, 0 is direct front, 90 right, 180 rear etc..

_dist = _this select 2; // distance from center of _leadVEhicle to its ofset position.

//find position to spawn in relation to.

_planePos = getPos _leadVehicle;

//Find compass direction to spawn from leader plane.

_compassDir = ((FormationDirection _leadVehicle)+_offdir) mod 360;

//Find absolute coordinates by adding relative to leader plane.

_newX = (_planePos select 0) + (sin _compassDir * _dist) - 300;

_newY = (_planePos select 1) + (cos _compassDir * _dist) + 50;

_newZ = (_planePos select 2);

_newPos = [_newX, _newY, _newZ];

_newPos

};

// Contact report function

_contactReport = {

private ["_units", "_objects", "_t"];

_units = _this select 0;

_t = 0;

{

_objects = nearestObjects [_x, ["Man","Car","Tank","Air"], 400];

_object = _objects select _t;

hint format ["object: %1", _object];

_totalobj = count _objects;

if (((_x knowsabout _object) > 1) && ((side _object) == EAST)) then {

{_x reveal _object} foreach _units;

_x sidechat "Contact!";

};

_t = _t + 1;

if (_t >= _totalobj) then {

_t = 0;

};

} foreach _units;

};

// for anchoring formation slots and updating

_logicAnchor = {

private ["_t", "_dist", "_offDir", "_leadVehicle", "_logics"];

_t = 0;

_dist = 0;

_offDir = 90;

_leadVehicle = _this select 0;

_logics = _this select 1;

{

_dist = _dist + 100; // lets add 10 spacing inbetween each object wich is placed in a direct line front of player no matter where he is looking.

_newPos = [_PlatoonCmd,_offDir,_dist] call _relativePosition;

_x setPos _newPos;

} foreach _logics;

};

// group formations

_groupForm = {

private ["_groups", "_logics", "_t", "_Safe", "_Leaders"];

_groups = _this select 0;

_logics = _this select 1;

_t = 0;

_Safe = true;

_Leaders = [];

{

_Leaders = _Leaders + [leader _x];

} foreach _groups;

{

{

if (behaviour _x == "COMBAT") then {

_Safe = false;

};

} foreach (units _x);

if (_Safe) then {

_x move (getpos (_logics select _t));

_t = _t + 1;

if (_t >= 4) then {

_t = 0;

};

};

} foreach _groups;

};

// Commander strategy

_CommanderManagement = {

private ["_task", "_PlatoonCmd", "_SquadLeaders", "_timeD", "_Sourceplace", "_Hills", "_Forest", "_Stratloc", "_Stealth"];

_PlatoonCmd = _this select 0;

_SquadLeaders = _this select 1;

_task = _this select 2;

_timeD = daytime;

_Stealth = false;

// Determine the terrain surrounding the task zone

// Look for Hills or high altitude points for observation

_Hills = selectBestPlaces [[getpos _PlatoonCmd select 0, getpos _PlatoonCmd select 1],500,"(1 - forest) * (1 + hills) * (1 - sea)", 50 ,5];

{

_dist = _PlatoonCmd distance (_x select 0);

if (_dist < 250) then {

_Stratloc = _x select 0;

_mg = createmarker ["Marker1", _Stratloc];

_mg setMarkerShape "ICON";

"Marker1" setMarkerType "DOT";

};

} foreach _Hills;

// Determine whether stealth is viable. If its nighttime then commander will order all to go into stealth and hold fire, and send out 1-2 teams to scout ahead for enemy

//if (_timeD > 21 && _timeD < 7) then {

if (!_Stealth) then {

{_x setCombatMode "GREEN"; _x setBehaviour "STEALTH"; doStop _x} foreach _SquadLeaders;

_compassDir = ((FormationDirection _PlatoonCmd)) mod 360;

_dist = _PlatoonCmd distance _task;

_ReconPos = [(getpos _PlatoonCmd select 0) + (sin _compassDir * _dist) - 200, (getpos _PlatoonCmd select 1) + (cos _compassDir * _dist) - 200, getpos _PlatoonCmd select 2];

_ReconMark = createMarker ["ReconMark", _ReconPos];

"ReconMark" setMarkerType "DOT";

_ReconSquad = (_SquadLeaders select (floor(random(count _SquadLeaders))));

_ReconSquad move (_ReconPos);

_PlatoonCmd sidechat format ["Alright listen up, Squad %1 will be going ahead to recon the area, we hold here.", _ReconSquad];

_Stealth = true;

//_PlatoonCmd move (_Stratloc);

_PlatoonCmd setCombatMode "Green";

_PlatoonCmd setBehaviour "Stealth";

waitUntil {unitReady (leader _ReconSquad)};

};

//};

};

// create default Logics to be used for formation slots between squads

for "_i" from 0 to 3 do {

nul = "Logic" createUnit [getPos _PlatoonCmd, _logicGroup];

hintsilent format ["number: %1", (_i + 1)];

};

_logics = _logics + (units _logicGroup);

// first split all groups into respective teams

//{nul = [_x] execVM "AutoTeamAssign.sqf"} foreach _groups;

// Spread groups out into a line formation

while {alive _PlatoonCmd} do {

player sidechat "In loop";

_dist2task = _PlatoonCmd distance _task;

if (_task getVariable "PC_Zone_Task" == "Attack") then {

_PlatoonCmd move (getpos _task);

player sidechat "Task: Assault";

};

if (_dist2task > 500) then {

player sidechat "Still in safe zone";

[_groups, _logics] call _groupForm;

};

if (_dist2task < 500) then {

_PlatoonCmd sidechat "Eyes sharp everyone, we're in possible hostile territory";

[_PlatoonCmd, _SquadLeaders, _task] call _CommanderManagement;

};

[_PlatoonCmd, _logics] call _logicAnchor;

[[_PlatoonCmd] + _SquadLeaders] call _contactReport;

sleep 10;

};

Edited by Igneous01

Share this post


Link to post
Share on other sites

Regarding LOS I just made a simple scripts that performs terrainbased LOS using simple checking of heights. I minimized the code in the loop so it should be pretty efficient (as it can be), and it should be no problem if you just want to see if a squad has LOS on another squad.

/*
Terrain based LOS - Does NOT check for vegetation or buildings
Returns nil on bad positions/objects.

FROM_ASL - OBJECT or POS in ASL
TO_ASL - OBJECT or POS in ASL
RESOLUTION - Distance between each check in meters.
EXTRA_ALLOWED - Defaults to 0.06 for extremely small stretches of terrain that technically obstructs LOS.
   "Eye"-height to add to both, or if [NUM, NUM] to add to both viewer and target respectively.
*/
haveTerrainLOS = {  
   private ["_result","_currentPos","_actualHeight","_fromASL","_toASL","_resolution","_maxChecks","_stepVector","_extra"];
   _fromASL = _this select 0;
   _toASL = _this select 1;
   //Object inputs?
   if (typeName _fromASL == "OBJECT") then {_fromASL = getPosASL _fromASL};
   if (typeName _toASL == "OBJECT") then {_toASL = getPosASL _toASL};
   //Don't return true on bad input!!!
   if (isNil "_fromASL" || isNil "_toASL") exitWith {nil};
   _resolution = _this select 2;
   _extra = 0.08;
   //Add extra
   if (count _this > 3) then {_extra = _this select 3;};
   if (typeName _extra == typeName []) then {
       _fromASL set [2, (_fromASL select 2) + (_extra select 0)];
       _toASL set [2, (_toASL select 2) + (_extra select 1)];
   } else {
       _fromASL set [2, (_fromASL select 2) + _extra];
       _toASL set [2, (_toASL select 2) + _extra];
   };
   //Save one multiplication per step for only a tiny loss in accuracy hopefully.
   _stepVector = [_fromASL, _toASL] call BIS_fnc_vectorFromXToY;
   _stepVector = [_stepVector, _resolution] call BIS_fnc_vectorMultiply;
   //Assert there is LOS
   _result = true;
   _currentPos = _fromASL;
   _maxChecks = floor ((_fromASL distance _toASL) / _resolution);
   for "_i" from 1 to _maxChecks do {
       _currentPos = [_currentPos, _stepVector] call BIS_fnc_vectorAdd;
       _actualHeight = getTerrainHeightASL _currentPos;
       if (_actualHeight > _currentPos select 2) exitWith {_result = false;};
   };
   _result
};

//Will check if player has LOS to target using height checks every 5m.

[player, target, 5] call haveTerrainLOS;

//However, that actually uses the height at feet level. If you want to see if soldier A can see the body of B you can do this:

[player, target, 5, 1.5] call haveTerrainLOS;

//This will perform the LOS at chest-like height. If you want to see whether you have LOS to the terrain the target is standing on you can do this:

[player, target, 5, [1.5, 0]] haveTerrainLOS;

However there are certain limitations this approach, eg. very steep slopes. To speed up the checks you might want to perform on with a resolution of 25 or 50m first and if that passes then use a finer res.

I did some simple testing on Utes and it worked fine. It is technically possible to include obstructions such as vegetations and buildings by checking if a building is in the way of a 'step' and then see if it actually obstructs by using stuff like line-box collisions.

Edited by Muzzleflash

Share this post


Link to post
Share on other sites

thanks muzzleflash for the script - that will certainly come in handy \o/

however it looks like my formation slots are still not working correctly - i thought it was fixed but i see that the formation

S S S S

- -P- -

doesnt rotate around the platoonleader (in this case the P) but goes off axis somewhere. Anyone have any ideas? im using this script for it:

// Relative position function
_relativePosition = {
private ["_dist", "_offdir", "_leadVehicle", "_planePos", "_compassDir", "_newX", "_newY", "_newZ", "_newPos"];
_leadVehicle = _this select 0;  // the object another object is offset from.
_offdir = _this select 1; // direction for newPos in relation to leadvehicle, 0 is direct front, 90 right, 180 rear etc..
_dist = _this select 2;  // distance from center of _leadVEhicle to its ofset position.

//find position to spawn in relation to.
_planePos = getPos _leadVehicle;

//Find compass direction to spawn from leader plane.
_compassDir = ((FormationDirection _leadVehicle)+_offdir) mod 360;

//Find absolute coordinates by adding relative to leader plane.
   _newX = (_planePos select 0) + ((sin _compassDir) * _dist) - 300;
   _newY = (_planePos select 1) + ((cos _compassDir) * _dist) + 50;
_newZ = (_planePos select 2);
_newPos = [_newX, _newY, _newZ];
_newPos
};

// for anchoring formation slots and updating
_logicAnchor = {
private ["_t", "_dist", "_offDir", "_leadVehicle", "_logics"];
_t = 0;
_dist = 0;

_leadVehicle = _this select 0;
_logics = _this select 1;

//if (BALLZ == true) then {

	BALLZ = false;
//};

if ((_leadVehicle getVariable "FORM") == "LINE") then {
	{
		_offDir = 90;
		_dist = _dist + 100;  // lets add 10 spacing inbetween each object wich is placed in a direct line front of player no matter where he is looking.
		_newPos = [_leadVehicle,_offDir,_dist] call _relativePosition; 
		_x setPos _newPos;
	} foreach _logics;
};

if ((_leadVehicle getVariable "FORM") == "FILE") then {
	{
		_offDir = 0;
		_dist = 50;  // lets add 10 spacing inbetween each object wich is placed in a direct line front of player no matter where he is looking.
		_newPos = [_leadVehicle,_offDir,_dist] call _relativePosition; 
		_x setPos _newPos;
		_logicmark = createmarker ["logicmark", _newPos];
		_logicmark setmarkertype "dot";
		_logicmark setMarkerColor "ColorBlue";
		_logicmark setmarkerpos _newPos;
	} foreach _logics;
};
_logicmark1 = createmarker ["logicmark1", getpos (_logics select 0)];	
"logicmark1" setmarkertype "dot";
"logicmark1" setMarkerColor "ColorBlue";
_logicmark2 = createmarker ["logicmark2", getpos (_logics select 1)];	
"logicmark2" setmarkertype "dot";
"logicmark2" setMarkerColor "ColorBlue";
_logicmark3 = createmarker ["logicmark3", getpos (_logics select 2)];	
"logicmark3" setmarkertype "dot";
"logicmark3" setMarkerColor "ColorBlue";
_logicmark4 = createmarker ["logicmark4", getpos (_logics select 3)];	
"logicmark4" setmarkertype "dot";
"logicmark4" setMarkerColor "ColorBlue";

"logicmark1" setmarkerpos (getpos (_logics select 0));
"logicmark2" setmarkerpos (getpos (_logics select 1));
"logicmark3" setmarkerpos (getpos (_logics select 2));
"logicmark4" setmarkerpos (getpos (_logics select 3));

};

markers are just there for visually seeing it, and they are way off what they are supposed to be :(

Share this post


Link to post
Share on other sites

_dist = 50;

You don't sum it like in "LINE" so all the markers in "FILE" ends up in the same location.

All the markers are also offset because of the fixed X and Y offsets in you add in _relativeOffsets (-300, +50). With the exception of these 2 issues it works fine.

If you want some unit to be 30m in front of the leader and 15m to the left then you can do something like this to get the relative angle and distance:

_ahead = 30;
_side = -15;
_dist = [0, 0] distance [_ahead, _side];
_angle = asin (_side / _dist);

Edited by Muzzleflash

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
Sign in to follow this  

×