Advertisement

Final Fantasy Style Combat AI

Started by January 26, 2010 07:19 AM
10 comments, last by Danny84 14 years, 9 months ago
Hi everybody, I'm not totally new to AI coding, but still not that expert... I want to put together a little demo where two AI controlled character parties fight against each other in a FF-style Combat (NOT tactics). I suppose I should do something as: - For each possible move (action-target pair) compute a score - Do the move with highest score Obviously the difficult part is finding a way to actually score these moves... In my game I have the following options: - Attack with weapon (physical damage to enemy) - Defend (less physical damage from the next received attack) - Use Spell (different type of spells do exist, attack ones and defensive ones) I'm just not sure about the heuristics to use and I was wondering if there's a tutorial on the net about this topic... Thanks in advance
It seems so obvious that it might never occur to you, but I once tried this as an experiment and the best method seemed to be to just be random. If, like FF, each bad guy/character has a limited subset of things they can do, things actually look pretty realistic. That doesn't mean you shouldn't however put weights on each type of move depending on the character (ie self-cure happens 50% of time, attack 25%, defend happens 25% for character "Wussy") to put it simply. But this actually works REALLY well.

-Scott
Advertisement
Well I trust you, really.
But, just to do something a little bit more clever, I could add some complexity to the way weights are computed. For example the "urgency" for healing would be a function of target HP and so on... this could also be generalized: action "urgency" would always be a function of the target's stat it influences. Once all urgencies are computed (with very simple methods, It's just a demo anyway) I could add some randomness and let the AI do the most urgent task.
I'm planning something like this:
Somewhere I define some ids for all "effects" an action can have, like: heal, damage, defup, defdown, poison etc... each character, then, has a method like this one:

float EvaluateUrgency(Action action, Character doer)

that returns a number in the interval [-1,1] where -1 means you really don't want to be the target of this action (being attacked the example) and 1 means you really want to be it (being healed for a character with low HP). AI will just do the most urgent action.

Do you think this will be too complex to implement?
Seems like you are over thinking it.

Final Fantasy 12 and Dragon Age use an easy system based on priorities. It's even user configurable on the fly, and very versatile. FF12 calls it the Gambit system. You could read up on it and implement something similar.

It's basically a list for every character. The lists start off small, but as a character levels up, they get longer lists to implement more commands.

Every item in the list is a boolean condition/reaction pair.

When any character has their turn come up, they iterate through their list, top to bottom, and act on the first condition test that returns true.

A list might be

Tank:
Condition: Self: Health < 50% Action: Use health potion
Condition: Enemy: Highest Armor Action: Some armor breaking attack
Condition: Enemy: Any Action: Attack

Mage / Healer / Supporter
Condition: Party Avg Health < 25% Action: Party healing command
Condition: Party: Any: Health < 25% Action: Cure
Condition: Party: Any: Slow Action: Cast haste
Condition: Enemy: Any Action: Attack

Mage's bodyguard
Condition: Enemy: Targeting Mage Action: Attack (or draw aggression)
...
...

Rogue / Assassin
Condition: Self: Health < 50% Action: Use health potion
Condition: Enemy: Targeting Party Member Action: Attack (gets critical here)
Condition: Enemy: Has inventory Action: Steal
Condition: Enemy: Any Action: Attack

You can implement the list logic once, and then just keep adding new condition checks for anything relevant to your game.
I must admit this looks like the best way of doing something that acts smart without being too complex to implement... tnx.
I suppose this pairs (condition/action) could be pre-built and reused for each character. Since I'm planning to have random battles I could just define different character classes and add pairs to their action-list accodingly. I could also take into account stats during this list generation process.

Anyway You give me something to start with, tnx a lot :)

[Edited by - Danny84 on January 26, 2010 9:38:52 AM]
I actually haven't played any of the newer FFs. Wouldn't a system like this make everything REALLY predictable?? Or is it more geared around non-interaction once the battle actually begins, but strategy before-hand? Won't your demo always display the same fight w/same moves over and over?
Advertisement
Well... actually I'm planning to randomly generate party members each time... also action priorities (list-sorting) could depend on characters stats. Anyway, It's true that this system does NOT take into account enemy strength/weaknesses but also random action picking does not. Maybe a dynamic-priority of actions should be considered... This will, anyway, be as complex as computing action-urgencies (as said before), so I really do not know how to get around this...

Help...!
Quote: Original post by popsoftheyear
I actually haven't played any of the newer FFs. Wouldn't a system like this make everything REALLY predictable??
No. It works very well. So well, that Dragon Age completely ripped it off (in a positive sense). It works less well in DA then it did in FF12 because the combat engine is a mess, but it's still fun, and you can spend hours screwing around with it.

The system in FF12 is like a fully functional programming language. Every possible combat condition is available, and every possible action is there too.

---

Danny, the lists are static. They aren't meant to be randomly generated. You set them once, and leave them be until you need something else.

Looking at the list above, when it's the tank's turn take action, he iterates through his list,

Condition: Self: Health < 50% Action: Use health potion

So, if his health is less than 50%, he'll use a potion if available, and that will be his turn, if he isn't under 50%, or no potions, he keeps iterating:

Condition: Enemy: High Armor Action: Some armor breaking attack

Is there any enemy that has very high armor? Use the special ability that lowers his defense, like some blunt bash, so other members can penetrate his denfense... If there isn't a valid target, or not enough ability points to use the command, keep iterating down the list

Condition: Enemy: Any Action: Attack

So this is the default command. He just attacks any enemy.

And your AI for everyone at their turn ends up being a simple linked list iteration. Instead of custom AI for every class with tons of redundant code.

A condition is simply an overloaded function that returns a pointer or a reference to a target. Target is null by default, and if it's not null after the test function runs, then it's valid...

eg:

CombatTarget PartyMemberHealthLessThan50(..)
{
Loop through all party members, and return the first who has less than 50%
else, return null result
}

Then everyone just loops through their list.

foreach Tactic t in CombatTactics
{
Target = TestConditionFunction[t]();
if (Target != null)
{
DoCondictionAction(Target);
return;
}
}

Every class should have their default tactics set up. And then you can customize new ones from your templates, or edit them on the fly as the game runs.

Your battles will still play out randomly. Different people will win the initiative roll and get the first strike. Different dmg amounts will be done. Turns will occur in different orders depending on whatever. People will resist or not resist status changing effects or magics, etc...

The randomness of RPG combat comes from the rolls of the dice, and not the AI. A healer will always attempt to heal as first priority, and fall back to attacking if no healing is needed that turn. A supporter will always buff or do crowd control. A tank will always look to attack for big damage, etc...

And anytime you want to add on top of your AI, it's as simple as adding new conditional functions, and adding those conditions to the tactic's lists, as opposed to cracking open a ton of AI scripts and hacking their use in.
I realize It now.
Just one thing: I'm assuming each condition/action pair only suggest the "type" of action to do... For example: an action like "Attack" means that the character wants to deal damage to enemy and the best way of doing It could be a melee attack for a warrior or a powerfull spell for a wizard... isn't it?
By attack I meant attack with currently equiped weapon. It would be the default action if there was nothing higher priority to do.

A high level spell is it's own command.

Condition: Enemy ~ Any
Action: Cast Spell ~ Fire

The actions should be interpreted at face value.

Every condition is a test for a valid target. The action to cast a high power should be attached to a conditional that confirms a valid target or situation for that spell!

The only time something should be left to interpretation by the command, is when you use something like a healing potion or magic potion. Use only the smallest that will get the job done.

If you can get your hands on ff12 for cheap, I highly recommend it.

This topic is closed to new replies.

Advertisement