diff --git a/TODO.md b/TODO.md index f04ce7a..cbd8a64 100644 --- a/TODO.md +++ b/TODO.md @@ -9,12 +9,13 @@ fullscreen and dont lock on main app dm view and the no-game-player view also better vert tab layout - labelt friendly -needs AC for players dude +x needs AC for players dude and quick entry hp hp do not carry from encounter to ecnounter!!! - +^feature to add back to campaign character after encounter? +maybe campaign toggle in charc section (choosing each end is DM overload will be missed forgotten done wront) hp wont go over max and no temp hp support diff --git a/shared/tests/turn.ac.test.js b/shared/tests/turn.ac.test.js new file mode 100644 index 0000000..4ae1ab8 --- /dev/null +++ b/shared/tests/turn.ac.test.js @@ -0,0 +1,35 @@ +// AC field on participant + builders. +const shared = require('@ttrpg/shared'); +const { makeParticipant, buildMonsterParticipant, buildCharacterParticipant, rollHpFormula } = shared; + +describe('AC field', () => { + test('makeParticipant accepts ac', () => { + const p = makeParticipant({ name: 'Orc', type: 'monster', initiative: 10, maxHp: 15, currentHp: 15, ac: 13 }); + expect(p.ac).toBe(13); + }); + + test('makeParticipant ac defaults null', () => { + const p = makeParticipant({ name: 'Orc', type: 'monster', initiative: 10, maxHp: 15, currentHp: 15 }); + expect(p.ac).toBeNull(); + }); + + test('buildMonsterParticipant accepts ac', () => { + const { participant } = buildMonsterParticipant({ name: 'Goblin', maxHp: 7, initMod: 2, ac: 15 }); + expect(participant.ac).toBe(15); + }); + + test('buildMonsterParticipant ac defaults null', () => { + const { participant } = buildMonsterParticipant({ name: 'Goblin', maxHp: 7 }); + expect(participant.ac).toBeNull(); + }); + + test('buildCharacterParticipant accepts character ac', () => { + const { participant } = buildCharacterParticipant({ id: 'c1', name: 'Fighter', defaultMaxHp: 30, defaultInitMod: 2, defaultAc: 18 }); + expect(participant.ac).toBe(18); + }); + + test('buildCharacterParticipant ac defaults null when unset', () => { + const { participant } = buildCharacterParticipant({ id: 'c1', name: 'Fighter', defaultMaxHp: 30 }); + expect(participant.ac).toBeNull(); + }); +}); diff --git a/shared/turn.js b/shared/turn.js index 1046286..d2a1672 100644 --- a/shared/turn.js +++ b/shared/turn.js @@ -361,6 +361,7 @@ function makeParticipant(opts) { deathSaveSuccesses: opts.deathSaveSuccesses || 0, deathSaveFailures: opts.deathSaveFailures || 0, hpFormula: opts.hpFormula || null, + ac: opts.ac !== undefined ? opts.ac : null, }; } @@ -377,12 +378,13 @@ function buildCharacterParticipant(character) { initiative: finalInitiative, maxHp, currentHp: maxHp, + ac: character.defaultAc !== undefined ? character.defaultAc : null, }), roll: { roll: initiativeRoll, mod: modifier, total: finalInitiative }, }; } -function buildMonsterParticipant({ name, maxHp, initMod, asNpc, hpFormula }) { +function buildMonsterParticipant({ name, maxHp, initMod, asNpc, hpFormula, ac }) { const initiativeRoll = rollD20(); const modifier = initMod !== undefined ? initMod : MONSTER_DEFAULT_INIT_MOD; const finalInitiative = initiativeRoll + modifier; @@ -406,6 +408,7 @@ function buildMonsterParticipant({ name, maxHp, initMod, asNpc, hpFormula }) { maxHp: hp, currentHp: hp, hpFormula: hpFormula || null, + ac: ac !== undefined ? ac : null, }), roll: { roll: initiativeRoll, mod: modifier, total: finalInitiative, hpRoll }, }; diff --git a/src/App.js b/src/App.js index 07a2559..7d10402 100644 --- a/src/App.js +++ b/src/App.js @@ -583,6 +583,7 @@ function EditParticipantModal({ participant, onClose, onSave }) { const [currentHp, setCurrentHp] = useState(participant.currentHp); const [maxHp, setMaxHp] = useState(participant.maxHp); const [hpFormula, setHpFormula] = useState(participant.hpFormula || ''); + const [ac, setAc] = useState(participant.ac != null ? participant.ac : ''); const [asNpc, setAsNpc] = useState(participant.type === 'npc'); const handleSubmit = (e) => { @@ -602,6 +603,7 @@ function EditParticipantModal({ participant, onClose, onSave }) { currentHp: finalCurrentHp, maxHp: finalMaxHp, hpFormula: (participant.type === 'monster' || participant.type === 'npc') ? (hpFormula.trim() || null) : null, + ac: ac !== '' ? parseInt(ac, 10) : null, type: participant.type === 'monster' || participant.type === 'npc' ? (asNpc ? 'npc' : 'monster') : participant.type, }); }; @@ -618,15 +620,27 @@ function EditParticipantModal({ participant, onClose, onSave }) { className="mt-1 block w-full px-3 py-2 bg-stone-800 border border-stone-700 rounded-md shadow-sm focus:outline-none focus:ring-amber-600 focus:border-amber-600 sm:text-sm text-white" /> -