How We Taught Math To Hades

Recently we have implemented many new rituals that cause a side effect for spells of our gods. One of them is the Divine Aura which has a side effect to the spell City Protection of Athena: Aura Of Healing.

When that side effect Aura Of Healing is running on a city and the city is being attacked there is a 20% chance of revival for each unit after a successful defense.
That is a very similar mechanic to the spell from Hades Return from the underworld which resurrects 10% of attacking units after an attack.

Knowing that was very helpful for the implementation since we needed to touch a very similar place deep down in the fight system to support the new effect Aura Of Healing. The right place in the fight system is just after the fight when the damaged units get applied and stored[1]. Shortly before that we need to restore the units that would otherwise have died and be removed from their home towns, freeing up population again.

Finding that place was already half the work because Aura Of Healing and Return from the underworld work so similar (one resurrects attacking units the other defending units). But wait what is this? We analyzed the old code from Return from the underworld and found this:

foreach (UnitTypes::getAvailableGroundUnits() as $unit_type) {
   $resurrected_units->$unit_type = floor($casualties->$unit_type * 0.1);
}

(simplified a bit for easier reading)

Can you spot the problem? You have to have at least 10 units to get 10 percent back. Why? Lets imagine we loose 9 units and now we multiply that with 0.1 (which is the same as 10%) so we get 0.9 units. After we round those 0.9 units down there is not much left of what we could revive.

An extreme case could look like this:

Unit A Unit B Unit C Unit D Total
Dead 9 9 9 9 36
Should be alive ? ? ? ? ?
Were alive 0 0 0 0 0

That is not good. But the question is: What should happen in that case? We can hardly resurrect 0.9 units.
So after some talks with one of our game designers we came up with a solution.

We still want to use the old way of resurrecting units – it works fine most of the time. We just have to take care of the edge cases. For that we calculate how much population we want to resurrect and how much we have resurrected already. And in a second step we resurrect random still dead units until we have reached our goal of 10%.

So this is what we do step by step:

– Take 10 percent for each of the dead unit types and try to resurrect those (the way it is currently)
– Calculate how much population should be alive and check how much we just resurrected
– If there is still some population missing before we have reached 10% pick random still dead units and resurrect those until we reached 10%

Unit A Unit B Unit C Unit D Total
Dead 9 9 9 9 36
Should be alive ? ? ? ? 3
Were alive 0 2 0 1 3

Since we use the units population and resurrect them randomly in those edge cases the same number of lost units result in different compositions of units that get resurrected. Lets show that with some more examples:

To fit population in lets use the schema amount (population)

Swordsmen
(1 pop.)
Slinger
(1 pop.)
Chariot
(4 pop.)
Catapult
(15 pop.)
Total
Dead 8 (8) 6 (6) 4 (16) 2 (30) 20 (60)
Should be alive ? ? ? ? ? (6)
Resurrected Units 1 0 (0) 2 (2) 1 (4) 0 3 (6)
Resurrected Units 2 3 (3) 3 (3) 0 0 6 (6)
Resurrected Units 3 6 (6) 0 0 0 6 (6)

And that is how we fixed a subtle bug that has been in the code since the original implementation 4 years ago.

[1] Fun-fact: The fight has many stages of which damaging the units and applying those damaged units to their hometowns are two different things which makes it easy to use that part of the fight system for the simulator.

Posted in Technical