Files
initiative/apps/web/src/components/turn-navigation.tsx
Lukas 817cfddabc
All checks were successful
CI / check (push) Successful in 2m18s
CI / build-image (push) Successful in 17s
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>
2026-04-04 14:52:23 +02:00

120 lines
3.4 KiB
TypeScript

import { Redo2, StepBack, StepForward, Trash2, Undo2 } from "lucide-react";
import { useState } from "react";
import { useEncounterContext } from "../contexts/encounter-context.js";
import { useRulesEditionContext } from "../contexts/rules-edition-context.js";
import { useDifficulty } from "../hooks/use-difficulty.js";
import { DifficultyBreakdownPanel } from "./difficulty-breakdown-panel.js";
import {
DifficultyIndicator,
TIER_LABELS_5_5E,
TIER_LABELS_2014,
} from "./difficulty-indicator.js";
import { Button } from "./ui/button.js";
import { ConfirmButton } from "./ui/confirm-button.js";
export function TurnNavigation() {
const {
encounter,
advanceTurn,
retreatTurn,
clearEncounter,
undo,
redo,
canUndo,
canRedo,
} = useEncounterContext();
const difficulty = useDifficulty();
const { edition } = useRulesEditionContext();
const tierLabels = edition === "5e" ? TIER_LABELS_2014 : TIER_LABELS_5_5E;
const [showBreakdown, setShowBreakdown] = useState(false);
const hasCombatants = encounter.combatants.length > 0;
const isAtStart = encounter.roundNumber === 1 && encounter.activeIndex === 0;
const activeCombatant = encounter.combatants[encounter.activeIndex];
return (
<div className="card-glow grid grid-cols-[1fr_minmax(0,auto)_1fr] items-center border-border border-b bg-card px-2 py-3 sm:rounded-lg sm:border sm:px-4">
{/* Left zone: navigation + history + round */}
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="icon"
onClick={retreatTurn}
disabled={!hasCombatants || isAtStart}
title="Previous turn"
aria-label="Previous turn"
>
<StepBack className="h-5 w-5" />
</Button>
<Button
variant="ghost"
size="icon"
onClick={undo}
disabled={!canUndo}
title="Undo"
aria-label="Undo"
>
<Undo2 className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
onClick={redo}
disabled={!canRedo}
title="Redo"
aria-label="Redo"
>
<Redo2 className="h-4 w-4" />
</Button>
<span className="ml-1 rounded-md bg-muted px-2 py-0.5 font-semibold text-foreground text-sm tabular-nums">
R{encounter.roundNumber}
</span>
</div>
{/* Center zone: active combatant name */}
<div className="min-w-0 px-2 text-center text-sm">
{activeCombatant ? (
<span className="truncate font-medium">{activeCombatant.name}</span>
) : (
<span className="text-muted-foreground">No combatants</span>
)}
</div>
{/* Right zone: difficulty + destructive + forward */}
<div className="flex items-center justify-end gap-1">
{difficulty && (
<div className="relative mr-1">
<DifficultyIndicator
result={difficulty}
labels={tierLabels}
onClick={() => setShowBreakdown((prev) => !prev)}
/>
{showBreakdown ? (
<DifficultyBreakdownPanel
onClose={() => setShowBreakdown(false)}
/>
) : null}
</div>
)}
<ConfirmButton
icon={<Trash2 className="h-5 w-5" />}
label="Clear encounter"
onConfirm={clearEncounter}
disabled={!hasCombatants}
className="text-muted-foreground"
/>
<Button
variant="ghost"
size="icon"
onClick={advanceTurn}
disabled={!hasCombatants}
title="Next turn"
aria-label="Next turn"
>
<StepForward className="h-5 w-5" />
</Button>
</div>
</div>
);
}