Improving the auto resolve system

Users who are viewing this thread


Sergeant Knight
I've made this for my Auxilarii variant of Sword of Damocles, but as it may interest other modders to adapt or developp more this system to improve realism, I signal it here.

I've make a script to determine terrain for AI vs AI battles (may need some changes on modded maps with lot of reliefs as my "mountain" terrain is based on the z coordinate and altitude is rather low in Calradia) and return a troop slot number for the appropriate bonus (need of course to add these troops slots in constants, with the good numbers).

Then I've modified the script evaluating strength to make it use this bonus (the bonus being in a troop slot, and the number of this slot being send as a script parameter - also needs to add a call to the determine terrain script and add the terrain parameter to the calls to the "evaluate strength script" in the "event_simulate_battle" one).

The way I've given bonuses to all units isn't perfect as I wanted to make well geared troops as elite in autoresolve as they are in 3d combat, I had to make a long (try_else) script to check each troop id (adapting it need to cut the references to SoD or auxilarii mod specific units), and give a note to their gear myself then formulaes to determine the bonuses on each terrain based on gear. But a more generic system should be easier to do, ie giving the same terrain bonuses to all mounted troops.


Other interesting tricks  :
- changing AI lords optimism (how they evaluate their own strength, key to make them more or less agressive), for that you need to add a multiplier to ":eek:ur_strength" everywhere it's used in AI calculations scripts (and the process siege simple trigger), but not in the evaluate strength script (as this strength is stored in the cached_strength party slot and used when a lord evaluate the strength of ennemy lords) ;
-  an easy to use method for making AI lords troops quality improve other time (need to add factions with different reinforcements templates then you can use a trigger changing the value of  the "troop_original_faction" slot of kingdom heroes to make them get better templates at regular intervals).
We had a lively discussion not long ago about this very topic.  If you haven't already done so, you can read all about it here:,58962.0.html.

As I spend more time digging through the autoresolve code (in all, what, five of its incarnations?) I'm noticing more and more things that are...questionable.  As a quick for instance, I spent most of Sunday morning debugging why autoresolving a siege battle didn't give the right strength values.  It turns out that this particular function didn't take into account any parties present in the city being sieged when doing strength and "how many defenders are left" calculations.  Curious!  (I'd post details about which function that is specifically but I'm at work waiting on a build and don't have the code with me.)

I have a fix but not a pretty one that I'd be happy to share if people are interested once I get it cleaned up.  This evening I plan on digging through more of the other autoresolve incarnations to see if they share the same deficiencies as the siege one.

More and more I'm seeing other modders and myself running up against the module system's biggest limitation: no way to access all of the information present in the various lists.

e.g. you can't evaluate all of the information about an item - most of it is only available to the engine, and it makes no effort to allow the module system access to it.  Same thing for troops & party templates - there are very limited amounts of information one can access - making these ridiculous try/else/end constructs and customizations for every mod out there :sad:.  Yuk!

I wonder if anyone at TaleWorlds is aware of how limiting this is?

EDIT: Rubik's innovative use of python to generate compile-time scripts to transfer that information into slots is a brilliant, and comprehensive solution to this problem.  We just need to come up with an algorithm that makes better sense, and I'll even volunteer to write the code.
Kt0, your fix would definetely improve the game. I'd be glad if you could share it with us.
Auto-Resolve?  pfft.  I quit using that useless 'feature' when 6 looters killed 7 of my 42 swadian knights.
Sadly, I haven't gotten back to it in a while.  The biggest issue is that there are six (!) places where autoresolve is done.  Fixing all of them and more importantly testing all of them is difficult at best.  It's on my list but I don't know when I'll get to it.
Frodogorn said:
Only cowards and whoremongers use autoresolve! The mighty Frodogorn commands this heresy to cease!

Actually the game also uses it to resolve all AI vs AI fights.. :wink:

But on a more serious note, I think now that we have code (thanks to Rubik, fisheye and Mordachai) that allows us some more information about units, there's a lot more to work with to make a proper auto-resolver.
+Knight said:
And where can I find that awesome piece of code?
Someone needs to put all of these ideas together... that's not been done yet.

Step 1: folks complain about auto-resolve (it sucks).
Step 2: folks think about how to improve it and quickly hit wall realizing that most of the important information such a function needs is not in MS - no way to know armor value of armor, or weapon damage values of weapons, so hard to write anything that takes these into account
Step 3: Rubik solves for how to bring this info into MS using Python & slots trick.
Step 4: <- You are here...

I think that one of you might help me. I've redirected the "inflict_casualties_to_party_group" operation to the "inflict_casualties_to_party" script.

The first time AI heroes never died during AI vs AI battle.

I found the problem: a line in "party_count_fit_regulars" prevented heroes from taking damage. I changed it and it worked: AI lord eventually got 0% hp but now I have another problem. Parties never disband! In fact there is always a unit alive in the party. Always.

