fix: preserve unit sprites until their death animation plays in pre-resolved turns
Units doomed to die later in a pre-resolved enemy turn were being removed from the board prematurely during sync, before their death animation could play. Now: - syncUnits only prunes dead views on actual army elimination, not on mid-turn CO powers that would kill units later in the same turn - Added 'loaded'/'joined' event handlers to remove views when units merge into transports at the correct animated moment - setUnitVisibility skips views whose units are already gone from state, preserving mid-replay sprites until their event fires
This commit is contained in:
parent
85996ad02c
commit
07a16f295d
|
|
@ -553,6 +553,15 @@ export default class AdvanceWarsGame extends Phaser.Scene {
|
|||
const v = run.view.unitViews.get(e.unitId);
|
||||
if (v) { v.c.destroy(); run.view.unitViews.delete(e.unitId); }
|
||||
}
|
||||
if (e.type === 'loaded' || e.type === 'joined') {
|
||||
// unit merged into a transport / another unit — remove its now
|
||||
// defunct board sprite once its move-in has been animated (no
|
||||
// explosion). Reaped here rather than by a state-diff sync so it
|
||||
// vanishes at this exact moment, not early (state is already the
|
||||
// whole turn's final result — see syncUnits' prune note).
|
||||
const v = run.view.unitViews.get(e.unitId);
|
||||
if (v) { v.c.destroy(); run.view.unitViews.delete(e.unitId); }
|
||||
}
|
||||
if (e.type === 'powerFired') {
|
||||
await new Promise((resolve) => Screens.powerCutIn(this, this.rules, this.oppById, run.state.armies[e.army].co, e.army, resolve));
|
||||
run.hud.refresh(run.state, 0);
|
||||
|
|
@ -570,9 +579,16 @@ export default class AdvanceWarsGame extends Phaser.Scene {
|
|||
// full-screen reveal, so those still do a full sync.
|
||||
const wideReveal = events.some((e) =>
|
||||
['powerFired', 'healedAll', 'refreshed', 'tsunami', 'meteor', 'armyEliminated'].includes(e.type));
|
||||
// A wide effect snaps every unit to its (final) state, but only an
|
||||
// actual army elimination may *prune* dead views here — otherwise a
|
||||
// mid-turn CO power would reap units merely doomed to die later in this
|
||||
// same pre-resolved turn, yanking them off the board before their own
|
||||
// death animation plays. Non-elimination deaths are reaped by the
|
||||
// 'destroyed'/'crashed' handlers above, at the moment they animate.
|
||||
const eliminated = events.some((e) => e.type === 'armyEliminated');
|
||||
const touchedIds = wideReveal ? null : new Set(events.flatMap((e) =>
|
||||
[e.unitId, e.attackerId, e.defenderId, e.transportId, e.intoId, e.byId].filter((id) => id != null)));
|
||||
run.view.syncUnits(touchedIds);
|
||||
run.view.syncUnits(touchedIds, { prune: eliminated });
|
||||
run.view.refreshFog(0);
|
||||
run.hud.refresh(run.state, 0);
|
||||
if (enemy && (moved || battles.length || capturing)) await this.wait(500);
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ export class AdvanceWarsMapView {
|
|||
// Used by replayEvents to reveal a turn's units one action at a time
|
||||
// instead of snapping everyone straight to the turn's final result. Pass
|
||||
// nothing (or null) for a full sync — creates/updates/prunes everyone.
|
||||
syncUnits(onlyIds = null) {
|
||||
syncUnits(onlyIds = null, { prune = !onlyIds } = {}) {
|
||||
const { state, rules, scene } = this;
|
||||
const present = new Set();
|
||||
for (const u of state.units) {
|
||||
|
|
@ -473,11 +473,12 @@ export class AdvanceWarsMapView {
|
|||
}
|
||||
if (justCreated || !onlyIds || onlyIds.has(u.id)) this.placeUnit(u, v);
|
||||
}
|
||||
// Stale-view cleanup only runs on a full sync — a scoped pass can't
|
||||
// tell a genuinely-dead unit from one whose own (not yet replayed)
|
||||
// death is later in this same turn's log; the 'destroyed'/'crashed'
|
||||
// Stale-view cleanup only runs when explicitly pruning (a full sync, by
|
||||
// default) — a mid-replay pass can't tell a genuinely-dead unit from one
|
||||
// whose own (not yet replayed) death/load/join is later in this same
|
||||
// pre-resolved turn's log; the 'destroyed'/'crashed'/'loaded'/'joined'
|
||||
// event handlers already remove views at the right moment for that.
|
||||
if (!onlyIds) {
|
||||
if (prune) {
|
||||
for (const [id, v] of [...this.unitViews]) {
|
||||
if (!present.has(id)) { v.c.destroy(); this.unitViews.delete(id); }
|
||||
}
|
||||
|
|
@ -507,7 +508,15 @@ export class AdvanceWarsMapView {
|
|||
}
|
||||
|
||||
setUnitVisibility(visibleIds) {
|
||||
// Views whose unit is already gone from state are mid-replay: the enemy
|
||||
// turn is resolved up front, so a unit that dies (or loads/joins) later
|
||||
// this turn vanishes from state while its board sprite is deliberately
|
||||
// held until its own event is replayed (see syncUnits / replayEvents).
|
||||
// Never force-hide those here — leave them exactly as last shown, so a
|
||||
// doomed unit stays put until its death animation actually plays.
|
||||
const alive = new Set(this.state.units.map((u) => u.id));
|
||||
for (const [id, v] of this.unitViews) {
|
||||
if (!alive.has(id)) continue;
|
||||
v.c.setVisible(visibleIds.has(id));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue