Add 2014 DMG encounter difficulty calculation
Support the 2014 DMG encounter difficulty as an alternative to the 5.5e system behind the existing Rules Edition toggle. The 2014 system uses Easy/Medium/Hard/Deadly thresholds, an encounter multiplier based on monster count, and party size adjustment (×0.5–×5 range). - Extract RulesEdition to its own domain module - Refactor DifficultyTier to abstract numeric values (0–3) - Restructure DifficultyResult with thresholds array - Add 2014 XP thresholds table and encounter multiplier logic - Wire edition from context into difficulty hooks - Edition-aware labels in indicator and breakdown panel - Show multiplier, adjusted XP, and party size note for 2014 - Rename settings label from "Conditions" to "Rules Edition" - Update spec 008 with issue #23 requirements Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,13 @@ import "@testing-library/jest-dom/vitest";
|
||||
|
||||
import type { Creature, CreatureId, PlayerCharacter } from "@initiative/domain";
|
||||
import { combatantId, creatureId, playerCharacterId } from "@initiative/domain";
|
||||
import { cleanup, render, screen, waitFor } from "@testing-library/react";
|
||||
import {
|
||||
cleanup,
|
||||
render,
|
||||
renderHook,
|
||||
screen,
|
||||
waitFor,
|
||||
} from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { createTestAdapters } from "../../__tests__/adapters/in-memory-adapters.js";
|
||||
@@ -13,6 +19,7 @@ import {
|
||||
buildEncounter,
|
||||
} from "../../__tests__/factories/index.js";
|
||||
import { AllProviders } from "../../__tests__/test-providers.js";
|
||||
import { useRulesEdition } from "../../hooks/use-rules-edition.js";
|
||||
import { DifficultyBreakdownPanel } from "../difficulty-breakdown-panel.js";
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -279,6 +286,63 @@ describe("DifficultyBreakdownPanel", () => {
|
||||
expect(container.innerHTML).toBe("");
|
||||
});
|
||||
|
||||
it("shows 4 threshold columns for 2014 edition", async () => {
|
||||
const { result: editionResult } = renderHook(() => useRulesEdition());
|
||||
editionResult.current.setEdition("5e");
|
||||
|
||||
try {
|
||||
renderPanel({
|
||||
encounter: defaultEncounter(),
|
||||
playerCharacters: defaultPCs,
|
||||
creatures: new Map([[goblinCreature.id, goblinCreature]]),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Easy:", { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText("Med:", { exact: false })).toBeInTheDocument();
|
||||
expect(screen.getByText("Hard:", { exact: false })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("Deadly:", { exact: false }),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
} finally {
|
||||
editionResult.current.setEdition("5.5e");
|
||||
}
|
||||
});
|
||||
|
||||
it("shows multiplier and adjusted XP for 2014 edition", async () => {
|
||||
const { result: editionResult } = renderHook(() => useRulesEdition());
|
||||
editionResult.current.setEdition("5e");
|
||||
|
||||
try {
|
||||
renderPanel({
|
||||
encounter: defaultEncounter(),
|
||||
playerCharacters: defaultPCs,
|
||||
creatures: new Map([[goblinCreature.id, goblinCreature]]),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Monster XP")).toBeInTheDocument();
|
||||
// 1 PC (<3) triggers party size adjustment
|
||||
expect(screen.getByText("Adjusted for 1 PC")).toBeInTheDocument();
|
||||
});
|
||||
} finally {
|
||||
editionResult.current.setEdition("5.5e");
|
||||
}
|
||||
});
|
||||
|
||||
it("shows Net Monster XP for 5.5e edition (no multiplier)", async () => {
|
||||
renderPanel({
|
||||
encounter: defaultEncounter(),
|
||||
playerCharacters: defaultPCs,
|
||||
creatures: new Map([[goblinCreature.id, goblinCreature]]),
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Net Monster XP")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it("calls onClose when Escape is pressed", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onClose = vi.fn();
|
||||
|
||||
Reference in New Issue
Block a user