replay: loop by real rounds, visible damage each turn, faster default
- ROUNDS now = full initiative cycles (not turns). Each round advances initiative until round counter ticks (all participants act). - Visible damage: current actor hits random living target for 3-10 dmg. Player view sees HP bars change live. - Default delay 200ms (was 800ms). - Reproduces M4 skip bug: rounds shrink as participants die (8→7→2→1). - Label accuracy: 'turn N (round X)'.
This commit is contained in:
+37
-29
@@ -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,44 +110,52 @@ 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++) {
|
||||
// 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;
|
||||
|
||||
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 t = nextTurn(enc);
|
||||
await storage.updateDoc(encounterPath, t.patch);
|
||||
enc = { ...enc, ...t.patch };
|
||||
totalTurns++;
|
||||
turnInRound++;
|
||||
|
||||
if (r % 2 === 0) {
|
||||
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 }; }
|
||||
// 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})`);
|
||||
}
|
||||
}
|
||||
|
||||
enc = await storage.getDoc(encounterPath);
|
||||
console.log(`round ${r}: current=${firstActiveName(enc)} | round=${enc.round}`);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user