Pestilence event broken

MetalMilitia-

Warlord
Joined
Nov 22, 2004
Messages
132
the Pestilence random event just killed all living units and reduced all cities to 1 in the game im playing.

Kind of a game breaker. I suppose Sebathiel will enjoy the free cities. The silly git went OO. ^^
 
the Pestilence random event just killed all living units and reduced all cities to 1 in the game im playing.

Kind of a game breaker. I suppose Sebathiel will enjoy the free cities. The silly git went OO. ^^
Tell me about it. I lost everything, all units, workers, heroes, cities down to "1", -nothing left, all due to a random event. Not so fun to play on after that. I can take losing a war and/or a hero, but to loose everything due to a random event ... not what I think is fun gaming. To further add to my misory, my neighbour had his diseased zombies left (yes I know that that is logical) and he made the process short with me (I hade only two catapults to protect my cities with).

And yes, I had just built the couldron in one city so some things in that city was converted to flesh golems, lucky me, -units, heroes, developed spellcasters for -a dozen or so flesh golems, no I quit that game and I play without armageddon since then. I know that this mean, losing an aspect of the game, but at least it is fair both to me and the AI.
 
aah heck, there are so many random things in this mod that could go wrong, i just play with New random seed on reload so if something crazy happens i can just reload an autosave. Its just a waste of precious gaming time to have your empire trashed by some random event.

Silly event though, i wouldnt mind if it spawned a bunch of undeads and lower some cities a bit, i guess thats what its suppose to do.
 
Tell me about it. I lost everything, all units, workers, heroes, cities down to "1", -nothing left, all due to a random event. Not so fun to play on after that. I can take losing a war and/or a hero, but to loose everything due to a random event ... not what I think is fun gaming. To further add to my misory, my neighbour had his diseased zombies left (yes I know that that is logical) and he made the process short with me (I hade only two catapults to protect my cities with).

And yes, I had just built the couldron in one city so some things in that city was converted to flesh golems, lucky me, -units, heroes, developed spellcasters for -a dozen or so flesh golems, no I quit that game and I play without armageddon since then. I know that this mean, losing an aspect of the game, but at least it is fair both to me and the AI.

But it is a bug, not a feature. Event was supposed to DAMAGE units and reduce population BY one (or two), not TO one. It'll probably be fixed in the next patch.
 
CvRandomEventInterface.py contains this 2 part code for what Pestilence does:

For the pop kill:

def doArmageddonPestilence(argsList):
kTriggeredData = argsList[0]
iPlayer = kTriggeredData.ePlayer
if iPlayer == 0:
for iPlayer in range(gc.getMAX_PLAYERS()):
pPlayer = gc.getPlayer(iPlayer)
if (pPlayer.isAlive()):
if pPlayer.getCivilizationType() != gc.getInfoTypeForString('CIVILIZATION_INFERNAL'):
for iCity in range(pPlayer.getNumCities()):
pCity = pPlayer.getCity(iCity)
if pCity.getPopulation() > 1:
pCity.changePopulation(-1 * pCity.getPopulation() / 4)

For the unit damage:
CyInterface().addMessage(iPlayer,True,25,CyTranslator().getText("TXT_KEY_MESSAGE_PESTILENCE_POPULATION_LOSS", ()),'',1,'Art/Interface/Buttons/Promotions/Apocalypse.dds',ColorTypes(7),pCity.getX(),pCity.getY(),True,True)
py = PyPlayer(iPlayer)
for pUnit in py.getUnitList():
if pUnit.isAlive():
pUnit.doDamage(25, 100, pUnit, gc.getInfoTypeForString('DAMAGE_DEATH'), false)

Okay. based on the very last line of first part, the intention was for it to reduce population in all cities by 1/4. Something in the formula got . .. .. .. .ed, I don't know what. Maybe the /4 isn't working right?

And the unit damage part is all moonspeak to me.

I'm not a goddamn C programmer, but we have some here, so hopefully one will come in and shed some light on what's wrong.
 
I'm not a goddamn C programmer, but we have some here, so hopefully one will come in and shed some light on what's wrong.

==
The following is a "stream of conciousness" as I looked into this - do not take it as gospel - it likely contains one or more mistakes. If you spot one - let me know, it might help.
==

Spoiler :

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...

