From af165f449184b546d16ec51679bce84d7090cee0 Mon Sep 17 00:00:00 2001 From: david raistrick <1108844+keen99@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:14:06 -0400 Subject: [PATCH] fix(replay): no-op + pointer-crossing reorder picks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replay reorder picker used living[0]→living[1] (HP-sorted). Wolf(20) already before Merchant(19) = no-op drag. Fired every 8 turns = UI animated drag, nothing changed = visual funk. 3 useless Wolf→Merchant drags in round 16-17 log. Also fixed pointer-cross: old picker dragged arbitrary pair. If swap crossed current pointer → ambiguous who-acted semantics (skip/double). New picker: swap two ADJACENT UPCOMING actors (both strictly after current pointer). Always real move, never crosses pointer. 13-round replay: 0 skips, 0 double-acts, 0 order shifts (was 2 skips, 4 double-acts with arbitrary swaps). Note: reorderParticipants itself has no pointer logic — pure drag. Crossing pointer behavior in real app untested (potential BUG-13). --- scripts/replay-combat.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/replay-combat.js b/scripts/replay-combat.js index 09c6f28..0050fe8 100644 --- a/scripts/replay-combat.js +++ b/scripts/replay-combat.js @@ -310,25 +310,25 @@ async function main() { lastPaused = true; } - // 10. reorderParticipants: every 8 turns, drag one past next (DM reorder). + // 10. reorderParticipants: every 8 turns, drag one past another (DM reorder). + // Pick two ADJACENT UPCOMING actors (both strictly after current pointer) + // and swap them. Avoids crossing current pointer — crossing it creates + // ambiguous "who acted this round" semantics (skip/double). Swapping two + // upcoming actors is always safe and still exercises reorder. if (totalTurns % 8 === 0 && lastReorder !== totalTurns) { - const living = enc.participants.filter(p => p.currentHp > 0 && p.isActive !== false); - if (living.length >= 3) { - // drag first past second (same-or-cross init, exercises reorder). - const dragged = living[0]; - const target = living[1]; + const curIdx = enc.turnOrderIds.indexOf(enc.currentTurnParticipantId); + // upcoming = everyone after current in turn order (rest of this round) + const upcomingIds = enc.turnOrderIds.slice(curIdx + 1) + .filter(id => { const p = enc.participants.find(x => x.id === id); return p && p.currentHp > 0 && p.isActive !== false; }); + // swap first adjacent upcoming pair (drag index1 before index0) + if (upcomingIds.length >= 2) { + const target = enc.participants.find(p => p.id === upcomingIds[0]); + const dragged = enc.participants.find(p => p.id === upcomingIds[1]); try { const r = reorderParticipants(enc, dragged.id, target.id); enc = await patch(encounterPath, enc, r, `reorder ${dragged.name}→before ${target.name}`); lastReorder = totalTurns; - } catch (e) { /* same-init only — try same-init pair */ - const sameInit = living.find(p => p !== dragged && p.initiative === dragged.initiative); - if (sameInit) { - const r = reorderParticipants(enc, dragged.id, sameInit.id); - enc = await patch(encounterPath, enc, r, `reorder ${dragged.name}→before ${sameInit.name}`); - lastReorder = totalTurns; - } - } + } catch (e) { /* swap not allowed — skip this round */ } } }