diff --git a/scripts/replay-combat.js b/scripts/replay-combat.js index c65dc55..e92c8af 100644 --- a/scripts/replay-combat.js +++ b/scripts/replay-combat.js @@ -18,8 +18,8 @@ const { createWsStorage } = require('../src/storage/ws'); const BACKEND = process.env.BACKEND_URL || 'http://127.0.0.1:4001'; const WS_URL = process.env.BACKEND_WS || BACKEND.replace(/^http/, 'ws') + '/ws'; -const ROUNDS = parseInt(process.argv[2], 10) || 100; -const DELAY = parseInt(process.argv[3], 10) || 800; +const ROUNDS = parseInt(process.argv[2], 10) || 10; +const DELAY = parseInt(process.argv[3], 10) || 200; const APP_ID = process.env.REACT_APP_TRACKER_APP_ID || 'ttrpg-initiative-tracker-default'; const PUB = `artifacts/${APP_ID}/public/data`; @@ -110,45 +110,53 @@ async function main() { console.log(`combat started: round ${enc.round}, first=${firstActiveName(enc)}`); await sleep(DELAY); - // main loop - for (let r = 1; r <= ROUNDS; r++) { - enc = await storage.getDoc(encounterPath); - const t = nextTurn(enc); - await storage.updateDoc(encounterPath, t.patch); - enc = { ...enc, ...t.patch }; + // main loop: ROUNDS = full initiative cycles (each round = all participants act). + const DAMAGERS = ['Fighter', 'Cleric', 'Rogue', 'Wolf', 'Goblin1', 'Goblin2', 'OrcBoss']; + const TARGETS = ['Goblin1', 'Goblin2', 'Wolf', 'OrcBoss', 'Fighter', 'Cleric', 'Rogue', 'Merchant']; + let turnInRound = 0; + let prevRound = enc.round; + let totalTurns = 0; - if (r % 2 === 0) { + for (let roundN = 1; roundN <= ROUNDS; roundN++) { + // advance initiative until round counter ticks (full cycle done). + const cap = (enc.participants.length + 1) * 2; + let guard = 0; + while (enc.round < roundN + 1 && guard < cap) { enc = await storage.getDoc(encounterPath); - const orc = enc.participants.find(p => p.name === 'OrcBoss' && p.currentHp > 0); - if (orc) { - const h = applyHpChange(enc, orc.id, 'damage', 5); - if (h.patch) { await storage.updateDoc(encounterPath, h.patch); enc = { ...enc, ...h.patch }; } - } - } - if (r % 3 === 0) { - enc = await storage.getDoc(encounterPath); - const cleric = enc.participants.find(p => p.name === 'Cleric' && p.currentHp > 0); - if (cleric) { - const h = applyHpChange(enc, cleric.id, 'heal', 3); - if (h.patch) { await storage.updateDoc(encounterPath, h.patch); enc = { ...enc, ...h.patch }; } - } - } - if (r % 5 === 0) { - enc = await storage.getDoc(encounterPath); - const fighter = enc.participants.find(p => p.name === 'Fighter' && p.currentHp > 0); - if (fighter) { - const c = toggleCondition(enc, fighter.id, 'stunned'); - if (c.patch) { await storage.updateDoc(encounterPath, c.patch); enc = { ...enc, ...c.patch }; } - } - } + const t = nextTurn(enc); + await storage.updateDoc(encounterPath, t.patch); + enc = { ...enc, ...t.patch }; + totalTurns++; + turnInRound++; - enc = await storage.getDoc(encounterPath); - console.log(`round ${r}: current=${firstActiveName(enc)} | round=${enc.round}`); - await sleep(DELAY); + // visible action: current turn actor damages a random living target. + const actorName = firstActiveName(enc); + const livingTargets = enc.participants.filter( + p => p.currentHp > 0 && p.name !== actorName && TARGETS.includes(p.name) + ); + if (livingTargets.length > 0 && DAMAGERS.includes(actorName)) { + const tgt = livingTargets[Math.floor(Math.random() * livingTargets.length)]; + const dmg = 3 + Math.floor(Math.random() * 8); // 3-10 + const h = applyHpChange(enc, tgt.id, 'damage', dmg); + if (h.patch) { + await storage.updateDoc(encounterPath, h.patch); + enc = { ...enc, ...h.patch }; + console.log(` ${actorName} → ${tgt.name} (-${dmg}, hp=${tgt.currentHp - dmg})`); + } + } + console.log(` turn ${turnInRound} (round ${enc.round}): ${actorName}`); + await sleep(DELAY); + guard++; + if (!enc.isStarted) { console.log('combat auto-ended'); break; } + } if (!enc.isStarted) { console.log('combat auto-ended'); break; } + console.log(`--- round ${roundN} complete (${turnInRound} turns total) ---`); + turnInRound = 0; } + console.log(`replay: ${totalTurns} total turns across ${ROUNDS} rounds`); + // end enc = await storage.getDoc(encounterPath); if (enc.isStarted) {