Damage formula (OXCE)
OXCE and mod scripts have greatly expanded the capabilities of weapons, items and armor. So here is a detailed breakdown of what happens when a weapon hits an enemy. Variable names are taken from the Ruleset or applicable Script or otherwise are ALL-CAPS.
Lethal ranged single target
The power of an attack is the sum of the base power (power) of the weapon, and the bonus power (damageBonus) which depends on the weapon and can also depend on the stats of the user. This sum is then randomized depending on the weapon (RandomType) but is typically 0%-200%.
GROSS POWER = (power + damageBonus) x RNG(0%, 200%)
The power can get weaker past a certain distance (powerRangeThreshold) in which case damage is reduced by an amount (powerRangeReduction) for every tile beyond that distance. The reduced power can't go negative and heal the target. TBD: can there be negative range reduction, i.e. power increases with distance? Is range damage reduction applied before or after randomType?
RANGE REDUCED POWER = GROSS POWER - LIMIT_LOWER(0, RANGE - powerRangeThreshold) x powerRangeReduction
[Yankes Scripts] For each projectile that hits, energy shields are taken into account in the following sequence regardless of hit direction: left-hand energy shield item, right-hand energy shield item, armor energy shield. Each shield has a separate pool of hit points (shieldHp) that is reduced by the power they absorb modified by a resistance factor depending on damage type (shieldResistCoeff). Once all shields are depleted any power remaining keeps going. As usual a lower resistance coefficient is more effective, but a value of zero means the shield is bypassed undamaged.
E-SHIELD DAMAGE = RANGE REDUCED POWER x shieldResistCoeff / 100 POWER THROUGH E-SHIELDS = RANGE REDUCED POWER - shieldHp * 100 / shieldResistCoeff
[Yankes Scripts] Physical shield items come after energy shields, but only one shield will have any effect and the left-hand takes priority over the right-hand. Physical shields do not take damage and cannot be destroyed, but have 50% effectiveness from the sides, 25% from below, and 0% from the rear. They reduce power by their armor value (shieldArmor) modified by a resistance factor depending on damage type (shieldResistCoeff). Once again a resistance of zero means the shield is bypassed.
POWER THROUGH P-SHIELDS = POWER THROUGH E-SHIELDS - shieldArmor * 100 / shieldResistCoeff x SIDE COEFFICIENT
Then armor pre-damage is applied to the side of the armor that was hit. Power is multiplied by a factor (ToArmorPre) and then randomized from 0-100% depending on a flag (RandomArmorPre). Default armor pre-damage is 0%. TBD: check if armor pre-damage comes before or after shields.
ARMOR PRE-DAMAGE = POWER THROUGH P-SHIELDS x ToArmorPre x IF(RandomArmorPre, RNG(0%, 100%))
Armor provides a final layer of protection. The remaining power is multiplied by a resistance factor depending on damage type (damageModifier) and then reduced by the current armor value of the side that was hit (frontArmor, sideArmor, sideArmor + leftArmorDiff, rearArmor, underArmor) modified by the weapons armor effectiveness (ArmorEffectiveness).
NET POWER = POWER THROUGH P-SHIELDS x damageModifier - facingArmor x ArmorEffectiveness
Finally damage is applied independently to each of the target unit stats. Power is multiplied by the weapons damage factor (ToArmor, ToHealth, ToStun, ToTime, ToEnergy, ToMorale) and then randomized from 0-100% depending on a flag (RandomArmor, RandomHealth, RandomStun, RandomTime, RandomEnergy, RandomMorale). By default damage factors are 10% for armor, 100% for health, 25% for stun, 100% for wounds and 0% for the rest, and random flags are only true for stun and wounds.
ARMOR DAMAGE = NET POWER x ToArmor x IF(RandomArmor, RNG(0%, 100%)) HEALTH DAMAGE = NET POWER x ToHealth x IF(RandomHealth, RNG(0%, 100%)) STUN DAMAGE = NET POWER x ToStun x IF(RandomStun, RNG(0%, 100%)) TU DAMAGE = NET POWER x ToTime x IF(RandomTime, RNG(0%, 100%)) ENERGY DAMAGE = NET POWER x ToEnergy x IF(RandomEnergy, RNG(0%, 100%)) MORALE DAMAGE = NET POWER x ToMorale x IF(RandomMorale, RNG(0%, 100%)) Wounds are a special case.
If the target suffered any health damage then they will also suffer morale damage unless the weapon has a flag against it (IgnoreNormalMoraleLose).
EXTRA MORALE DAMAGE = HEALTH DAMAGE x (110 - Bravery) / 100