[Vanilla] Hans Lemurson figures out the Combat Formula

Not knowing the unit attacking does not help...and if it attacking walls or city?
Indeed. I suspect walls to be the main source of this behavior. Units apparently have their str adjusted through modifiers and i suppose those would show in AttackerStrMod rather than require a different formula. Walls however seem to follow entirely different rules. When attacking cities i often felt my damage was different when attacking the walls or the city's HP themselves. Also part of the damage dealt to the walls is usually applied to the city so the code must be different for this splitting of damage damage between two different health bars.
I also wonder how this might affect AIs ability (or inability rather) to actually take down cities. Early in the game, walls have only 50 HP but it takes quite some attacks to bring them down. The preview usually shows a "major defeat" even if the unit will definitely survive, maybe because walls take less damage due to this formula. What if the AI "looks" at the numbers before deciding to attack, and choose not to attack because they will deal much less damage than they take, but out of 50 health rather than 100?
It also looks like there is not enough samples in that one and you need to combing a few .csv's.
Looks like i'll have to play domination:viking::ar15:
 
Well its been stated that
1-melee attacks do less damage to walls
2-ranged(not siege) units have less strength attacking walls

#1 would throw off the curve (especially if some damage was v city itself and some was v walls)

...to test it properly you need hotseat domination. (Or see if you can sort out melee v ranged attacks)
 
Melee vs ranged is very easy as ranged attackers always receive 0 damage but i didn't consider this. Wall vs no wall is harder when looking at the log, i could try to disable early walls for a test if simply separating melee from ranged is not enough.

Less str could simply modify the value of AttackerStrMod in the same way the +10 vs anti cavalry or similar unit abilities change it and the formula could be the same, only with a reduced str to calculate damage.
What those results seem to imply however is that the formula itself is different when attacking a city/city wall. The code that handles this is probably different to account for an object with 2 "health bars" rather than 1 so the formula could also be different.

EDIT. Separating ranged from melee doesn't show a different trend, but of course i have even less samples making the entire exercise rather pointless. I will try again once i have more samples.

Ranged vs District
UnitVsDistrictRanged.png

Melee vs District
UnitVsDistrictMelee.png
 
Last edited:
Trying to square the circle aren't you?:p
If you really want something easy to understand for whoever have no mathematical background you probably need to drop your formula completely and go for something visual.
View attachment 459815
OK, i took my own combat log and plotted the results. Here's what i have (roughly what you have)

