Skip to content

Commit

Permalink
Fix max damage calculations when you mix exploding with conditional b…
Browse files Browse the repository at this point in the history
…onus (#53)

## Fixes

- Fix max damage calculations when you mix exploding modifier with conditional bonus modifier (that applies on the same characteristic)
    - It was incorrectly applying the conditional bonus to the exploded values
  • Loading branch information
damonhook authored May 21, 2020
1 parent 7624014 commit f9da7f3
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 18 deletions.
6 changes: 6 additions & 0 deletions api/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ export const getCharacteristic = (val: string): Characteristic | null => {
return null;
};

export const getCharacteristicsAfter = (characteristic: Characteristic): Characteristic[] => {
const values = Object.keys(Characteristic).map((key) => Characteristic[key]);
const index = values.indexOf(characteristic);
return values.slice(index + 1);
};

export const SAVES = [2, 3, 4, 5, 6, 0];
44 changes: 29 additions & 15 deletions api/processors/maxDamageProcessor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Characteristic as C } from '../constants';
import { Characteristic as C, getCharacteristicsAfter } from '../constants';
import { MODIFIERS as m } from '../models/modifiers';
import type WeaponProfile from '../models/weaponProfile';

Expand Down Expand Up @@ -31,9 +31,22 @@ export default class MaxDamageProcessor {

private getMaxDamageForModel(): number {
const { attacks, damage } = this.profile;
const attacksMax = this.resolveExplodingModifiers(attacks.max + this.resolveBonusModifiers(C.ATTACKS));
const damageMax = this.resolveMortalWounds(damage.max + this.resolveBonusModifiers(C.DAMAGE));
return attacksMax * damageMax;
const baseAttacks = attacks.max + this.resolveBonusModifiers(C.ATTACKS);

const baseDamage = this.resolveMortalWounds(damage.max + this.resolveBonusModifiers(C.DAMAGE));
const fullConditionalDamage = baseDamage + this.getConditionalBonusDamage(null, C.DAMAGE);

const baseAttacksDamage = baseAttacks * fullConditionalDamage;

return m.EXPLODING.availableCharacteristics.reduce((acc, c) => {
const mod = this.profile.modifiers.getModifier(m.EXPLODING, c);
if (mod && mod.extraHits.max > 0) {
const extraHits = baseAttacks * mod.extraHits.max;
const conditionalDamage = baseDamage + this.getConditionalBonusDamage(c, C.DAMAGE);
return acc + extraHits * conditionalDamage;
}
return acc;
}, baseAttacksDamage);
}

private resolveBonusModifiers(characteristic: C): number {
Expand All @@ -42,25 +55,26 @@ export default class MaxDamageProcessor {
if (modList && modList.length) {
bonus += modList.reduce((acc, mod) => acc + mod.bonus.max, 0);
}
return bonus;
}

m.CONDITIONAL_BONUS.availableCharacteristics.forEach((c) => {
private getConditionalBonusDamage(fromCharacteristic: C | null, forCharacteristic: C): number {
let bonus = 0;
let { availableCharacteristics } = m.CONDITIONAL_BONUS;
if (fromCharacteristic !== null) {
availableCharacteristics = availableCharacteristics.filter((val) =>
getCharacteristicsAfter(fromCharacteristic).includes(val),
);
}
availableCharacteristics.forEach((c) => {
const mod = this.profile.modifiers.getModifier(m.CONDITIONAL_BONUS, c);
if (mod && mod.bonusToCharacteristic === characteristic) {
if (mod && mod.bonusToCharacteristic === forCharacteristic) {
bonus += mod.bonus.max;
}
});

return bonus;
}

private resolveExplodingModifiers(numAttacks: number): number {
return m.EXPLODING.availableCharacteristics.reduce((acc, c) => {
const mod = this.profile.modifiers.getModifier(m.EXPLODING, c);
if (mod && mod.extraHits.max > 0) return acc + numAttacks * mod.extraHits.max;
return acc;
}, numAttacks);
}

private resolveMortalWounds(currentMax: number): number {
let cumulativeMax = currentMax;
let discreteMax = 0;
Expand Down
5 changes: 5 additions & 0 deletions api/tests/maxDamageProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,9 @@ describe('MaxDamageProcessor', () => {
test('Rattling Gunners', () => {
expect(u.rattlingGunners.maxDamage()).toEqual(12);
});

test('Edge Cases', () => {
expect(u.explodingAndConditionalSame.maxDamage()).toEqual(21);
expect(u.explodingAndConditionalDifferent.maxDamage()).toEqual(27);
});
});
39 changes: 39 additions & 0 deletions api/tests/utils/units.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,42 @@ export const plagueMonksOld = new Unit('Plague Monks (pre Dec 2019 FAQ)', [
export const rattlingGunners = new Unit('Rattling Gunners', [
new WeaponProfile(1, DiceValue.parse('2D6'), 4, 4, 1, 1, []),
]);

// #region edge cases
export const explodingAndConditionalSame = new Unit('Exploding And Conditional (Same Characteristic)', [
new WeaponProfile(1, 3, 3, 4, 1, 2, [
new m.EXPLODING({
characteristic: C.TO_HIT,
on: 6,
extraHits: 2,
unmodified: true,
}),
new m.CONDITIONAL_BONUS({
characteristic: C.TO_HIT,
bonus: 1,
unmodified: true,
bonusToCharacteristic: C.DAMAGE,
}),
]),
]);

export const explodingAndConditionalDifferent = new Unit(
'Exploding And Conditional (Different Characteristic)',
[
new WeaponProfile(1, 3, 3, 4, 1, 2, [
new m.EXPLODING({
characteristic: C.TO_HIT,
on: 6,
extraHits: 2,
unmodified: true,
}),
new m.CONDITIONAL_BONUS({
characteristic: C.TO_WOUND,
bonus: 1,
unmodified: true,
bonusToCharacteristic: C.DAMAGE,
}),
]),
],
);
// #endregion
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "statshammer",
"version": "2.1.0",
"version": "2.1.1",
"private": true,
"proxy": "http://localhost:5000/",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"*",
"."
],
"version": "2.1.0",
"version": "2.1.1",
"npmClient": "yarn"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "statshammer-express",
"private": true,
"version": "2.1.0",
"version": "2.1.1",
"engines": {
"node": "12.16.3",
"yarn": "1.22.4"
Expand Down

0 comments on commit f9da7f3

Please sign in to comment.