✨Feature: add character creation logic and tests
- Implement character creation functions to handle adventurer setup. - Add validation for adventurer name and selection of starting items. - Introduce new items in sample content pack: Flint and Steel, Pouch, Wax Sealing Kit, and Backpack. - Create tests for character creation functionality to ensure valid options and error handling. ✨Feature: implement dice rolling mechanics and tests - Add functions for rolling various dice types (d3, d6, 2d6, d66) with validation. - Implement modifier application with clamping for roll results. - Create tests to verify correct behavior of dice rolling functions. ✨Feature: add table lookup functionality and tests - Implement table lookup and resolution logic for defined tables. - Support for modified ranges and fallback to nearest entries. - Create tests to ensure correct table entry resolution based on roll results.
This commit is contained in:
113
src/rules/dice.ts
Normal file
113
src/rules/dice.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { DiceKind, RollResult } from "@/types/rules";
|
||||
|
||||
export type DiceRoller = (sides: number) => number;
|
||||
|
||||
const defaultDiceRoller: DiceRoller = (sides) =>
|
||||
Math.floor(Math.random() * sides) + 1;
|
||||
|
||||
function assertRollInRange(value: number, sides: number) {
|
||||
if (!Number.isInteger(value) || value < 1 || value > sides) {
|
||||
throw new Error(`Dice roller returned invalid value ${value} for d${sides}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function clampTotal(total: number, min: number, max: number) {
|
||||
return Math.max(min, Math.min(max, total));
|
||||
}
|
||||
|
||||
export function rollD3(roller: DiceRoller = defaultDiceRoller): RollResult {
|
||||
const value = roller(6);
|
||||
assertRollInRange(value, 6);
|
||||
const total = Math.ceil(value / 2);
|
||||
|
||||
return {
|
||||
diceKind: "d3",
|
||||
rolls: [value],
|
||||
total,
|
||||
modifiedTotal: total,
|
||||
};
|
||||
}
|
||||
|
||||
export function rollD6(roller: DiceRoller = defaultDiceRoller): RollResult {
|
||||
const value = roller(6);
|
||||
assertRollInRange(value, 6);
|
||||
|
||||
return {
|
||||
diceKind: "d6",
|
||||
rolls: [value],
|
||||
total: value,
|
||||
modifiedTotal: value,
|
||||
};
|
||||
}
|
||||
|
||||
export function roll2D6(roller: DiceRoller = defaultDiceRoller): RollResult {
|
||||
const first = roller(6);
|
||||
const second = roller(6);
|
||||
assertRollInRange(first, 6);
|
||||
assertRollInRange(second, 6);
|
||||
const total = first + second;
|
||||
|
||||
return {
|
||||
diceKind: "2d6",
|
||||
rolls: [first, second],
|
||||
primary: first,
|
||||
secondary: second,
|
||||
total,
|
||||
modifiedTotal: total,
|
||||
};
|
||||
}
|
||||
|
||||
export function rollD66(roller: DiceRoller = defaultDiceRoller): RollResult {
|
||||
const primary = roller(6);
|
||||
const secondary = roller(6);
|
||||
assertRollInRange(primary, 6);
|
||||
assertRollInRange(secondary, 6);
|
||||
const total = primary * 10 + secondary;
|
||||
|
||||
return {
|
||||
diceKind: "d66",
|
||||
rolls: [primary, secondary],
|
||||
primary,
|
||||
secondary,
|
||||
total,
|
||||
modifiedTotal: total,
|
||||
};
|
||||
}
|
||||
|
||||
export function applyRollModifier(
|
||||
roll: RollResult,
|
||||
modifier = 0,
|
||||
limits?: { min: number; max: number },
|
||||
): RollResult {
|
||||
if (roll.total === undefined) {
|
||||
throw new Error("Cannot apply a modifier to a roll without a total.");
|
||||
}
|
||||
|
||||
const nextTotal = roll.total + modifier;
|
||||
const boundedTotal = limits
|
||||
? clampTotal(nextTotal, limits.min, limits.max)
|
||||
: nextTotal;
|
||||
|
||||
return {
|
||||
...roll,
|
||||
modifier,
|
||||
modifiedTotal: boundedTotal,
|
||||
clamped: limits ? boundedTotal !== nextTotal : false,
|
||||
};
|
||||
}
|
||||
|
||||
export function rollDice(
|
||||
diceKind: DiceKind,
|
||||
roller: DiceRoller = defaultDiceRoller,
|
||||
): RollResult {
|
||||
switch (diceKind) {
|
||||
case "d3":
|
||||
return rollD3(roller);
|
||||
case "d6":
|
||||
return rollD6(roller);
|
||||
case "2d6":
|
||||
return roll2D6(roller);
|
||||
case "d66":
|
||||
return rollD66(roller);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user