Code:
if (iResist < 100)
Checking if the resistance is 100% - if not, we need to do some damage...
Code:
iResist *= -1;
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.




The short version is, that having sanity checked the process, it looks as though it *should* work. Experimentally however, Hyborem dies 47% of the time to Divine Retribution. From a purely theoretical point of view, that should be around 30% (1711 out of 5625 possible outcomes give a result that would cause 100 damage or greater to a Level 1 Hyborem).

EDIT: Just looking over Kael's changelog - I realised I'd missed one thing and that changes the results to match the experimental. Demons have a -50% resistance to Holy damage built in. So Hyborem at Level 1 doesn't take 109% of the damage, he takes 159% of the damage. That means he only actually needs 63HP damage to be inflicted, not 92. That raises the probability to around the 50% mark, possibly even slightly higher than the 47% observed. With -75% resistance to Holy total - there's no wonder the old boy got his backside kicked around by the world spell...
 
As my last post got off topic a little, I thought I'd better offer this as well as a temporary "fix" to the pestilence event. It takes the "teeth" away from it to a massive extent, but ensures it's no longer a "game-breaker" for those that feel it is such.

Code:
						if pCity.getPopulation() > 2:
							pCity.changePopulation(-2)
							CyInterface().addMessage(iPlayer,True,25,CyTranslator().getText("TXT_KEY_MESSAGE_PESTILENCE_POPULATION_LOSS", ()),'',1,'Art/Interface/Buttons/Promotions/Apocalypse.dds',ColorTypes(7),pCity.getX(),pCity.getY(),True,True)
					py = PyPlayer(iPlayer)
					for pUnit in py.getUnitList():
						if pUnit.isAlive():
							pUnit.doDamage(25, 90, pUnit, gc.getInfoTypeForString('DAMAGE_DEATH'), false)

Any city of Size 3 or greater loses 2 population. All living units take damage but will always have 10% health left as a minimum. No units should die as a result.

The CvRandomEventInterface.py file in the zip should go in \Assets\Python\entrypoints\ - I've included the directory structure for those that just want to unzip to the mod folder.
 

Attachments

My testing isnt showing any problem with pestilence. It doesnt reduce cities to 1 population and it only does about 25% damage to units. I can make the change to keep a unit from being effected by its own promotions, but that shouldnt cause the issues you guys are seeing.
 
My testing isnt showing any problem with pestilence. It doesnt reduce cities to 1 population and it only does about 25% damage to units. I can make the change to keep a unit from being effected by its own promotions, but that shouldnt cause the issues you guys are seeing.

I must admit that I've seen it run without issue previously and gave the result as you indicated. I have also seen one case where every living unit in my empire died (I didn't lose all the population although more than 1/4 died - the units were wiped out). That game was on Niki's huge map so there were a *lot* of players active - could the event be triggering for multiple civilizations on the same turn?
 
I don't know if it is any help, but here is a save taken directly after the "massive disaster". And if it isn't to any help, at least some of the people out there like to see the aftermath to a really nasty event (thank god for my cauldron).
 

Attachments

Looking at DemonMasters save it looks like the Pestilence event is only triggering once, but its definitly hitting the same unit multiple times. Im checking it out but I haven't been able to figure out why yet.

edit: ahh.. I think I got it. The pestilence event was set as a global event. I think that means it trriggers for each player (all my tests are done on a dual map with only 2 players so i hadnt been able to see much of a difference). Im going to play with it a bit and see if removing the global tage seems to fix it.
 
Just to confirm, we just had a pestilence that wiped out every living unit and set every city to a population of 1. It seems to have happened for every player.

Best wishes,

Breunor
 
This should be fixed in the recent patch, gogo update. :)

Ok, I saw this on the changelog, but I'm pretty sure this happened on h. I have installed the latest patch. This wasn't my game (a relative was using my machine), so I'm not positive that he had the latest, but I'm pretty sure I have the latest patch.

So, I'll try to keep an eye out for this, and so should everyone else...


Here is the first lines on the Readme ...

'Fall from Heaven 2
Version 0.30h'

I installed it into the c:\program files\firaxis\civilization4\beyond the sword\mods directory, I did not install it into the FFH 0.30 directory. Is this correct?



Best wishes,

Breunor
 
Back
Top Bottom