Files
noname-shuying/mengsan/runtime/battle-loop.js
T
2026-09-28 20:58:29 +08:00

88 lines
3.5 KiB
JavaScript

// Track only the turn created by this battle's cooperative loop.
const battleTurns = new WeakMap();
const turnStages = new Set(["phase", "phaseZhunbei", "phaseJudge", "phaseDraw", "phaseUse", "phaseDiscard", "phaseJieshu"]);
export function stopBattleTurn(session, eventManager) {
const turn = battleTurns.get(session);
if (!turn) return;
const belongsToTurn = event => {
const seen = new Set();
while (event && !seen.has(event)) {
if (event === turn) return true;
seen.add(event);
event = event.parent;
}
return false;
};
// Finish phase controllers, not the card/damage/death events currently resolving.
// Those events must unwind before the session can safely dispose players.
for (const event of eventManager.eventStack.slice().reverse()) {
if (turnStages.has(event.name) && belongsToTurn(event)) event.finish();
}
turn.finish();
}
// Candidate derived from local engine phaseLoop; no global replacement.
export async function runPhaseLoop(event, player, session, { game, lib, _status, get, beforeTurn = async () => {} }) {
let num = 1;
let current = player;
while (session.active && !event.finished && current.getSeatNum() === 0) {
current.setSeatNum(num);
current = current.next;
num++;
}
while (session.active && !event.finished) {
if (game.players.includes(event.player)) {
await beforeTurn(event.player);
if (!session.active || event.finished) return;
lib.onphase.forEach((i) => i());
if (!session.active || event.finished) return;
const phase = event.player.phase();
battleTurns.set(session, phase);
try {
event.next.remove(phase);
let isRoundEnd = false;
if (lib.onround.every((i) => i(phase, event.player))) {
isRoundEnd = _status.roundSkipped;
if (_status.isRoundFilter) {
isRoundEnd = _status.isRoundFilter(phase, event.player);
} else if (_status.seatNumSettled) {
const seatNum = event.player.getSeatNum();
if (seatNum != 0) {
if (get.itemtype(_status.lastPhasedPlayer) != "player" || seatNum < _status.lastPhasedPlayer.getSeatNum()) {
isRoundEnd = true;
}
}
} else if (event.player == _status.roundStart) {
isRoundEnd = true;
}
if (isRoundEnd && _status.globalHistory.some((i) => i.isRound)) {
game.log();
await event.trigger("roundEnd");
}
}
if (!session.active || event.finished) { phase.finish(); return; }
event.next.push(phase);
await phase;
} finally {
if (battleTurns.get(session) === phase) battleTurns.delete(session);
}
}
if (!session.active || event.finished) return;
await event.trigger("phaseOver");
if (!session.active || event.finished) return;
const findNext = (current2) => {
const players = game.players.slice(0).concat(game.dead).sort((a, b) => parseInt(a.dataset.position) - parseInt(b.dataset.position));
const position = parseInt(current2.dataset.position);
for (const player2 of players) {
if (parseInt(player2.dataset.position) > position) {
return player2;
}
}
return players[0];
};
event.player = findNext(event.player);
if (!event.player) return;
}
}