Feature: add town systems, saves, recovery, and level progression

This commit is contained in:
Keith Solomon
2026-03-18 19:37:01 -05:00
parent a4d2890cd9
commit 8600f611a6
22 changed files with 2434 additions and 16 deletions
+71
View File
@@ -0,0 +1,71 @@
import { describe, expect, it } from "vitest";
import { sampleContentPack } from "@/data/sampleContentPack";
import { createStartingAdventurer } from "./character";
import {
applyLevelProgression,
getLevelForXp,
getNextLevelXpThreshold,
getXpThresholdForLevel,
} from "./progression";
function createAdventurer() {
return createStartingAdventurer(sampleContentPack, {
name: "Aster",
weaponId: "weapon.short-sword",
armourId: "armour.leather-vest",
scrollId: "scroll.lesser-heal",
});
}
describe("level progression rules", () => {
it("uses linear xp thresholds for the current MVP ruleset", () => {
expect(getXpThresholdForLevel(1)).toBe(0);
expect(getXpThresholdForLevel(2)).toBe(8);
expect(getXpThresholdForLevel(3)).toBe(16);
expect(getNextLevelXpThreshold(1)).toBe(8);
expect(getLevelForXp(0)).toBe(1);
expect(getLevelForXp(8)).toBe(2);
expect(getLevelForXp(16)).toBe(3);
});
it("levels up immediately once xp crosses a threshold", () => {
const adventurer = createAdventurer();
adventurer.xp = 8;
adventurer.hp.current = 7;
const result = applyLevelProgression({
content: sampleContentPack,
adventurer,
at: "2026-03-18T10:00:00.000Z",
});
expect(result.adventurer.level).toBe(2);
expect(result.adventurer.hp.max).toBe(12);
expect(result.adventurer.hp.current).toBe(9);
expect(result.adventurer.manoeuvreIds).toContain("manoeuvre.sweeping-cut");
expect(result.levelUps).toEqual([
expect.objectContaining({
previousLevel: 1,
newLevel: 2,
hpGained: 2,
unlockedManoeuvreIds: ["manoeuvre.sweeping-cut"],
}),
]);
});
it("leaves the adventurer unchanged when no threshold is crossed", () => {
const adventurer = createAdventurer();
adventurer.xp = 7;
const result = applyLevelProgression({
content: sampleContentPack,
adventurer,
});
expect(result.adventurer.level).toBe(1);
expect(result.adventurer.hp.max).toBe(10);
expect(result.levelUps).toEqual([]);
});
});