Unit vs Unit Both attacker and defender damage considered for a total of 748 samples after removing 100 and 0 damage (attacker always takes 0 when it's a ranged unit)
View attachment 459821

District vs Unit (unit bombarded by City Center or Encampment with walls) Obviously only defender damage is used (126 samples)
View attachment 459822

Unit vs District. I used only the damage dealt by the unit to the district and removed the 200 damage (cap for max city wall). Only 79 samples unfortunately.
View attachment 459823

So both for unit vs unit and district vs unit i get very similar results. For unit vs district however, my results differ a lot but the distribution doesn't even show a clear exponential curve, i just fitted an exponential curve because it's what we have for normal combat. It's possible that the formula used to damage cities is different. Interestingly, the fitted curve has 15 baseline damage, half the normal baseline for unit vs unit, so it might be good despite the low number of samples.

EDIT. Just to let you know the log doesn't really tell you a district is under attack but there is a value set to either 1 or 3 to identify the object type of the attacker and defender. "AttackerObjType" 3 is clearly identified as a district (city center or encampment) while "DefenderObjType" 3 is "UNKNOWN" but given that city walls can grow to 200 and i see some 200 damage when defender is 3 i guess they used the same numeric identifier for defender and attacker and "DefenderObjType" 3 means a district (city center or encampment) was under attack at that time.
Great job, Lord Yanaek!!! Your analysis looks even better than mine, so I feel no shame in full-quoting you so your graphs can appear in page 2 of the thread. The exponent constant you're getting seems to be closer to 1/26 than 1/25, but I suspect this being because the best-fit line is finding the geometric-mean of the variance rather than the arithmetic-mean. The geometric mean of 1.25 and 0.75 is 0.968 which when divided by 25 yields the 0.387 value we keep seeing.

For Unit vs. District, although that data is quite messy, it does at least fit with the idea that "Melee attacks deal half damage to city walls" is accomplished by literally cutting the damage in half rather than just a -17 strength penalty. If some units do not face this penalty sometimes, then that could lead to the very scattered data you have seen.
 
The geometric mean of 1.25 and 0.75 is 0.968 which when divided by 25 yields the 0.387 value we keep seeing.
Hmm...upon second thought, although the numbers fit I'm not actually sure that what I said has any mathematical basis. :P
 
Walls however seem to follow entirely different rules. When attacking cities i often felt my damage was different when attacking the walls or the city's HP themselves. Also part of the damage dealt to the walls is usually applied to the city so the code must be different for this splitting of damage damage between two different health bars.
A couple things to point out regarding city attacks, most is probably very much known already. Ranged units have a -17 str modifier when attacking cities. I believe, iirc, that applies with or without walls. Melee units also have a penalty to attacking walls if no battering ram is present. On the other side, city walls give the city a modifier. I believe ancient walls is +4 strength and medieval walls is shown as "enhanced defenses", which I've seen as an additional +6. Once the walls are gone, you will see in the matchup that the city's strength number dropped and all your units are now doing much more damage, even melee with battering rams because of that strength differential changing.
 
Actually I have a couple questions to add. So the incendiary promotion for ranged units, +7 against district defenses, essentially changes their -50% penalty against cities into -25% then? Also, that means the melee promotion that gives +10 when occupying a district is essentially +50% damage bonus against cities?
 
Actually I have a couple questions to add. So the incendiary promotion for ranged units, +7 against district defenses, essentially changes their -50% penalty against cities into -25% then? Also, that means the melee promotion that gives +10 when occupying a district is essentially +50% damage bonus against cities?
Let's try to keep "Damage Bonuses" and Strength modifiers separate. Strength modifiers result in bonuses to damage dealt as well as reductions to damage received in return. (I make this distinction because Walls seem to reduce damage the city receives without increasing their counterattack.)

+7 Strength means a unit will deal 1.33x damage, and take 0.75x damage in return. X being whatever damage it would have dealt/taken without that +7 modifier.

I believe the Incendiary promotion for ranged units will turn their -17 strength penalty into a -10 penalty. Instead of dealing 0.5x damage to the district, they will deal 0.67x damage (or 1.33x as much as before).

I try to state everything in terms of multiplicative factors because percents can become misleading since our instinct is often to add them. Also +4 strength has the same effect on damage dealt and received regardless of whether you are starting at a +10 advantage or a -17 penalty. +4 strength means you deal 1.17x damage and receive 0.85x as much damage as you would have otherwise.
 
I would suggest a slight tweak to the formula based on the following values in GlobalParameters.xml

<Row Name="COMBAT_BASE_DAMAGE" Value="24" />
<Row Name="COMBAT_MAX_EXTRA_DAMAGE" Value="12" />
<Row Name="COMBAT_POWER_SCALING" Value="0.04" /> 0.04 is equal to 1/25 conveniently

Combat Damage = random between(24 - 36) * exp(StrDifference / 25)

This formula above fails in 3 of the 193 combats from Hans' .csv file. The 3 failures appear below the predicted range by 1 or 2 points. These failures could potentially be due to rounding steps. Hans' initial formula does not fail in any of the combats as it predicts a slightly wider range. I think a good test would be to simulate equal strength combats to see if they fall within (24 - 36) 100% of the time. One could also modify the parameter file to see how they interact with results.

There is another parameter that sounds like it may effect all combat, but I'm not sure how. It would be interesting to see tests run on this.
<Row Name="COMBAT_POWER_DAMPENER" Value="5" />
 
Wonderful!
I guess it's an 80% to 120% range rather than the 75% to 125% I'd eyeballed. And stating it as 24-36 is much more intuitive than 30 +/-(0-20%).
 
I would suggest a slight tweak to the formula based on the following values in GlobalParameters.xml
Usually i look at the database first when i try to figure how things work but i totally missed this one:wallbash:
Edited the graph on page 2 to include that formula which is probably correct.
I wouldn't be surprised if COMBAT_POWER_DAMPENER had something to do with the reduction in strength when a unit is damaged.
There is apparently a value for this under "COMBAT_WOUNDED_DAMAGE_MULTIPLIER". BTW this one is pretty deadly because a wounded unit with reduced str will not only deal less damage, but also receive more
 
Last edited:
OK. For reference here are all the global combat parameters found in the database (so no unit or promotion special modifier). Most of those are probably str modifier, they change the attacker/defender str that's fed into the combat damage formula rather than affecting the formula itself.
Spoiler :
[table=head]
Parameter|Value
COMBAT_AIR_SUPPORT_BONUS_MODIFIER|5
COMBAT_AMPHIBIOUS_ATTACK_PENALTY|-10
COMBAT_ANTI_AIR_SUPPORT_BONUS_MODIFIER|5
COMBAT_ARMY_STRENGTH_MODIFIER|17
COMBAT_BASE_CAPTURE_STRENGTH_DIFFERENCE|20
COMBAT_BASE_DAMAGE|24
COMBAT_BOMBARD_VS_UNIT_STRENGTH_MODIFIER|17
COMBAT_CITY_RANGED_DAMAGE_THRESHOLD|50
COMBAT_CORPS_STRENGTH_MODIFIER|10
COMBAT_DAMAGE_MULTIPLIER_MINIMUM|0,25
COMBAT_DEFENSE_DAMAGE_PERCENT_BOMBARD|100
COMBAT_DEFENSE_DAMAGE_PERCENT_MELEE|15
COMBAT_DEFENSE_DAMAGE_PERCENT_RANGED|50
COMBAT_DISTRICT_STRENGTH_REDUCTION|15
COMBAT_FLANKING_BONUS_MODIFIER|2
COMBAT_GARRISON_MILITIA_MODIFIER|10
COMBAT_MAX_EXTRA_DAMAGE|12
COMBAT_MAX_HIT_POINTS|100
COMBAT_MAX_NUM_ATTACKS|1
COMBAT_MIN_CIVILIAN_DAMAGE_PERCENT|65
COMBAT_MIN_HEALTH_PERCENT_TO_ADVANCED_AIR_PILLAGE|1
COMBAT_MIN_HEALTH_PERCENT_TO_AIR_PILLAGE|50
COMBAT_MINIMUM_CITY_STRIKE_STRENGTH|3
COMBAT_MINIMUM_DAMAGE|1
COMBAT_MINIMUM_GARRISON_STRENGTH|0
COMBAT_POPULATION_PER_STRENGTH|2
COMBAT_POWER_DAMPENER|5
COMBAT_POWER_SCALING|0,04
COMBAT_RANGED_VS_DISTRICT_STRENGTH_MODIFIER|17
COMBAT_RELIGIOUS_FOLLOWING_CITY_TILE|5
COMBAT_RELIGIOUS_HOLY_CITY_TILE|15
COMBAT_RIVER_DEFENSE|5
COMBAT_STRENGTH_FROM_ENVOYS|1
COMBAT_SUPPORT_BONUS_MODIFIER|2
COMBAT_WOUNDED_DAMAGE_MULTIPLIER|10
COMBAT_WOUNDED_DISTRICT_DAMAGE_MULTIPLIER|10
MAYHEM_UNIT_COMBAT|1
MAYHEM_UNIT_COMBAT_WITH_BARBARIANS|0,5
[/table]

If someone knows how to format tables in this forum i'd like to know, normal BBCode doesn't seem to work.
Thanks to Browd for the table.

COMBAT_DAMAGE_MULTIPLIER_MINIMUM|0,25 also looks interesting. Not sure what this one is doing.

Also this : COMBAT_DISTRICT_STRENGTH_REDUCTION|15 could be the source of the difference we see when a district is attacked.
Not sure what this one is for but after some tests i'm almost sure the difference in behavior is caused by those values and walls :
COMBAT_DEFENSE_DAMAGE_PERCENT_BOMBARD 100
COMBAT_DEFENSE_DAMAGE_PERCENT_MELEE 15
COMBAT_DEFENSE_DAMAGE_PERCENT_RANGED 50
Kongo decided to help me run my tests as he attacked me very early with warriors so i countered with a warrior and some archers while he had no walls. The results show the same exponential curve we have seen for unit vs unit. Only 53 samples but i think it's safe to say the formula is the same before walls makes things messy.
UnitVsDistrictNoWall.png

Next he had walls in his last city so i modified those values, setting them all to 100 and from the few samples i have, it looks like the curve is once again the same. As soon as i try to set it to something different thought i can't get a good curve, probably because i have too few samples. Lowering those values to 25 for every unit definitely makes taking the last city much longer so i suppose they are just plain old percent modifying the damage, possibly the base damage before "exponenting" (does this word exist) the str difference. This doesn't explain why i haven't seen a difference between ranged and melee attacks previously. I think i'll run a test with an advanced start so every city have walls and see if i can get something useful with more samples.
 
Last edited:
Sounds like making a simple mod to test would be a good way to figure out how this works exactly. If anyone has time to try it out, I attached a mod that should get you started. Simply unzip it to My Documents\My Games\Sid Meier's Civilization VI\Mods, then edit the CombatTest.sql file in a text editor (notepad will do in a pinch) - if you want to add more rows, just copy a line and adjust the name accordingly. Start the game and enable the mod. The best approach is probably to switch off any modifiers and presumed randomness first, then check if the formula fits with your now perfectly predictable battles. Have fun!

P.S.: Great work so far!

Edit: By the way, it's much easier to see the linear relationship and upper and lower range is a constant factor if you plot on a logarithmic y axis. Here's my combat log (ca 1600 observations)
stats.png

Seems to me like you're mostly on the right track. Orange is the max given by your theory, green the min. The max fits pretty much perfectly (rounding errors?), while the minimum has some outliers (ignoring the obvious cut-off at 100)
 

Attachments

Last edited:
I think there's a slight math error in the lower-limit on your graph, alpaca. Subtracting ln(1.2) is equal to ln(0.833), not ln(0.8) which is what is indicated by the XML constants. Your lines are showing max of 36 and min of 25 instead of 24.

And then yes, the 100s are probably skewing things a bit with the main trend line as well as bleeding below the minimum line.

But aside from those details, that's a very useful and informative graph. The true nature of the data is readily apparent and easy to measure.
 
I think there's a slight math error in the lower-limit on your graph, alpaca. Subtracting ln(1.2) is equal to ln(0.833), not ln(0.8) which is what is indicated by the XML constants. Your lines are showing max of 36 and min of 25 instead of 24.

And then yes, the 100s are probably skewing things a bit with the main trend line as well as bleeding below the minimum line.

But aside from those details, that's a very useful and informative graph. The true nature of the data is readily apparent and easy to measure.
Yes, you're right. I noticed a few minutes after posting that I had made a mistake, but had already switched off my PC. I'll edit the graph tonight ;)
 
Combat is explained in the UnitPanel.lua in the UI.
 
UnitPanel.lua is the code that creates the unit panel (the small UI panel where you see units information and where the buttons to give them orders are). It will also draw the combat preview. unfortunately at some point it calls for the dll with methods like unit:GetDamage(); and that's where we loose the track.:undecide:
 
Thanks for the quick answer, Lord Yanaek. Where is the UnitPanel.lua file located? Steam must put it somewhere on a local drive, right?
 
<your steam folder>\SteamApps\common\Sid Meier's Civilization VI\Base\Assets\UI\Panels\UnitPanel.lua
It's certainly possible to get some information there but it's a 3729 lines file and from what i've seen looking quickly no code there deals with the actual damage calculation, just how to display the results (or rather the expected result)
 
Back
Top Bottom