Fix problem with check target numbers <= 0

The fix has 2 components:
1. The logic for evaluating checks now supports check target numbers<= 0
   by still using a single die in this case
2.  The CheckFactory sets the check target number to 0 even if it would
   be < 0. This is necessary because negative numbers would interfer
   with foundry's math evaluation in rolls and would not be picked up
   correctly.
This commit is contained in:
Johannes Loher 2021-06-26 12:50:55 +02:00
parent ebdc0405d8
commit ff6427f5a9
3 changed files with 118 additions and 14 deletions

View file

@ -48,30 +48,134 @@ describe("evaluateCheck with a single die", () => {
{ result: 20, checkTargetNumber: 4, active: false, discarded: true, failure: true }, { result: 20, checkTargetNumber: 4, active: false, discarded: true, failure: true },
]); ]);
}); });
it("should roll a die even when the checkTargetNumber is 0 and coup on 1", () => {
expect(evaluateCheck([1], 0)).toEqual([
{ result: 1, checkTargetNumber: 0, active: true, discarded: false, success: true, count: 0 },
]);
});
it("should roll a die even when the checkTargetNumber is 0 and fail on values > 1 and < 20", () => {
for (let i = 2; i < 20; i++) {
expect(evaluateCheck([i], 0)).toEqual([
{ result: i, checkTargetNumber: 0, active: false, discarded: true },
]);
}
});
it("should roll a die even when the checkTargetNumber is 0 and fumble on 20", () => {
expect(evaluateCheck([20], 0)).toEqual([
{ result: 20, checkTargetNumber: 0, active: false, discarded: true, failure: true },
]);
});
it("should roll a die even when the checkTargetNumber is < 0 and coup on 1", () => {
expect(evaluateCheck([1], -1)).toEqual([
{ result: 1, checkTargetNumber: -1, active: true, discarded: false, success: true, count: -1 },
]);
});
it("should roll a die even when the checkTargetNumber is < 0 and fail on values > 1 and < 20", () => {
for (let i = 2; i < 20; i++) {
expect(evaluateCheck([i], -1)).toEqual([
{ result: i, checkTargetNumber: -1, active: false, discarded: true },
]);
}
});
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 20", () => {
expect(evaluateCheck([20], -1)).toEqual([
{ result: 20, checkTargetNumber: -1, active: false, discarded: true, failure: true },
]);
});
}); });
describe("evaluateCheck with a single die and coup / fumble modification", () => { describe("evaluateCheck with a single die and coup / fumble modification", () => {
const maximumCoupResult = 2;
const minimumFumbleResult = 19;
it("should assign the checkTargetNumber to the single die and coup on 'maximumCoupResult'", () => { it("should assign the checkTargetNumber to the single die and coup on 'maximumCoupResult'", () => {
expect(evaluateCheck([2], 4, { maximumCoupResult: 2 })).toEqual([ expect(evaluateCheck([maximumCoupResult], 4, { maximumCoupResult })).toEqual([
{ result: 2, checkTargetNumber: 4, active: true, discarded: false, success: true, count: 4 }, {
result: maximumCoupResult,
checkTargetNumber: 4,
active: true,
discarded: false,
success: true,
count: 4,
},
]); ]);
}); });
it("should assign the checkTargetNumber to the single die and not coup on lower edge case '3'", () => { it("should assign the checkTargetNumber to the single die and not coup on lower edge case 'maximumCoupResult + 1'", () => {
expect(evaluateCheck([3], 4, { maximumCoupResult: 2 })).toEqual([ expect(evaluateCheck([maximumCoupResult + 1], 4, { maximumCoupResult })).toEqual([
{ result: 3, checkTargetNumber: 4, active: true, discarded: false }, { result: maximumCoupResult + 1, checkTargetNumber: 4, active: true, discarded: false },
]); ]);
}); });
it("should assign the checkTargetNumber to the single die and fumble on 'minimumFUmbleResultResult'", () => { it("should assign the checkTargetNumber to the single die and fumble on 'minimumFumbleResultResult'", () => {
expect(evaluateCheck([19], 20, { minimumFumbleResult: 19 })).toEqual([ expect(evaluateCheck([minimumFumbleResult], 20, { minimumFumbleResult })).toEqual([
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, failure: true }, { result: minimumFumbleResult, checkTargetNumber: 20, active: true, discarded: false, failure: true },
]); ]);
}); });
it("should assign the checkTargetNumber to the single die and not fumble on upper edge case '18'", () => { it("should assign the checkTargetNumber to the single die and not fumble on upper edge case 'minimumFumbleResult - 1'", () => {
expect(evaluateCheck([18], 20, { minimumFumbleResult: 19 })).toEqual([ expect(evaluateCheck([minimumFumbleResult - 1], 20, { minimumFumbleResult })).toEqual([
{ result: 18, checkTargetNumber: 20, active: true, discarded: false }, { result: minimumFumbleResult - 1, checkTargetNumber: 20, active: true, discarded: false },
]);
});
it("should roll a die even when the checkTargetNumber is 0 and coup on 'maximumCoupResult'", () => {
expect(evaluateCheck([maximumCoupResult], 0, { maximumCoupResult })).toEqual([
{
result: maximumCoupResult,
checkTargetNumber: 0,
active: true,
discarded: false,
success: true,
count: 0,
},
]);
});
it("should roll a die even when the checkTargetNumber is 0 and fail on '> maximumCoupResult and < minimumFumbleResult", () => {
for (let i = maximumCoupResult + 1; i < minimumFumbleResult; i++) {
expect(evaluateCheck([i], 0, { maximumCoupResult, minimumFumbleResult })).toEqual([
{ result: i, checkTargetNumber: 0, active: false, discarded: true },
]);
}
});
it("should roll a die even when the checkTargetNumber is 0 and fumble on 'minimumFumbleResult'", () => {
expect(evaluateCheck([minimumFumbleResult], 0, { minimumFumbleResult })).toEqual([
{ result: minimumFumbleResult, checkTargetNumber: 0, active: false, discarded: true, failure: true },
]);
});
it("should roll a die even when the checkTargetNumber is < 0 and coup on 'maximumCoupResult'", () => {
expect(evaluateCheck([maximumCoupResult], -1, { maximumCoupResult })).toEqual([
{
result: maximumCoupResult,
checkTargetNumber: -1,
active: true,
discarded: false,
success: true,
count: -1,
},
]);
});
it("should roll a die even when the checkTargetNumber is < 0 and fail on '> maximumCoupResult and < minimumFumbleResult", () => {
for (let i = maximumCoupResult + 1; i < minimumFumbleResult; i++) {
expect(evaluateCheck([i], -1, { maximumCoupResult, minimumFumbleResult })).toEqual([
{ result: i, checkTargetNumber: -1, active: false, discarded: true },
]);
}
});
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 'minimumFumbleResult'", () => {
expect(evaluateCheck([minimumFumbleResult], -1, { minimumFumbleResult })).toEqual([
{ result: minimumFumbleResult, checkTargetNumber: -1, active: false, discarded: true, failure: true },
]); ]);
}); });
}); });

View file

@ -113,5 +113,5 @@ function evaluateDiceWithSubChecks(
} }
export function getRequiredNumberOfDice(checkTargetNumber: number): number { export function getRequiredNumberOfDice(checkTargetNumber: number): number {
return Math.ceil(checkTargetNumber / 20); return Math.max(Math.ceil(checkTargetNumber / 20), 1);
} }

View file

@ -45,8 +45,8 @@ class CheckFactory {
); );
} }
createCheckTargetNumberModifier(): string | null { createCheckTargetNumberModifier(): string {
return "v" + (this.checkTargetNumber + this.gmModifier); return "v" + Math.max(this.checkTargetNumber + this.gmModifier, 0);
} }
createCoupFumbleModifier(): string | null { createCoupFumbleModifier(): string | null {