• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Best way to calculate fighting power?

Status
Not open for further replies.
Level 13
Joined
Mar 29, 2012
Messages
530
I've been thinking about it.
So, I have a hero unit that will decide whether or not he will engage a group of hostile units. How do you think is the best way to calculate all the things needed for the hero to engage in a fight?

Calculate time to kill each enemy units? time they needed to kill the hero?
Total attack damage, armor, attack speed, (even movement speed)?

I just started to approach this by learning about damage reduction to try to calculate estimated death time of any units in combat, but I need your opinion. :(

I might need detailed info to approach this..
 
Level 12
Joined
Jun 15, 2016
Messages
472
That depends on what data you can extract by triggers from each unit. A possible way to do it is by some sort of point system which will track and manage a hero's "fitness to engage" an enemy group. The system will increment certain amount of points for factors which will improve the hero's ability to fight, and decrement for factors leading to unfavorable results. Here are a few examples for factors:

- A hero's relative health & mana. But note those factors are not linear, as the difference between engaging with 70% hp and 40% is very big. You could put it in a formula like this hp_factor = total_hp*(hp_percentage**2)/10 - 30, and some similar formula for mana.
- The amount of available units in the hero's vicinity, can be further differentiated by type and percentage hp/mana.
- Enemy units present in visible areas, as well as enemy heroes along with hp and mana percentage, etc.
- Metrics like possible DPS dealt to the hero and such would be good, too, although I'm sure how you would collect them mid-game.

To put it generally, Allied units and heroes (including the hero in question) close by will increase engagement fitness if they can actually fight, if not they should reduce fitness. The same can be applied to visible enemy units and heroes, but with inverse effects on fitness. In the end, whether to engage or not will be decided by comparing the total fitness to some threshold number. This threshold can be played with according to AI difficulty, hero playing style, game state and any other metric you want.

This should generate a system smart enough for heroes to choose engagement with higher probability of success, although I think creating it might require a lot of fussing with fitness formulas and threshold tests. Either way that's an interesting topic, please keep updating.
 
Level 13
Joined
Mar 29, 2012
Messages
530
Thank you for the idea.
I've never try doing with 'threshold' cuz I don't know what exactly a threshold is.. but I'll try to learn more.

Sorry for the late reply, I'm kinda busy but I still working with these AI stuff. I've made some deep coding on my AI system so I think I'll regret if I don't finish it. I'll be back to WE as soon as I got to my bedroom.
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
Threshold is just a limit or value at which you do something. He's saying threshold because he suggests literally computing a number and then comparing that to another number (the threshold) that determines if they're ready to fight the enemies. For example:

JASS:
globals
    real THRESH = 1.25 //only attacks if ally strength is 25% stronger than enemy strength
endglobals

function shouldFight ...
    local real allyPwr = 0.00
    local real enemyPwr = 0.00

    set allyPwr = allyPwr + HERO_WEIGHT*LEVEL_FACTOR*HERO_LEVEL
    set allyPwr = allyPwr + ALLY_WEIGHT*NUM_ALLIES
    set allyPwr = allyPwr + HEALING_WEIGHT*HEALING_FACTOR //weighting the importance of each factor will be... important
    set allyPwr = allyPwr + ...
    // ...
    set enemyPwr = enemyPwr + LEVEL_WEIGHT*NUM_CREEPS*AVG_CREEP_LEVEL
    set enemyPwr = enemyPwr + ITEM_WEIGHT*NUM_OFFENSIVE_ITEMS
    set enemyPwr = enemyPwr + ABILITY_FACTOR*NUM_OFFENSIVE_ABILITIES
    set enemyPwr = enemyPwr + ...

   return allyPwr/enemyPwr >= THRESH
endfunction
 
Level 13
Joined
Mar 29, 2012
Messages
530
Threshold is just a limit or value at which you do something. He's saying threshold because he suggests literally computing a number and then comparing that to another number (the threshold) that determines if they're ready to fight the enemies. For example:

JASS:
globals
    real THRESH = 1.25 //only attacks if ally strength is 25% stronger than enemy strength
endglobals

function shouldFight ...
    local real allyPwr = 0.00
    local real enemyPwr = 0.00

    set allyPwr = allyPwr + HERO_WEIGHT*LEVEL_FACTOR*HERO_LEVEL
    set allyPwr = allyPwr + ALLY_WEIGHT*NUM_ALLIES
    set allyPwr = allyPwr + HEALING_WEIGHT*HEALING_FACTOR //weighting the importance of each factor will be... important
    set allyPwr = allyPwr + ...
    // ...
    set enemyPwr = enemyPwr + LEVEL_WEIGHT*NUM_CREEPS*AVG_CREEP_LEVEL
    set enemyPwr = enemyPwr + ITEM_WEIGHT*NUM_OFFENSIVE_ITEMS
    set enemyPwr = enemyPwr + ABILITY_FACTOR*NUM_OFFENSIVE_ABILITIES
    set enemyPwr = enemyPwr + ...

   return allyPwr/enemyPwr >= THRESH
endfunction
I see, thanks.. that's very useful example.

- A hero's relative health & mana. But note those factors are not linear, as the difference between engaging with 70% hp and 40% is very big. You could put it in a formula like this hp_factor = total_hp*(hp_percentage**2)/10 - 30, and some similar formula for mana.
I don't really understand the formula :/
Why and how can that formula formulates as a useful factor?

Sorry if you found something confusing as english isn't my native language. *_*
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
I don't really understand the formula :/
Why and how can that formula formulates as a useful factor?
He's trying to resolve these two problems.

If you JUST look at each unit's total health percentage, a unit with a small actual amount of health (say 150) might think it can take on another unit with a very large amount of health (say 5000) simply because they both have 100% hp. Alternatively a 5k hp unit at 10% how actually has 500 health and should be able to defeat the 150hp hero at 100% hp.

So instead of comparing (unit a % hp) and (unit b % hp) you also multiply them by some other factor to scale up the comparison. What Nowow suggested is (unit a total hp)*(unit a % hp), which is actually just (unit a current hp) in this scenario, but you could use a different factor in front of it instwad. His concern is real and you gotta consider that because maybe you don't scale it by total hp if there's a better way. You'll have to find out.


What Nowow is saying about 40% vs 80% (he used 70, I think 80 is a better example) is a little nuanced. Once you get to low enough health health spells, strong attacks, and items start being able to kill the hero outright so when it reaches that low hp amount the hero becomes a big target. Players tend to order their ranged units to pick off weakend targets so the hero probably doesn't last long at that point.

Imagine the hero came into the fight at 80% hp, fights for 30 seconds, and dies. If it had come in at 40% hp it might only have lived 10 seconds (not the full 15s, which would be half as long as the hero with twice as much health). If he had come with 100% hp maybe he would have lived another 5 seconds (rather than 30*(100-80)/80 = 7.5s if it was proportional).

It won't always be that way but in general the unit having half the hp % of another unit probably doesn't even live half as long on the battlefield.
 
Level 13
Joined
Oct 12, 2016
Messages
769
- Metrics like possible DPS dealt to the hero and such would be good, too, although I'm sure how you would collect them mid-game.
This can easily be determined by linear level scaling for creeps, units or heroes, and making it all standardized with variations for specialized purposes.
 
Last edited:
Level 13
Joined
Mar 29, 2012
Messages
530
He's trying to resolve these two problems.

If you JUST look at each unit's total health percentage, a unit with a small actual amount of health (say 150) might think it can take on another unit with a very large amount of health (say 5000) simply because they both have 100% hp. Alternatively a 5k hp unit at 10% how actually has 500 health and should be able to defeat the 150hp hero at 100% hp.

So instead of comparing (unit a % hp) and (unit b % hp) you also multiply them by some other factor to scale up the comparison. What Nowow suggested is (unit a total hp)*(unit a % hp), which is actually just (unit a current hp) in this scenario, but you could use a different factor in front of it instwad. His concern is real and you gotta consider that because maybe you don't scale it by total hp if there's a better way. You'll have to find out.


What Nowow is saying about 40% vs 80% (he used 70, I think 80 is a better example) is a little nuanced. Once you get to low enough health health spells, strong attacks, and items start being able to kill the hero outright so when it reaches that low hp amount the hero becomes a big target. Players tend to order their ranged units to pick off weakend targets so the hero probably doesn't last long at that point.

Imagine the hero came into the fight at 80% hp, fights for 30 seconds, and dies. If it had come in at 40% hp it might only have lived 10 seconds (not the full 15s, which would be half as long as the hero with twice as much health). If he had come with 100% hp maybe he would have lived another 5 seconds (rather than 30*(100-80)/80 = 7.5s if it was proportional).

It won't always be that way but in general the unit having half the hp % of another unit probably doesn't even live half as long on the battlefield.
Okay, I think I'm getting it.

This can easily be determined by linear level scaling for creeps, units or heroes, and making it all standardized with variations for specialized purposes.
Linear level scaling?
 
Level 13
Joined
Oct 12, 2016
Messages
769
Linear level scaling?
For example:
lvl 1 units have 100 hp, and deal 3 - 5 damage.
lvl 2 units have 150 hp, and deal 5 - 7 damage.
lvl 3 units have 200 hp, and deal 7 - 9 damage.

Something like that, so that a unit's level can accurately indicate its power rating in calculations.
Of course you may have some units that are ranged, some that are more tanky melee, some with spells, etc. for variation.
You'll just have to weight those differently with some coefficient value.
A rule of thumb is that a lvl 2 should beat a lvl 1 unit, but two lvl 1 units should beat a lvl 2 unit (with some exceptions, like unit counters).
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,199
On top of health based "fitness" or threat one needs a per unit type value, possibly modified by the current life percentage. This is because some units might have low health but powerful abilities like the Undead Necromancer which can summon skeletons.

A step above that would be to factor in actual army compositions. So for example if the player has a largely air army and the enemy team has a lot of Siege Engines with upgrades then each siege engine should be worth more threat than if the player had a largely land army due to the Air Barrage mechanics of Siege Engines making them hard counter air.
 
Level 21
Joined
Mar 27, 2012
Messages
3,232
If the units are simple autoattackers in a 1v1 fight you can calculate the time it takes for one to kill the other.
Just take the health of defender and divide it by DPS of attacker, then you get the time in seconds. Do the same thing in reverse and you can already compare the results to know who will win. Since you know the result in time, you can make a unit engage only if the result is overwhelming enough.
 
Level 39
Joined
Feb 27, 2007
Messages
5,016
If the units are simple autoattackers in a 1v1 fight you can calculate the time it takes for one to kill the other.
Just take the health of defender and divide it by DPS of attacker, then you get the time in seconds. Do the same thing in reverse and you can already compare the results to know who will win. Since you know the result in time, you can make a unit engage only if the result is overwhelming enough.
Good idea, but to show how this can be broken (ignoring armor for examples):
  1. Footman with 100 hp, 50 damage per attack, 1 attack per second vs siege tank 100 hp 200 damage per attack, 1 attack per 5 seconds. Footman sees time-to-kill of 2 seconds vs siege's 2.5, but since siege damage is frontloaded into the first attack it will actually win on that attack... and then wait around for 5 seconds.
  2. footman with 100 hp, 50 damage per attack, 1 attack per second vs ranger 100 hp, 50 damage per attack, 1 attack per second. Same TTK, but archer wins because it's ranged and gets its attacks in first.
  3. Units with 1 hp, ranged attacks, very high dps, very high attack speed. Those would likely seem 'easy' to kill for most forces but they would actually annihilate.
I do, however, think that if you compared (total HP of force 1)/(total DPS of force 2) vs (total HP of force 2)/(total DPS of force 1) that might be a pretty good estimate of how the battle might go. Something tells me there should be an exponential in there because the strength of each force is decaying over time. A slightly stronger force that preserves slightly more units earlier on will be able to kill more and more and slightly more enemy units over time, like a lane autopushing in LoL.
 
Last edited:
Level 13
Joined
Mar 29, 2012
Messages
530
Damage Detection?
No, it cannot return directly as what is written on the game interface (base and bonus).

So the following does not factor in bonuses?
JASS:
native BlzGetUnitBaseDamage takes unit whichUnit,integer weaponIndex returns integer
They only return the data that we input from Object Editor I guess. Just like the new collision function.
 
Level 6
Joined
Jan 11, 2008
Messages
176
It might be easier to set up a baseline and estimate upon that whether or not the threshold to engage, disengage or hold position(can be patrolling position) is needed.

if its for regular melee units you could cheat around a bit and base it of the full building cost of the armies in x area of influence on both sides and compare them. It's less elegant but it might give you a good starting point. You could also consider using information that a player does not have the player won't know. unless you tell the player that your AI ally doesn't engage because the enemy has more units in the unexplored vicinity. He'll likely never notice just like how most people don't notice that melee AI insane bots have increased income.

There are quite a couple of game design cheats people never noticed. In doom players actually had 110% health where the last 10% was showed as only being 1 health. To give the player more experiences that he survived with only 1 health.
Ratchet and clank would trip when the player would try to go towards an area that hadn't full loaded yet.
 
Last edited:
Level 6
Joined
Jan 11, 2008
Messages
176
Holy shit I never knew that!
Jennifer Scheurle on Twitter
there's a huge thread dedicated to these things.

Which also reminded me of the Alien xenomorph trick that might be another idea to use for OP.
Dump all the available data of enemy unit locations in 1 mind. And have that mind give hints to your AI. It might make it a lot easier for you to build your rules of engagement.
 
Level 8
Joined
Mar 19, 2017
Messages
248
There is currently no way to detect damage before reduction, except if all the reductions are done by you.

The other clunky but possible way, is to centralize bonus attack and armor, ie. green values, with BonusMod library or another similar. Why? because those libraries have "GetUnitBonus" functions. Retrieving attack damage becomes white (damage functions, don't know if on heroes it accounts for primary attribute bonus damage tho, in which case you also count that stat) plus green (get bonus custom functions). In regards of armor you don't even need such mechanism, as the new get armor function returns total armor (white plus green) except in one, functional case. The method is still clunky because, if you're using the also clunky WC3 armor type system (ie. siege v/s fortified) you will need to take unto account those previous damage reduction percentages. Positive and Negative armor formulas then have to be taken unto account to get your ending damage reduction percentage. But all this armor type data is always modifable and accessible (game constants).
With all this data, attack and armor, you can safely get damage "done" (not really done). The tradeoff is that all "green" values have to use the AddUnitBonus functions. This specially terrible for auras i guess, as those are a staple among hero abilities, and you will need to trigger them yourself.

For the problem at hand, a not so ambitious way could be, 1. creating and determining an absolute fighting of a particular hero ("engaging unit"), based primarily on it's level, skills leveled and items acquired (all these easily accessible events), basically an integer attached to each unit that is engagable, and 2. determining it's relative fightning power against a particular unit. If engagable units are regular creeps that don't level or have dynamic effectiveness, then this value could be just another integer, based on the unit type id of the creep (rawcode) measured against the unit type id of the hero. A hashtable can be used here. To determine if engaging proceeds or not then relative value is substracted from the absolute fighting power of the engaging unit, ie.
1. Antonidas (rawcode of 'AAAA') has an absolute fighting power of 1000 (i would use an unit indexer here plus a regular integer array, IE. FIGHTING_POWER[GetUnitId(Antonidas)]), and starts fighting against 2 bandits.
2. Bandits (rawcode of 'BBBB') and Antonidas have a relative fighting power of 10 (Hashtable['AAAA']['BBBB'] = 10), so the overall engaging power of Antonidas, it's own relative fighting power is 980.
3. Antonidas proceeds to beat the crap out of the bandits because 980 means direct engage.

Cons: this a very mechanic way of achieving this, data heavy and arbitrary. But with experience and testing on it's side, it can be tinkered well enough.
 
Status
Not open for further replies.
Top