I'm starting to look at the "DoDamage" function as a source of problems. Basium's spell is able to kill Hyborem using it, the Pestilence event is killing everything with it.
Looking at the signature for the function
Code:
void CyUnit::doDamage(int iDmg, int iDmgLimit, CyUnit* pAttacker, int iDmgType, bool bStartWar)
And at the two events in question;
Code:
pUnit.doDamage(75, 100, caster, gc.getInfoTypeForString('DAMAGE_HOLY'), false) // Divine Retribution
pUnit.doDamage(25, 100, pUnit, gc.getInfoTypeForString('DAMAGE_DEATH'), false) // Pestilence
Both have damage limits of 100 so it's quite possible for them to kill a unit. However, I'm also assuming that they're supposed to have a range of possible damages between iDmg and iDmgLimit. The number seems to be hitting the "limit" more often than intended if *every* susceptible unit has been killed. The only other event to have the damage limit as 100 is Raging Seas - though I've seen this used numerous times but it has never wiped out the target units as described in the bug posts.
===
Onto the actual DoDamage - the "resist" of a unit is;
Code:
iResist = baseCombatStr() *2;
iResist += getLevel() * 2;
It is used later as a % value, so we're looking at a percentage equal to double the unit's base strength + double their level. A Level 5 Warrior would be (Base Strength 3 doubled + Level 5 Doubled = 16% Resist).
Code:
if (iDmgType != -1)
{
iResist += getDamageTypeResist((DamageTypes)iDmgType);
}
The resist is then modified if the Damage has a type (FIRE, COLD etc) and the unit has a resistance to that type (this part has been reported broken elsewhere though).
Code:
if (pAttacker != NULL)
{
iDmg += pAttacker->getSpellDamageModify();
}
Now hang on... pAttacker isn't NULL for worldspells and events. For the WorldSpells, it's the unit that cast the spell and for the pestilence event it's actually the unit being affected by the spell.
From the Pestilence event...
Code:
for pUnit in py.getUnitList():
if pUnit.isAlive():
pUnit.doDamage(25, 100, [color=red]pUnit[/color], gc.getInfoTypeForString('DAMAGE_DEATH'), false)
SpellDamageModify is 5 for every CombatPromotion and 10 for UnholyTaint. There doesn't seem to be anything that handles it in either C++ or Python other than the line in...
setHasPromotions
Code:
changeSpellDamageModify(GC.getPromotionInfo(eIndex).getSpellDamageModify() * iChange);
A combat 5 unit should therefore get a 25 point increase to iDmg (which is the base amount allowed). In the case of the pestilence event, iDmg is now 50 (25+25) and for Divine Retribution it is now 100 (75+25).
High combat units are more susceptible to Pestilence as they are considered to be casting it themselves. If you use a High Combat unit to cast Raging Seas or Divine Retribution, it will do more damage.
====
Moving back to the DoDamage...
Checking if the resistance is 100% - if not, we need to do some damage...
Changes the iResist into a negative value to be deducted from damage.
Code:
iDmg = GC.getGameINLINE().getSorenRandNum(iDmg, "Damage") + GC.getGameINLINE().getSorenRandNum(iDmg, "Damage");
iDmg = iDmg * (iResist + 100) / 100;
First we get a two random numbers upto iDmg, and add them together. Then we deduct iResist as a percentage (iResist + 100 gives the percentage NOT resisted, dividing by 100 gives a decimal representation of the percentage)
===
At this point I ran a quick test with a Warrior (no COMBAT promotions at all) casting Divine Retribution against 5 Hyborems. 4 Hyborems died - one was reduced to 4.8. There is no *guarantee* of wiping them out - but it seems very likely.
===
Assuming a worst case scenario of Level 1 Hyborem - his resistance will be;
Level*2 + BaseStrength*2 = (1*2 + 7*2)
= 2 + 14
= 16 (effectively 16%)
However - Hyborem has the Gela promotion, which is -25% to Holy Damage.
So his resistance is actually;
16 - 25 = -9 (effectively -9%)
A "Better case scenario" for Hyborem would be at Level 10.
Level*2 + BaseStrength*2 = (10*2 + 7*2)
= 20 + 14
= 34 (effectively 34%)
Taking away the Gela penalty again gives us 9% resistance.
So Hyborem, between levels 1 and 10 improves from giving away a 9% damage bonus to Divine Retribution, to gaining a 9% resistance to it.
==
Mental runthrough with some random numbers then... For Divine Retribution (with no promotions), the numbers can between 0 and 74 (if I recall getSorenRandNum(n) is from 0 to n-1).
Assuming the worst case - 74 and 74 vs the worst case Hyborem (who takes 109% of that damage) we have (148 * 1.09) = 161.32 points of damage.
Dead Hyborem.
Assuming the worst case - 74 and 74 vs the "Better case" Hyborem (who takes 91% of that damage) we have (148 * 0.91) = 134.68 points of damage.
Dead Hyborem.
So in the worst case, or any case approaching worst case, Hyborem is going to die unless he's a *very* high level.
Assuming the "average case" 38 + 38 vs the worst case Hyborem (who takes 109% of that damage) we have 74 * 1.09 = 75.09 points of damage.
Hyborem should survive that.
Assuming the "average case" 38 + 38 vs the better case Hyborem (who takes 91% of that damage we have 74 * 0.91 = 67.34 points of damage.
Hyborem should survive that too.
So on average, a Hyborem of any level *should* survive that much damage. 4 out of 5 dead Hyborems doesn't suggest that is the case.
====
Time for a few more tests...
====
175 Level 1 Hyborems. 82 died. 47% losses. Plus the additional damage to the others.
75 Level 10 Hyborems. 29 died. 39% losses. Plus additional damage, but observationally less than the first case.
Ok - so "4 out of 5" was lucky. We're generally looking at about 47% chance of a Level 1 Hyborem dying.
==
Moving on through DoDamage.
Code:
if (iDmg + getDamage() > iDmgLimit)
{
iDmg = iDmgLimit - getDamage();
}
If iDmg + the unit's current damage is greater than the limit, change iDmg so that it's just enough to bring the damage to the limit. Seems fair enough.
Skipping over the part that sends the messages to the screen...
Code:
if (pAttacker != NULL)
{
changeDamage(iDmg, pAttacker->getOwner());
if (getDamage() >= GC.getMAX_HIT_POINTS())
{
kill(true,pAttacker->getOwner());
}
If the attacker isn't NULL (which in our cases, it isn't) changeDamage by the amount we worked out for iDmg. If the new total damage is greater than our MaxHP - kill the unit. All seems to be fine.