'use strict'; const shared = require('@ttrpg/shared'); const { makeParticipant, startEncounter, nextTurn, applyHpChange, deathSave } = shared; const { mockCtx } = require('./_helpers'); function pc(id, init, extra = {}) { return makeParticipant({ id, name: id, type: 'character', initiative: init, maxHp: 100, currentHp: 100, ...extra, }); } function enc(ps) { return { name:'t', participants:ps, isStarted:false, isPaused:false, round:0, currentTurnParticipantId:null, turnOrderIds:[] }; } const byId = (e, id) => e.participants.find(p => p.id === id); describe('death state: participant remains in initiative and encounter', () => { test('dropping to 0 keeps PC active, in participants, and in turnOrderIds', async () => { const { ctx } = mockCtx(); let e = enc([pc('a', 20), pc('b', 15), pc('c', 10)]); e = await startEncounter(e, ctx); const orderBefore = e.turnOrderIds.slice(); e = await applyHpChange(e, 'b', 'damage', 100, ctx); expect(byId(e, 'b').status).toBe('dying'); expect(byId(e, 'b').isActive).not.toBe(false); expect(e.participants.map(p => p.id)).toContain('b'); expect(e.turnOrderIds).toEqual(orderBefore); }); test('dead PC remains in participants and turnOrderIds after three failures', async () => { const { ctx } = mockCtx(); let e = enc([pc('a', 20), pc('b', 15), pc('c', 10)]); e = await startEncounter(e, ctx); const orderBefore = e.turnOrderIds.slice(); e = await applyHpChange(e, 'b', 'damage', 100, ctx); e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = (await deathSave(e, 'b', 'fail', ctx)).enc; expect(byId(e, 'b').status).toBe('dead'); expect(byId(e, 'b').isActive).not.toBe(false); expect(e.participants.map(p => p.id)).toContain('b'); expect(e.turnOrderIds).toEqual(orderBefore); }); test('nextTurn may still land on dead PC because DM/tool events may matter', async () => { const { ctx } = mockCtx(); let e = enc([pc('a', 20), pc('b', 15), pc('c', 10)]); e = await startEncounter(e, ctx); e = await applyHpChange(e, 'b', 'damage', 100, ctx); e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = await nextTurn(e, ctx); expect(e.currentTurnParticipantId).toBe('b'); expect(byId(e, 'b').status).toBe('dead'); }); test('deathSave rejected/no-op once dead', async () => { const { ctx } = mockCtx(); let e = enc([pc('a', 20), pc('b', 15)]); e = await startEncounter(e, ctx); e = await applyHpChange(e, 'b', 'damage', 100, ctx); e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = (await deathSave(e, 'b', 'fail', ctx)).enc; e = (await deathSave(e, 'b', 'fail', ctx)).enc; const before = byId(e, 'b'); try { e = (await deathSave(e, 'b', 'fail', ctx)).enc; } catch (err) {} expect(byId(e, 'b')).toMatchObject(before); }); });