From 160788d63bd3b674b2dc7645b3bd619c47edbe62 Mon Sep 17 00:00:00 2001 From: itsamejms Date: Mon, 7 Sep 2026 10:28:30 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20condition=20durations=20=E2=80=94=20'He?= =?UTF-8?q?xed=20(3)'=20ticks=20down=20each=20turn,=20drops=20at=200?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Numbered conditions decrement when the combatant's turn starts and expire at zero; plain conditions still toggle manually. The custom condition input hints the syntax. Roadmap 1.8 — Phase 1 complete. --- src/components/InitiativeTracker.tsx | 29 +++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/components/InitiativeTracker.tsx b/src/components/InitiativeTracker.tsx index 92b42b9..125e70a 100644 --- a/src/components/InitiativeTracker.tsx +++ b/src/components/InitiativeTracker.tsx @@ -157,10 +157,34 @@ export function InitiativeTracker() { ); }, []); + // ponytail: conditions written as "Name (N)" auto-expire: the number + // decrements each time that combatant's turn starts, and the condition + // drops at zero. Plain conditions run until toggled off manually. + function tickDurations(id: string) { + setCombatants((prev) => + prev.map((c) => { + if (c.id !== id) return c; + const next: string[] = []; + for (const cn of c.conditions) { + const m = cn.match(/^(.*?)\s*\((\d+)\)$/); + if (!m) { + next.push(cn); + continue; + } + const n = parseInt(m[2], 10); + if (n > 1) next.push(`${m[1]} (${n - 1})`); + // n === 1 → expired, dropped + } + return { ...c, conditions: next }; + }), + ); + } + const nextTurn = useCallback(() => { if (combatants.length === 0) return; if (!activeId) { setActiveId(combatants[0].id); + tickDurations(combatants[0].id); bus.emit(Events.LogEntry, { text: `⚔ Combat starts — ${combatants[0].name}`, tag: "#combat" }); return; } @@ -168,9 +192,11 @@ export function InitiativeTracker() { if (idx === combatants.length - 1) { setRound((r) => r + 1); setActiveId(combatants[0].id); + tickDurations(combatants[0].id); bus.emit(Events.LogEntry, { text: `⚔ Round ${round + 1} — ${combatants[0].name}'s turn`, tag: "#combat" }); } else { setActiveId(combatants[idx + 1].id); + tickDurations(combatants[idx + 1].id); bus.emit(Events.LogEntry, { text: `⚔ ${combatants[idx + 1].name}'s turn`, tag: "#combat" }); } setTimerLeft(TURN_SECONDS); // ponytail: reset the per-turn timer. @@ -513,7 +539,8 @@ export function InitiativeTracker() {
setCondInput((p) => ({ ...p, [c.id]: e.target.value }))} onKeyDown={(e) => {