Spurred on by some "interesting" results playing Native Expansion mod, I started poking around in game_event_simulate_battle to find out what's up. Utilizing the search feature of this board, there's been a couple discussions already but I've got further questions that don't seem to be addressed. I'll start with the questions first so people don't get bored of reading my drivel and follow with my current understanding of how the game resolves combat automagically.
Questions
[list type=decimal]
[*]Once all the strength determination is done and it's time for bodies to hit the floor, it looks like the normal thing to do is to call inflict_casualties_to_party_group which is assumedly not a script and hardcoded somewhere. Does anyone know what the domain is for the strength inputs to this function?
[*]Also in inflict_casualties_to_party_group: it takes the opposing force's strength as an input as well as "this" party's ID. Does it call script_party_calculate_strength to calculate the strength or is there another script/internal function that handles it?
[*]What's the most reasonable/efficient way to test any hackeries I might make to these funcitons?
[/list]
Discussion
The basic code does something like this:
- calculate the strengths of both sides
- do some normalization
- call inflict_casualties_to_party_group for both sides
- rinse & repeat until someone bails or is dead
The strength calculation goes something like this:
for each unit stack:
get the level of the stack and add 12 to it
square the result
integer divide by 100
multiply by the number of fit troops in the stack
add this to the force's total strength
So if we look through some random examples:
- a level 1 farmer has strength 1
- a level 10 footman has strength 4
- a level 20 knight has strength 10
- a level 30 super-elite troop has strength 17 (note that Native doesn't seem to have troops over L2
The normalization step divides each team's strength by 20 and clamps it to [1...50]. With a usable force strength range of roughly 20 to 1000.
The Plot Thicks!
Heading backward from the normalization step and factoring in some test numbers, it starts to become clear why the auto-resolver gives us some wacky results.
If you have a group of 10 farmers vs. one Swadian Knight (L25, strength 13) you end up resolving one group of 10 guys with strength 1 and one group of one guy with strength 1. Naturally, the peasants typically win this even though our in-game experience tells us otherwise.
Worse yet, if you have a big army of really good troops and fight a bigger army of really poor troops, you can sometimes get strange results. A group of 100 Huscarls (L28, strength 16) has a raw strength score of 1600 which gets divided by 20 and then clamped to 50. A group of 500 Vaegir Recruits (L4, strength 2) with raw strength score 1000 has the same strength of 50. Result: bloodbath for the Huscarls even though our in-game experience again tells us otherwise.
Closing Thoughts
What I'll probably do is change the strength calculation and then change the normalization. It wouldn't be terribly difficult to find (or assign) an armor/weapon/whatever value to a troop and factor that into their individual strengths. This would better capture the multiplicative nature of troop level (higher level troops->more hitpoints->more damage output->better weapons->better armor means more staying power->yadda yadda). Changing the normalization is similarly easy. I can almost trivially keep the [1...50] range by scaling the strength of forces relative to each other if their unnormalized strengths are out of bounds.
This would also open up some fun opportunities like:
- If I have the Talisman of AssKicking, my kingdom does better in sieges.
- I can have werewolf patrols that are very Bruce Banner during the day and super Hulk at night (though, for some reason, auto-battles don't happen at night).
- Actually using the auto-resolver for battles I'm leading for those pesky 3 guys that always seem to be at the end of every big fight rather than running them down and putting arrows into them.
Assuming nothing blows up given my penchant for hackery, I suspect I'll have some results to spam in the future.
Questions
[list type=decimal]
[*]Once all the strength determination is done and it's time for bodies to hit the floor, it looks like the normal thing to do is to call inflict_casualties_to_party_group which is assumedly not a script and hardcoded somewhere. Does anyone know what the domain is for the strength inputs to this function?
[*]Also in inflict_casualties_to_party_group: it takes the opposing force's strength as an input as well as "this" party's ID. Does it call script_party_calculate_strength to calculate the strength or is there another script/internal function that handles it?
[*]What's the most reasonable/efficient way to test any hackeries I might make to these funcitons?
[/list]
Discussion
The basic code does something like this:
- calculate the strengths of both sides
- do some normalization
- call inflict_casualties_to_party_group for both sides
- rinse & repeat until someone bails or is dead
The strength calculation goes something like this:
for each unit stack:
get the level of the stack and add 12 to it
square the result
integer divide by 100
multiply by the number of fit troops in the stack
add this to the force's total strength
So if we look through some random examples:
- a level 1 farmer has strength 1
- a level 10 footman has strength 4
- a level 20 knight has strength 10
- a level 30 super-elite troop has strength 17 (note that Native doesn't seem to have troops over L2
The normalization step divides each team's strength by 20 and clamps it to [1...50]. With a usable force strength range of roughly 20 to 1000.
The Plot Thicks!
Heading backward from the normalization step and factoring in some test numbers, it starts to become clear why the auto-resolver gives us some wacky results.
If you have a group of 10 farmers vs. one Swadian Knight (L25, strength 13) you end up resolving one group of 10 guys with strength 1 and one group of one guy with strength 1. Naturally, the peasants typically win this even though our in-game experience tells us otherwise.
Worse yet, if you have a big army of really good troops and fight a bigger army of really poor troops, you can sometimes get strange results. A group of 100 Huscarls (L28, strength 16) has a raw strength score of 1600 which gets divided by 20 and then clamped to 50. A group of 500 Vaegir Recruits (L4, strength 2) with raw strength score 1000 has the same strength of 50. Result: bloodbath for the Huscarls even though our in-game experience again tells us otherwise.
Closing Thoughts
What I'll probably do is change the strength calculation and then change the normalization. It wouldn't be terribly difficult to find (or assign) an armor/weapon/whatever value to a troop and factor that into their individual strengths. This would better capture the multiplicative nature of troop level (higher level troops->more hitpoints->more damage output->better weapons->better armor means more staying power->yadda yadda). Changing the normalization is similarly easy. I can almost trivially keep the [1...50] range by scaling the strength of forces relative to each other if their unnormalized strengths are out of bounds.
This would also open up some fun opportunities like:
- If I have the Talisman of AssKicking, my kingdom does better in sieges.
- I can have werewolf patrols that are very Bruce Banner during the day and super Hulk at night (though, for some reason, auto-battles don't happen at night).
- Actually using the auto-resolver for battles I'm leading for those pesky 3 guys that always seem to be at the end of every big fight rather than running them down and putting arrows into them.
Assuming nothing blows up given my penchant for hackery, I suspect I'll have some results to spam in the future.