Imagine the following scenario:

  1. A weapon does D points of (constant) damage per hit.
  2. An armor has R damage resistance to that weapon (e.g., R = 50 means only 50% of damage taken).
  3. How much damage will the armor take?

In the current algorithm, we use integer division and round-off (as opposed to truncation):

damage-taken = floor(((D * R) + 50) / 100)

For large values of D, this equation leads to reasonable damage, but if D is small, then the effect of round-off becomes apparent.

Let D = 5 and R = 50. At floating point precision, we expect 2.5 points of damage per hit. But when plugged into the formula above, we get:

damage-taken = floor(((5 * 50) + 50) / 100) = floor((250 + 50) / 100) = 3

However, if D = 5 and R = 49, we expect 2.45, but end up with 2 after round off. This is exactly how round-off is supposed to work.

If R were randomly distributed, then this effect would cancel out. Unfortunately, because damage resistance is much more likely to be 50 than 49, we end up with a bias towards higher damage for low-damage weapons.

The obvious solution is to use floating-point numbers for hit points and damage, but that would require large-scale changes all through the code. Instead, we need to use a stochastic round off:

First we compute both the truncated integer value of the division, and the remainder. We then randomly add 1 to the result with frequency proportional to remainder. Something like:

damage-int = floor(D * R / 100)
damage-remainder = mod(D * R, 100)

if random(1, 100) <= damage-remainder
   damage-taken = damage-int + 1
else
   damage-taken = damage-int

Edit: I should mention that the error here is small. The error is worst when damage adj is around a multiple of 25 (25, 50, 75, or their partners: 24, 49, 74). At those values the error is about 15%, meaning that a low-damage weapon will do 15% more damage than it should.

But other damage adj values don't have errors that large. For something like reactive armor, which has 44 damage adj against kinetic, the error is only 2%.

Nevertheless, this is a bug in a core mechanic, and thus worth fixing.

the_shrike on 1/19/2016 11:23 PM:

Keep in mind though that low-damage weapons can already have some serious issues doing damage in the first place. This is an important fix, but it may have some unintended repercussions.

This may also affect WMD, although that system tends to produce the opposite problem: rounding down into oblivion.

Edit: on the other hand, this should definitely help specialist armors in the early game.

johnbwatson on 1/20/2016 2:57 AM:

Right now, weapons that fire a large quantity of low damage shots are much more powerful than they ought to be. Armors with high resistance will also become better, which may make particle actually viable. I think this change will be a huge step forward for combat balance.

george moromisato on 1/20/2016 4:25 AM:

@johnbwatson: I'm not sure we'll notice the difference. A resistance of 49 and 50 will show the greatest difference (since that's just at the flip point). But most armors have different resistance values.

For example, reactive armor has 44% damage adj against kinetic. At that value the error is only about 2%.

But I could be wrong.

george moromisato on 1/20/2016 4:37 AM:

@the_shrike: What other serious issues are you referring to (I don't doubt it, I'm just curious)?

As for WMD, you're right. It uses the following formula:

WMD-damage = max(1, floor(WMD-adj * damage / 100))

As you can see, we truncate any fractional HP. We should probably do the same fix to the WMD calculation.

the_shrike on 1/20/2016 4:52 AM:

Checking my notes from old play sessions, it looks like I was misremembering an extreme example (ressurector tau beam weapons VS a high general-resistance Invincible). So that can probably be discounted although there'll be an increase in cases where armors and shields can completely outmatch weapons (and I think Blast Plate may become even more powerful). Because low damage guns will get nerfed by this, general-purpose armors that can already shrug off a lot of weapons will become more of an issue. Still, that's more a problem with overlevelled armor turning up in the early game.

I'd certainly support having the same fix apply to WMD. It would seriously improve all those crappy low-damage, low-WMD weapons, and make it possible to apply fixes from ( https://ministry.kronosaur.com/record.hexm?id=29058 )without having to discard the lower WMD range on low-damage weapons.

nms on 1/20/2016 8:43 AM:

The WMD fix I proposed, Rebalance compartments by making WMD into a damage bonus, involved rescaling internal hp, but that was mainly to avoid rounding issues. If WMD used stochastic rounding, then a close equivalent to my proposal could be implemented simply by getting rid of the minimum of 1 damage and changing the WMD adjustment table to this:

WMD     compartment damage %
 0               33
 1               39
 2               46
 3               53
 4               62
 5               73
 6               85
 7              100

Or some other possible values: https://forums.kronosaur.com/viewtopic.php?f=3&t=6502&p=67208#p67208

george moromisato on 1/22/2016 6:40 AM:

Fixed in 1.7 Alpha 1.