feat: auto-start Dying condition when HP hits 0, clear when HP recovers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Aaron Wood 2026-04-12 01:23:21 -04:00
parent ee6026b8b4
commit d4790edc1a

View file

@ -6,6 +6,7 @@ import db from "../db.js";
import { broadcastToCampaign } from "../socket.js"; import { broadcastToCampaign } from "../socket.js";
import { parseJson } from "../utils/parseJson.js"; import { parseJson } from "../utils/parseJson.js";
import { requireAuth } from "../auth/middleware.js"; import { requireAuth } from "../auth/middleware.js";
import { rollDice } from "../dice.js";
type CampaignParams = ParamsDictionary & { campaignId: string }; type CampaignParams = ParamsDictionary & { campaignId: string };
@ -192,7 +193,7 @@ router.patch("/:id", requireAuth, async (req, res) => {
"name", "class", "ancestry", "level", "xp", "hp_current", "hp_max", "name", "class", "ancestry", "level", "xp", "hp_current", "hp_max",
"ac", "alignment", "title", "notes", "background", "deity", "languages", "ac", "alignment", "title", "notes", "background", "deity", "languages",
"gp", "sp", "cp", "gear_slots_max", "overrides", "color", "luck_token", "gp", "sp", "cp", "gear_slots_max", "overrides", "color", "luck_token",
"torch_lit_at", "torch_lit_at", "is_dead",
]; ];
const updates: string[] = []; const updates: string[] = [];
@ -218,6 +219,48 @@ router.patch("/:id", requireAuth, async (req, res) => {
return; return;
} }
// Auto-start or clear Dying condition based on HP change
if (req.body.hp_current !== undefined) {
const newHp = Number(req.body.hp_current);
if (newHp <= 0) {
// Check if already dying or permanently dead
const [dyingRows] = await db.execute<RowDataPacket[]>(
"SELECT id FROM character_conditions WHERE character_id = ? AND name = 'Dying'",
[id]
);
const [deadRows] = await db.execute<RowDataPacket[]>(
"SELECT is_dead FROM characters WHERE id = ?",
[id]
);
const isAlreadyDying = dyingRows.length > 0;
const isAlreadyDead = Boolean(deadRows[0]?.is_dead);
if (!isAlreadyDying && !isAlreadyDead) {
// Roll 1d4, add CON modifier, clamp to minimum 1
const d4 = rollDice("1d4");
const [statRows] = await db.execute<RowDataPacket[]>(
"SELECT value FROM character_stats WHERE character_id = ? AND stat_name = 'CON'",
[id]
);
const conValue = (statRows[0]?.value as number) ?? 10;
const conMod = Math.floor((conValue - 10) / 2);
const roundsRemaining = Math.max(1, d4.total + conMod);
await db.execute(
"INSERT INTO character_conditions (character_id, name, description, rounds_remaining) VALUES (?, 'Dying', '', ?)",
[id, roundsRemaining]
);
}
} else {
// HP above 0: remove any Dying condition (character was healed)
await db.execute(
"DELETE FROM character_conditions WHERE character_id = ? AND name = 'Dying'",
[id]
);
}
}
const [rows] = await db.execute<RowDataPacket[]>( const [rows] = await db.execute<RowDataPacket[]>(
"SELECT * FROM characters WHERE id = ?", "SELECT * FROM characters WHERE id = ?",
[id] [id]