Home » MODDING HQ 1.13 » v1.13 Feature Requests » [Feature Request] More Dynamic Enemy Orders
[Feature Request] More Dynamic Enemy Orders[message #344862]
||Mon, 04 April 2016 14:53 |
Location: Bremen, Germany
||Some of you might know how the enemies receive their basic behavior when placed in maps. For those who don't know it, fear not... it is easier than you'd expect:
Now here's the main problem with this: Once set, the enemy in question will keep his simple order regardless of the tactical situation. Which is actually kinda bad.
Some explanations (correct me if I'm wrong, as I have not looked into the code)
On Call -> Enemy will stroll around the placement point randomly, but will stay within a few tiles. When reinforcements by another guy are called or if the enemy hears gun fire, the enemy will move freely. This is a good basic order. The enemy will however be rarely a major problem to dispatch, as on call guys tend to run around a lot and most of the time can easily be shot. Nevertheless most important enemy order.
Seek Enemy -> These enemies are very frustrating, as they actually will start their seek merc routine once you enter the sector (you will have very little time to actually set up your team). And the enemies are very precise in finding the mercs (as if the AI actually cheats and knows where you are... it's more a "go to merc position" than an actual "seek mercs"... at least it feels like this most of the time).
Close Patrol -> Enemies will stroll around in a far larger area than on call guys, but basically without much control over where they move).
Far Patrol -> Even bigger area of patrol routes, still pretty much random.
On Guard -> Only a few tiles movement around the initial placement.
Stationary -> Won't move at all.
Random Point Patrol -> Pre-set patrol points which are patrolled as the name suggests in a random order.
Point Patrol -> Pre-set clean patrol route along waypoints. Great for patrols, but will be pretty much useless once an actual attack starts, as these enemies will keep their patrolling routine.
Point patrols and the other random patrols are great at the beginning of combat, but once ~50% or more of the enemies are dead, they look rather stupid (ever noticed enemies that squat around in one place once you managed to fight off the first onslaught? yes, those are all the on guard, close patrol and far patrol guys who reached their area boundaries before actually being able to commit themselves to the heat of battle and will wait there until the actual fight is over... basically sitting ducks).
My idea is to either add certain (maybe even map based) triggers that will change basic orders at some point during a battle (like radio reinforcements called once -> change "close patrol / aggressive" guys to "far patrol / aggressive" or "on call / brave" to "seek enemy / brave"). Of course some enemies should remain on their guard post (maybe add a simple "will change orders" checkbox?)
There's probably a big bunch of possibilities hidden here that could make tactical gameplay even more interesting.
Some ideas for possible triggers that could change enemy basic orders (obviously just from the top of my head and highly up for discussion):
Radio reinforcements called once / obvious gun shots heard (will activate the seek enemy guys, instead of having seek enemy guys zerg rushing you right from the get go)
Radio reinforcements called twice (will activate the on call guys)
Radio reinforcements called three times (will change "far patrol" to "on call", "close patrol" to "far patrol", "on guard" to "close patrol"
X % of enemies killed (could make point patrols break their patrol routine)
Room number x/y/z entered by mercs (could change on guard and stationary guys to a more active stance)
Maybe even something like rally points could be a nice idea if enemies drop down to a certain amount
Obviously those last few campers in a map often are the hardest to deal with, but there are quite several times when it's not only annoying to hunt down the last three guys, but they are also very obviously lame positioned at their area boundaries that you always think, that's not a very realistic end of combat routine. I have this all the time in the initial Omerta battle with like 3-4 guys who always sit behind a fence far away from the starting battle... and it became annoying enough for me to write this request. So... any takers? More need for discussion?
Re: [Feature Request] More Dynamic Enemy Orders[message #344864 is a reply to message #344862]
||Mon, 04 April 2016 16:31 |
Location: Under the Mountain
||Some important things to know about enemy AI:
Each order has roaming range restrictions:
STATIONARY: 0 (5 if he can see enemy or he is under fire)
CLOSEPATROL: 5 (15 if raised alert, 30 if knows enemy position)
POINTPATROL: 10 (20 if raised alert, 40 if known enemy position)
RNDPTPATROL: 10 (20 if raised alert, no limit if knows enemy position)
FARPATROL: 15 (30 if raised alert, no limit if knows enemy position)
ONCALL: 10 (30 if raised alert, no limit if knows enemy position)
SEEKENEMY: no limit
For POINTPATROL and RNDPTPATROL, roaming range is calculated from his next patrol point, for other orders - from initial location.
These limits are different from vanilla values, and they originally were made for small maps.
Vanilla maps (and all good maps made with knowing how AI works) use these limits to make playing more interesting, but this is not the case for 1.13 where everything was changed.
Orders don't stay unchanged, instead the function CheckForChangingOrders is called first when red alert is raised and second when soldier sees enemy (or maybe in some other cases also).
In this function, ONGUARD is changed to CLOSEPATROL, CLOSEPATROL is changed to FARPATROL.
Militia soldiers always change their orders to SEEKENEMY when the red alert is raised (except for snipers).
SNIPER is special order which you cannot select in editor but it's made randomly from stationary soldiers at the time of soldier creation (don't know how exactly it works).
So this means that usually all ONGUARD and CLOSEPATROL orders are changed to FARPATROL, which means there are no limits if soldier knows enemy location.
As for patrol orders, patrol routes are ignored when soldier is in combat and can do something meaningful, but if soldier in red state cannot see/hear enemy, he switches to green state orders and will move to next patrol point.
Roaming range restrictions look stupid when the maps are not made exactly for existing AI, because soldier can stop somewhere in the open terrain and simply wait instead of hiding or helping friends.
How the AI works?
Most important thing is the red AI (when red alert is raised, but this soldier cannot see anybody at current moment).
In this AI, there are 4 main red decisions (this part is called MAIN RED AI):
SEEK - soldier will move to closest reachabe disturbance (enemy or noise)
WATCH - soldier will turn to closest watch point where he knows enemy is and end turn
HELP - soldier will move to closest friend which is under fire or raised alert recently
HIDE - soldier will execute take cover decision which means he will find 'best' position within max 9 tiles, this means he can hide from enemy or run to enemy, depending on his AI morale and taking into account possible cover at spot.
The main difference between orders is the order in which these decisions are checked.
Each possible order has it's weight, and then the order with maximum weight is executed.
For example, SEEKENEMY order will try to SEEK first and only then something else (unless he has really bad morale),
STATIONARY will try to hide/watch.
What is also important, that not only order, but combination of order/attitude is used, so SEEKENEMY/DEFENSIVE has not much sense, and STATIONARY/BRAVESOLO also.
The exact weight calculation is simple:
At the start, all weights are zeroed except bWatchPts which is initialized with maximum watch points for available watch location (can be 1-4).
// modify RED movement tendencies according to morale
case MORALE_HOPELESS: bSeekPts = -99; bHelpPts = -99; bHidePts = +2; bWatchPts = -99; break;
case MORALE_WORRIED: bSeekPts += -2; bHelpPts += 0; bHidePts += +2; bWatchPts += 1; break;
case MORALE_NORMAL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
case MORALE_CONFIDENT: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
case MORALE_FEARLESS: bSeekPts += +1; bHelpPts += 0; bHidePts = -1; bWatchPts += 0; break;
// modify tendencies according to orders
case STATIONARY: bSeekPts += -1; bHelpPts += -1; bHidePts += +1; bWatchPts += +1; break;
case ONGUARD: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; break;
case CLOSEPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
case RNDPTPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
case POINTPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
case FARPATROL: bSeekPts += 0; bHelpPts += 0; bHidePts += 0; bWatchPts += 0; break;
case ONCALL: bSeekPts += 0; bHelpPts += +1; bHidePts += -1; bWatchPts += 0; break;
case SEEKENEMY: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += -1; break;
case SNIPER: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +1; break;
// modify tendencies according to attitude
case DEFENSIVE: bSeekPts += -1; bHelpPts += 0; bHidePts += +2; bWatchPts += +1; break;
case BRAVESOLO: bSeekPts += +1; bHelpPts += -1; bHidePts += -1; bWatchPts += -1; break;
case BRAVEAID: bSeekPts += +1; bHelpPts += +1; bHidePts += -1; bWatchPts += -1; break;
case CUNNINGSOLO: bSeekPts += 1; bHelpPts += -1; bHidePts += +1; bWatchPts += 0; break;
case CUNNINGAID: bSeekPts += 1; bHelpPts += +1; bHidePts += +1; bWatchPts += 0; break;
case AGGRESSIVE: bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
case ATTACKSLAYONLY:bSeekPts += +1; bHelpPts += 0; bHidePts += -1; bWatchPts += 0; break;
There are also some other things that influence weight calculation:
MILITIA team: bSeekPts += -1; bHelpPts += 0; bHidePts += +1; bWatchPts += +0;
If soldier is under fire or in dangerous place, WATCH is penalized by 10
If soldier is in overcrowded place, WATCH is reduced by number of neighbours
If solder has seen enemy recently or is under fire, HELP is penalized by 10 points.
There's also flanking thing: soldier can start flanking if he decides to SEEK and he has orders CUNNINGAID or CUNNINGSOLO (and also BRAVESOLO/BRAVEAID if overcrowded).
So, if we want to know how ONCALL order will work, we should calculate weights for his possible decisions, for example, we have ONCALL/DEFENSIVE:
Most of the time, AI soldiers will have MORALE_FEARLESS AI morale, especially as there are usually much more AI soldiers than player mercs.
Assume, this soldier has no watch points, in this case his bWatchPts = -99
So, for MORALE_FEARLESS we have: bSeekPts = 1; bHelpPts = 0; bHidePts = -1; bWatchPts = -99;
Modify by ONCALL order: bSeekPts = 1; bHelpPts = 1; bHidePts = -2; bWatchPts = -99;
Modify by DEFENSIVE attitude: bSeekPts = 0; bHelpPts = 1; bHidePts = 0; bWatchPts = -98;
So, taking into account the order in which decisions are checked (SEEK/WATCH/HELP/HIDE), this AI with good morale (not wounded or tired or outnumbered by enemy) will try to help (if there are reachable friends in trouble), then seek (if there's reachable disturbance), then watch (if there's watch location) and then hide (if he can find 'best cover' position).
This AI will not try to flank.
If this AI has some watch points (if he has seen enemy at some location 1 time, then bWatchPts will be 1, if he has seen enemy at some location 4 times, bWatchPts will be 4, each turn when there's no enemy at location, it's value is reduced by 1 point, there can be no more than 3 watched locations for each soldier, also soldiers can communicate their watched locations), it's likely that his WATCH decision will win, so AI will check watch decision first (check that he can see this location and he has some cover at his current spot), and only then he will try to help friends and only then SEEK/HIDE.
Hope this can help to understand how tactical AI works.
[Updated on: Mon, 04 April 2016 16:58]
Stable 7609+fix | Stable 7609+AI (r571) | Win8+ fix | Additional content | Youtube channel
Re: [Feature Request] More Dynamic Enemy Orders[message #344867 is a reply to message #344865]
||Mon, 04 April 2016 17:59 |
Location: Under the Mountain
||smeagol wrote on Mon, 04 April 2016 19:17
Thanks for the great and informative explanation. So basically there is already some change in AI orders during a battle? Neat!
Maybe it's possible to extend this as described in initial posting?
Perhaps the main offender is already the "seek enemy" order. I used it for big maps VERY little, due to the annoyance that the enemy starts seeking even if not on RED ALERT (and even worse that the enemy seems to actively cheat while seeking).
There's no cheating in green code (maybe there's some unknown bug? haven't checked that), but SEEKENEMY is the only order which has no roaming limits in green state, so it's the only order which allows AI to find you (except specifically set patrol routes for patrol orders).
It should be possible to limit roaming range for SEEKENEMY to half of the map, for example, and change it to 'no limits' when soldier goes to YELLOW or RED state.
I have this all the time in the initial Omerta battle with like 3-4 guys who always sit behind a fence far away from the starting battle.
This can happen because of bug in pathfinding code - AI cannot find path over fence when doing SEEK or HELP decisions, but can sometimes jump over fence when doing HIDE decision.
Changing orders is possible, also it's possible to alter the roaming range depending on the tactical situation, the main problem is to decide when and how to change it.
I had some ideas for my ja2+AI project, but nothing is implemented in the code yet except that I removed all roaming range restrictions in RED state (except stationary and snipers).
For example, it should be possible to use some counter - each time someone uses radio, counter is increased, or each turn or with each friend killed etc, and we simply add this counter*10 to current roaming range, so the more turns the battle lasts (or the more enemies are killed), the more of them will come to you.
Another possible solution is to restrict roaming for SEEKING but allow helping at any range, if friend in trouble has less friends nearby than recently seen enemies, or has his AI morale less than MORALE_FEARLESS.
There are many possibilities here, but I have no clear picture of how to make it best yet.
Stable 7609+fix | Stable 7609+AI (r571) | Win8+ fix | Additional content | Youtube channel
Current Time: Wed Dec 13 22:50:01 EET 2017
Total time taken to generate the page: 0.00854 seconds