Did you experience this as well, or do you know what the problem is ?
Nemo - I'm just thinking out loud here, but I would expect that you have to actually remove that troop from the party, and then delete the party to actually get it to disband.

If you look around - I'm sure that there is code that handles killing a party.  But only causing damage to a party isn't going to disband it.  The game doesn't have a sweeper function that finds parties and kills them - its up to your code to explicitly do so.
interesting. thanks.

Does the game automatically respawn lords? (I remember reading somewhere that it does every 48h)

However I would have to rewrite the capturing lord thing and I don't feel like it.
Yes, there is a 48hr timer that checks for lords of an active faction, looks for a walled center of that faction that isn't under siege, and then spawns said lord with a new party there.
I looked at the damn code after the inflict_casualties_to_party_group operation, and it checks the strength of the party. If the strength is null, the battle ends and the battle resolution script starts. But I can't get rid of that last guy! He is in a stack of wounded soldiers, so there's no reason he doesn't get hit as well.

How do you improve the auto resolve system without tweaking that script ?
Mordachai said:
Step 1: folks complain about auto-resolve (it sucks).
Step 2: folks think about how to improve it and quickly hit wall realizing that most of the important information such a function needs is not in MS - no way to know armor value of armor, or weapon damage values of weapons, so hard to write anything that takes these into account
Step 3: Rubik solves for how to bring this info into MS using Python & slots trick.
Step 4: <- You are here...

Ok, Mord, that's twice now you've used your Ebil Jedi mind tricks on me to make me write a bunch of code.  Knock it off.  (You should have a talk with my boss :razz:)

If you want to mess with this stuff, please take the time to figure out what it does.  There are going to be bugs.  Let me know where they are.  This will slow down both your compile times and your game init times.  Also, read the trailer below:  there will be more code.


Stick this at the top of your
import string
from process_common import *
from module_troops import *

# pulls the gigantor values out of the skill blob and returns a 3-tuple
# containing power draw, power strike, and power draw skill values.
def kt_get_power_skills( flags ):
pdraw = 0
pstrk = 0
pthrw = 0
pdraw_top = knows_power_draw_10 + knows_power_draw_5
pstrk_top = knows_power_strike_10 + knows_power_strike_5
pthrw_top = knows_power_throw_10 + knows_power_throw_5

if ( flags & pdraw_top ) > 0:
pdraw = flags & pdraw_top
pdraw /= knows_power_draw_1
if ( flags & pstrk_top ) > 0:
pstrk = flags & pstrk_top
pstrk /= knows_power_strike_1
if ( flags & pthrw_top ) > 0:
pthrw = flags & pthrw_top
pthrw /= knows_power_throw_1

return (pdraw, pstrk, pthrw)

# pulls the gigantor values out of the skill blob and returns a 3-tuple
# containing shield, athletics, and ironflesh skill values.
def kt_get_melee_skills( flags ):
shld = 0
athl = 0
irfl = 0
shld_top = knows_shield_10 + knows_shield_5
athl_top = knows_athletics_10 + knows_athletics_5
irfl_top = knows_ironflesh_10 + knows_ironflesh_5

if ( flags & shld_top ) > 0:
shld = flags & shld_top
shld /= knows_shield_1
if ( flags & athl_top ) > 0:
athl = flags & athl_top
athl /= knows_athletics_1
if ( flags & irfl_top ) > 0:
irfl = flags & irfl_top
irfl /= knows_ironflesh_1

return (shld, athl, irfl)

# creates the list with the module code tuples for the new troop slots.
# very slow.  comment out the stuff you don't need.
def kt_python_init_troop_slots():
module_code = []

# figure out our bounds
underscore_pos = string.find( soldiers_begin, "_" )
id_str = soldiers_begin[ underscore_pos+1:len(soldiers_begin) ]
begin_troop = find_troop( troops, id_str )
underscore_pos = string.find( soldiers_begin, "_" )
id_str = soldiers_end[ underscore_pos+1 : len(soldiers_end) ]
end_troop = find_troop( troops, id_str )

