Skip to content
Kassandra
On this pageThe order
Combat

How damage is worked out

The whole path from a statistic to a health bar, in the order it happens.

Every blow in the game goes through one function. Not by convention — by construction: there is a single place where health goes down, and everything that can hurt a player is routed through it. That is what makes this page short enough to be true.

The order

  1. Mitigate the school. Physical damage is answered by Armor, magic damage by Magic Resist. The reduction is flat and one-for-one: a point of Armor cancels a point of Physical Damage. Never below zero — damage does not heal.

    text
    mitigated = max(0, damage − mitigation)
  2. Add True Damage. Added after mitigation, so nothing reduces it. This is the whole of its design: it is the statistic that beats a defensive build rather than out-scaling it.

    text
    blow = mitigated + trueDamage
  3. Weight it by where it landed. A trauma resolves into an ordered path through the body's hit volumes, and that path yields one weight for the whole blow. Applied once, over the sum — mitigating per volume would make a deeper wound hurt less, because armour would be paid for every volume the shot passed through.

    text
    damage = round(blow × locationWeight)
  4. Spend shields. Absorption stands between that number and the health bar: the matching school's pool first, then the general one. See Shields.

  5. Take the remainder off health. Whatever the shields did not absorb.

Death

Death is not a flag. A player is dead exactly while their health is at zero, so a corpse cannot disagree with its health bar.

That leaves one thing that is not damage: losing a vital volume. A trauma that takes the head or destroys the torso kills directly, by setting health to zero — it does not roll a large number and hope. This is why a shield does not save you from decapitation: a shield absorbs harm, and losing your head is not harm, it is an outcome.

The numbers, as they actually are

A player's full health pool, and every statistic they carry before anything is equipped, are rendered below straight from the simulation.

640
StatisticBaseWhat it does
physicalDamage55Answered by Armor.
magicDamage0Answered by Magic Resist.
trueDamage0Answered by nothing. Added after mitigation.
haste0Shortens cooldowns, by division.
movementSpeed6Metres per second.
armor30Cancels Physical Damage, one for one.
magicResist30Cancels Magic Damage, one for one.

An enemy's health pool and its own statistics follow the same rule.

420
StatisticValue
physicalDamage70
magicDamage85
armor25
magicResist25

Spells

There is no basic attack. The thing you do by default is a spell like any other and is meant to be swapped, so it is an entry in a table rather than a special case.

Timing is gameplay and lives in the simulation — the client stretches its animation to fit what this says. The alternative is combat timing living inside an exported GLB, where nobody can reason about it.

Every spell is listed below, with its tick counts converted into seconds at the server's tick rate.

20
SpellSchoolCostCastLands onReachCooldownKind
kickphysical151.0 s0.6 s2.4 m2.2 scrush

A worked example

An unmodified player kicks an unmodified enemy, square in the torso.

  1. The player's Physical Damage is mitigated by the enemy's Armor, leaving the difference — both figures are in the tables above.

  2. True Damage is added. Base is zero, because nobody is born ignoring armour.

  3. The sum is weighted by where the blow landed, then rounded.

  4. The enemy carries no shields, so all of it reaches health.

Haste

Cooldowns are shortened by Haste, which divides rather than subtracts:

text
ticks = max(1, round(cooldown / (1 + haste / 100)))

100 Haste halves a cooldown, 200 thirds it, and no amount of it ever reaches zero. Subtracting would have meant some finite pile of Haste removed cooldowns altogether, and a cost you can delete is not a cost.

Last updated Aug 3, 2026