There were some issues with the initial Siralim 3 damage equation and as a result, Zack is testing some adjustments to the damage equation today (See Patch 0.023).
This got me thinking about damage equations in general. It seems that a damage equation has two competing goals:
- Make stat increases “feel” awesome (this is afterall a stat-based game)
- Make one-shots rare (the game is less fun if either my creatures are dying instantly, or I am annihilating enemies in one hit)
So I am going to start with the premise that it is okay to have enemy max health affect my damage. (This is potentially controversial, but it is a useful premise if one wants to minimize one-shots).
I think a reasonable approach to limiting one shots is to start with a “natural” damage value and then modify the adjust the value based on enemy health if it is too extreme.
For a game like Siralim with Attack and Defense both in approximately the same range, I think a good “natural” damage equation is:
NatDamage = (alpha * ATTACK - delta * DEFENSE) * DAMAGE MODIFIERS
Here, alpha and delta are parameters than can be tuned as desired. Basically decide how much damage should be done for given ATTACK \ DEFENSE values against a super-high health creature and set alpha and delta appropriately. (For instance, set alpha a bit larger than delta if you want a bit of damage to be dealt when ATTACK is equal to DEFENSE)
Now we get to the interesting part, how do you adjust based on enemy health. My starting assumption here is that a creature should be able to one-shot an enemy if the natural damage is sufficiently high relative to enemy max health. I am going to pick 5 arbitrarily as a constant (this can be adjusted) and say that a creature should one-shot an enemy if the natural damage is at least 5 times the enemy max health.
Therefore, I propose that we calculate:
NatDamagePercent = NatDamage / MaxHealth
Now we want to squish down NatDamagePercent if it is too large, let’s call our adjustment function F()
AdjustedDamagePercent = F( NatDamagePercent )
Then our final damage is:
FinalDamage = AdjustedDamagePercent * MaxHealth
So the interesting question is what is the right adjustment function, F() …
We want F to be the identity function for small inputs (no need to adjust if natural damage is small)
… and we want F(5) = 1
(This is based on the assumption that a one-shot should happen if Natural Damage is five times max health … Feel free to replace 5 with any value you prefer)
One possibility is to use a piece-wise function:
F(x) = x if (x < 0.2)
F(x) = (x + 1) / 6 if (x > 0.2)
F(x) = x if (x < 0.2)
F(x) = SQRT( 0.2* x) if (x > 0.2)
There is likely something more elegant than a piece-wise function, but I think piecewise functions probably provide about the right behavior. If you want something more nuanced, you can always go with a three-part piece-wise function.