# process for each troop
for i_troop in range(begin_troop, end_troop+1):
oneh_prof = (troops[i_troop][9] >> one_handed_bits) & 0x3FF
twoh_prof = (troops[i_troop][9] >> two_handed_bits) & 0x3FF
pole_prof = (troops[i_troop][9] >> polearm_bits) & 0x3FF
arch_prof = (troops[i_troop][9] >> archery_bits) & 0x3FF
xbow_prof = (troops[i_troop][9] >> crossbow_bits) & 0x3FF
thrw_prof = (troops[i_troop][9] >> throwing_bits) & 0x3FF
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_1hprof, oneh_prof) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_2hprof, twoh_prof) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_poleprof, pole_prof) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_archprof, arch_prof) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_xbowprof, xbow_prof) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_thrwprof, thrw_prof) )
att_str = (troops[i_troop][8] & 0xFF)
att_agi = (troops[i_troop][8] & 0xFF00) >> 8
att_int = (troops[i_troop][8] & 0xFF0000) >> 16
att_cha = (troops[i_troop][8] & 0xFF000000) >> 24
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_str, att_str) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_agi, att_agi) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_int, att_int) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_cha, att_cha) )
(skill_pdraw, skill_pstrike, skill_pthrow) = kt_get_power_skills( troops[i_troop][10] )
(skill_shld, skill_athl, skill_irfl) = kt_get_melee_skills( troops[i_troop][10] )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_pstrike, skill_pstrike) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_pdraw, skill_pdraw) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_pthrow, skill_pthrow) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_shield, skill_shld) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_atheltics, skill_athl) )
module_code.append( (troop_set_slot, "trp_"+troops[i_troop][0], kt_slot_troop_ironflesh, skill_irfl) )
return module_code[:]

Stick this at the bottom of your
  # the body of this script is generated at compile time.
( "kt_init_troop_slots", kt_python_init_troop_slots() ),

New slot values in, I stuck them after the rebellion changes:
kt_slot_troop_1hprof = 200
kt_slot_troop_2hprof = 201
kt_slot_troop_poleprof = 202
kt_slot_troop_archprof = 203
kt_slot_troop_xbowprof = 204
kt_slot_troop_thrwprof = 205
kt_slot_troop_str = 206
kt_slot_troop_agi = 207
kt_slot_troop_int = 208
kt_slot_troop_cha = 209
kt_slot_troop_pstrike = 210
kt_slot_troop_pdraw = 211
kt_slot_troop_pthrow = 212
kt_slot_troop_shield = 213
kt_slot_troop_atheltics = 214
kt_slot_troop_ironflesh = 215

In an ideal world, I would have gotten home at a reasonable time from work and would have had time to parse all the item values as well.  That's not the world I live in.  (For those wondering, this is exactly what I meant when I kept talking about "hacking up the compile code", rubik just beat me to it :wink:)

This is just groundwork; the better thing to do which I will explore tomorrow after work (provided I'm home at a reasonable hour) is to do the strength and defense calculations at compile time where the auto-gen code lives rather than trying to do it in script.  It will be much faster.  Then I can go back to the strength calculation and do something sane.  That plus the strength calc code I posted the last time I posted code and a quick jaunt through all six instances of autoresolving completes the deal. 


edit:  needed more BLAM
Wow so it's possible to use a generic system instead of 500 lines of tries to fill the bonuses slots.

I think a complete storing of units attributes etc... isn't needed anyway as most of the factors are already included in troops level and autoresolve hasn't be so detailed that it makes difference between 2 usefull skills or attribute (I mean there are 2 stats and 6 skills usefull in combat, it's interesting to know if an unit has a bonus in these compared to other units of equal level, not necessary to know which stat/skill exactly is better, or the value of stats having no effect in combat).

More needed is to analyze the items an unit is (or may be) carrying as gear especially armor quality make more difference than skills, and as weapons categories are the main factor to give a relevant bonus for each kind of fight (I guess it's doable with an init giving slots to items first, then for each unit making a calculation based on the values of items it may carry ; the problem here is the fact the item list is a potential so if say an unit may have 5 different weapons/armors the value should be either an average or based on a probability).

ps :

Only cowards and whoremongers use autoresolve! The mighty Frodogorn commands this heresy to cease!

The main goal of changing autoresolve is to make fight between AIs when the player is not there give about the same results as a 3d fight, ie making archers more deadly in sieges situations or a force of cavalry fighting an infantry one in plain able to win easily.
For fights with player's army the best way to improve battle simulation is just to completely remove autoresolve, like it's done in sword of damocles (instead of being forced to autoresolve when you are knocked out, you are allowed to watch the end of the fight).
You men are making the world better :grin: As the battles are the essence of M&B and improving them is the primary task of the strange guys prefering modding the game to playing it (it's haunting me, I've never finished STALKER:SHOC because of writing scripts for it, and now the same story with M&B, I've been playing it for a couple of weaks maybe :shock:).

2 Nemeo
I did't get what is your goal in redirecting  from inflict_casualties_to_party_group to  inflict_casualties_to_party. Do you want the AI lords to die in battles occasionally and their parties to disband? Maybe make them deserters instead of disbanding?

Twan said:
instead of being forced to autoresolve when you are knocked out, you are allowed to watch the end of the fight.
Yes, this is must have!

I have a question about realtime battles. The archers act quite intelligently: they are firing at distant enemy while hiding their bows and trying to fight the close enemy with their penknives :smile: And what about cavalry? AFAIK they randomly choose between spears and say swords from their inventory. Ideally, they should always charge with spears, and change them to something more handy if they get stopped, surrounded by infantry or unhorsed. Is it just me or cavalry AI is acting inefficiently? If I am correct, is it possible to make it the proper way?
Top Bottom