Battlefield AI - it'll be a bit different.
In NWN, we did the AI almost entirely in script, with the engine providing the "heavy lifting" of things like pathfinding, LoS, etc. I guess I should explain that NWN was, at the heart of things, truly turn-based, but it had the "end turn" button hit every six seconds.
The model we used was a heartbeat script that ran during every turn coupled with an Observer pattern. The engine served as the Observer, and had a list of registered dependants (the critters/mobs) and when certain things happened (they got attacked, they perceived a player, they got blocked) it would fire off an event activation tied to the creature.
In order to keep things fast, we precompiled the scripts down and stored them in memory. You can do the same with python, the bytecode compiles to .pyc/.pyo files.
Beyond the basic model, what we did specifically was relied very heavily on the idea that we would be facing groups of enemies, and that the AI should prioritize certain foes, "focus fire," and determine who the individual monster would be best off attacking. If it had a high attack bonus, it would go after the unit that just dealt it the most damage. If it had a low attack bonus, it would swarm the weakest foe present.
The algorithims were designed to avoid waste. The monsters would not fight those who they could barely hit, but who did little damage (i.e, if there's a low damage tank, just ignore him and go straight for 300+ damage mage fireballing us, or the heavy-hitter who kills one of us a turn) which meant tanks had to be sure to "hold the line," if they wanted to be super effective. Just like a real player, the AI knew to "kill the LRMs and carriers first, forget the sentinels."
That was the base model. We had heavily-developed algorithims to handle being blocked, to determine the source of blockage and deal with it (like doors or fordable water or PCs), so the AI would be able to access a really good, intelligent function to deal with difficult situations. When the "tank" units blocked you, the AI knew that it was time to pick one tank and start smashing it.
We actually went a lot further than this. It was important that our AI did not cheat, and only had access to the same informed guesses the players would have.
We had one unit set to be a "commander," and based on that commander's intelligence/wisdom score, he would have things available to him. The commander would assess strengths and weaknesses, make skill rolls, etc, and bark out orders to the individual units. The non-commander units would defer to his request except in special circumstances.
The commander knew everything there was to know about his own units, and unlike the individual AI which was weighted toward self-preservation and personal efficiency, the commander was all about weighing the multiples. If there was a ton of damage being dished out by a unit he knows is weak (his perception event has recorded that even his weakest unit has no problem doing damage to this unit) he'll prioritize them. If there's tanks holding the line, he'll prioritize destroying them. He'll deliberately sacrafice units to buy time, to protect his mages, to protect himself, and to confuse the enemy. He'll keep the mages invisible until their AoEs will be most effective, he'll have a group of rogues stay hidden, sneak past the tanks and wail on the player all at once, dealing masive damage.
This was all pretty neat because it meant two things: the AI got a lot more nastier, but could still fall back to individual mob-rules. It also meant that the players had a target that killing would result in a devastating blow.
We also wanted to reflect real intelligence in what units would do. Taking the blocking algorithim for example: animals with low intelligence scores don't know how to open doors. Human units could usually figure out opening a door. Smart animals might realize "bash door down," and stupid animals might just go... "dur... peekaboo, where'd it go?" This went for dealing with invisibility, everything where we realized "wait, should the AI know what to do in this instance?"
And we wanted to make morale not an artificial "thing" but something that was right there in the game engine, a consequence of events that was not just an on-off switch. Every time an AI got damaged, he lost morale individually. He would gain morale if he was "awesome" and just killed an enemy, or he if did a ton of damage, or when the bard made a rallying cry, or when the foes around him were just dying.
Here's the algoritihm we used for calulcating the morale penalty for taking damage.
- if(iDamage > i0)
- if(!GetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER))
- int iToDamage = GetBoundriedAIInteger(AI_DAMAGE_AT_ONCE_FOR_MORALE_PENALTY, GetMaxHitPoints()/i6, GetMaxHitPoints(), i1);
- int iPenalty = GetBoundriedAIInteger(AI_DAMAGE_AT_ONCE_PENALTY, i6, i50, i1);
- if(iDamage > iToDamage)
- // 61: "[Damaged] Morale Penalty for 600 seconds [Penalty]" + IntToString(iPenalty)
- DebugActionSpeakByInt(61, OBJECT_INVALID, iPenalty);
- // Apply penalty
- SetMoralePenalty(iPenalty, 300.0);
It'd have a specific duration, so damage that came hard and fast was frightening, but just taking periodic damage over a long time didn't fell that bad.
Your leader fleeing, your neighbor fleeing - all could trigger more morale penalties. Spells of course affected morale.