// ponytail: 5e DMG p.82 XP thresholds per character. Single source of truth // for the encounter builder's budget math. The "Easy/Medium/Hard/Deadly" // thresholds are *per character*; the table is indexed by character level. export type Difficulty = "Easy" | "Medium" | "Hard" | "Deadly"; const THRESHOLDS: Record> = { 1: { Easy: 25, Medium: 50, Hard: 75, Deadly: 100 }, 2: { Easy: 50, Medium: 100, Hard: 150, Deadly: 200 }, 3: { Easy: 75, Medium: 150, Hard: 225, Deadly: 400 }, 4: { Easy: 125, Medium: 250, Hard: 375, Deadly: 500 }, 5: { Easy: 250, Medium: 500, Hard: 750, Deadly: 1100 }, 6: { Easy: 300, Medium: 600, Hard: 900, Deadly: 1400 }, 7: { Easy: 350, Medium: 750, Hard: 1100, Deadly: 1700 }, 8: { Easy: 450, Medium: 900, Hard: 1400, Deadly: 2100 }, 9: { Easy: 550, Medium: 1100, Hard: 1600, Deadly: 2400 }, 10: { Easy: 600, Medium: 1200, Hard: 1900, Deadly: 2800 }, 11: { Easy: 800, Medium: 1600, Hard: 2400, Deadly: 3600 }, 12: { Easy: 1000, Medium: 2000, Hard: 3000, Deadly: 4500 }, 13: { Easy: 1100, Medium: 2200, Hard: 3400, Deadly: 5100 }, 14: { Easy: 1250, Medium: 2500, Hard: 3800, Deadly: 5700 }, 15: { Easy: 1400, Medium: 2800, Hard: 4300, Deadly: 6400 }, 16: { Easy: 1600, Medium: 3200, Hard: 4800, Deadly: 7200 }, 17: { Easy: 2000, Medium: 3900, Hard: 5900, Deadly: 8800 }, 18: { Easy: 2100, Medium: 4200, Hard: 6300, Deadly: 9500 }, 19: { Easy: 2400, Medium: 4900, Hard: 7300, Deadly: 10900 }, 20: { Easy: 2800, Medium: 5700, Hard: 8500, Deadly: 12700 }, }; export interface Budget { easy: number; medium: number; hard: number; deadly: number; } /** Compute per-difficulty XP budgets for a party. Clamps level to 1..20. */ export function encounterBudget(partyLevel: number, partySize: number): Budget { const lvl = Math.max(1, Math.min(20, Math.round(partyLevel))); const size = Math.max(1, Math.round(partySize)); const t = THRESHOLDS[lvl]; return { easy: t.Easy * size, medium: t.Medium * size, hard: t.Hard * size, deadly: t.Deadly * size, }; } /** Map an XP total to a difficulty bucket. */ export function difficultyForXp(xp: number, budget: Budget): Difficulty { if (xp >= budget.deadly) return "Deadly"; if (xp >= budget.hard) return "Hard"; if (xp >= budget.medium) return "Medium"; if (xp >= budget.easy) return "Easy"; return "Easy"; } export const DIFFICULTY_COLOR: Record = { Easy: "var(--color-success)", Medium: "var(--color-info)", Hard: "var(--color-gold-bright)", Deadly: "var(--color-danger)", };