diff --git a/server/src/routes/initiative.ts b/server/src/routes/initiative.ts index f826ba0..cc8720b 100644 --- a/server/src/routes/initiative.ts +++ b/server/src/routes/initiative.ts @@ -103,6 +103,55 @@ async function insertRollLog( return saved[0]; } +// ── Death timer ──────────────────────────────────────────────────────────── + +async function tickDeathTimers(io: Server, campaignId: number): Promise { + // Find all living characters in this campaign with a Dying condition + const [charRows] = await db.execute( + "SELECT id FROM characters WHERE campaign_id = ? AND is_dead = FALSE", + [campaignId] + ); + if (charRows.length === 0) return; + + const charIds = (charRows as RowDataPacket[]).map((r) => r.id as number); + const placeholders = charIds.map(() => "?").join(", "); + + const [dyingRows] = await db.execute( + `SELECT * FROM character_conditions WHERE name = 'Dying' AND character_id IN (${placeholders})`, + charIds + ); + if (dyingRows.length === 0) return; + + for (const condition of dyingRows) { + const newRounds = (condition.rounds_remaining as number) - 1; + + if (newRounds <= 0) { + // Timer expired — remove Dying condition and mark permanently dead + await db.execute("DELETE FROM character_conditions WHERE id = ?", [condition.id]); + await db.execute("UPDATE characters SET is_dead = TRUE WHERE id = ?", [condition.character_id]); + } else { + await db.execute( + "UPDATE character_conditions SET rounds_remaining = ? WHERE id = ?", + [newRounds, condition.id] + ); + } + + // Broadcast updated character to the campaign room + const [charRow] = await db.execute( + "SELECT * FROM characters WHERE id = ?", + [condition.character_id] + ); + const [updatedConditions] = await db.execute( + "SELECT * FROM character_conditions WHERE character_id = ?", + [condition.character_id] + ); + io.to(`campaign:${campaignId}`).emit("character:updated", { + ...charRow[0], + conditions: updatedConditions, + }); + } +} + // ── Handler registration ─────────────────────────────────────────────────── export function registerInitiativeHandlers(io: Server, socket: Socket): void { @@ -291,6 +340,12 @@ export function registerInitiativeHandlers(io: Server, socket: Socket): void { }); await saveCombats(data.campaignId, updated); broadcast(io, socket, data.campaignId, updated, true); + + // Tick death timers when the party's turn begins + const flippedCombat = updated.find((c) => c.id === data.combatId); + if (flippedCombat && flippedCombat.current_side === "party") { + await tickDeathTimers(io, data.campaignId); + } } catch (err) { console.error("[initiative]", err); }