refactor: fix card visibility and animation sequence in GoFish
- Pass live sprites from `animateAsk` to `animatePairedCards` to prevent premature destruction. - Update `animateAsk` to fan collected cards into the asker's show zone instead of hiding them. - Update `animatePairedCards` to accept pre-existing sprites for seamless pair animation. - Remove `canAsk` check in `applyAsk` to ensure turn advances correctly after any ask.
This commit is contained in:
parent
b63bf1b651
commit
975ee6ea25
|
|
@ -651,7 +651,7 @@ export default class GoFishGame extends Phaser.Scene {
|
||||||
if (after === before) { this.animating = false; return; }
|
if (after === before) { this.animating = false; return; }
|
||||||
const last = after.lastAsk;
|
const last = after.lastAsk;
|
||||||
|
|
||||||
this.animateAsk(askerSeat, targetSeat, rank, last, before, () => {
|
this.animateAsk(askerSeat, targetSeat, rank, last, before, (liveSprites) => {
|
||||||
// Fish with pool cards remaining — player must pick interactively.
|
// Fish with pool cards remaining — player must pick interactively.
|
||||||
if (after.phase === 'pick') {
|
if (after.phase === 'pick') {
|
||||||
this.gs = after;
|
this.gs = after;
|
||||||
|
|
@ -678,16 +678,17 @@ export default class GoFishGame extends Phaser.Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last.newPairs > 0 && last.pairedCards?.length >= 2) {
|
if (last.newPairs > 0 && last.pairedCards?.length >= 2) {
|
||||||
this.time.delayedCall(600, () => {
|
// Cards from animateAsk are already visible in position.
|
||||||
this.hideBanner();
|
// Do NOT call renderAll() here — it would destroy the liveSprites.
|
||||||
|
// renderAll() is deferred to after the pair animation completes.
|
||||||
|
this.hideBanner();
|
||||||
|
this.animatePairedCards(askerSeat, last.pairedCards, () => {
|
||||||
this.renderAll();
|
this.renderAll();
|
||||||
this.updateStatus();
|
this.updateStatus();
|
||||||
this.animatePairedCards(askerSeat, last.pairedCards, () => {
|
this.animating = false;
|
||||||
this.animating = false;
|
if (isGameOver(this.gs)) { this.endGame(); return; }
|
||||||
if (isGameOver(this.gs)) { this.endGame(); return; }
|
this.maybeStartAITurn();
|
||||||
this.maybeStartAITurn();
|
}, liveSprites ?? null);
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
this.time.delayedCall(900, () => {
|
this.time.delayedCall(900, () => {
|
||||||
this.renderAll();
|
this.renderAll();
|
||||||
|
|
@ -754,24 +755,31 @@ export default class GoFishGame extends Phaser.Scene {
|
||||||
return { sprite, tx, ty };
|
return { sprite, tx, ty };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// After target cards are shown, fan all sprites into a holding position
|
||||||
|
// at the asker's show zone — they stay visible for the pair animation.
|
||||||
const waitMs = 250 + (n - 1) * 80 + 380;
|
const waitMs = 250 + (n - 1) * 80 + 380;
|
||||||
this.time.delayedCall(waitMs, () => {
|
this.time.delayedCall(waitMs, () => {
|
||||||
if (askerSprite.active) {
|
const FGAP = 8;
|
||||||
this.tweens.add({
|
const nTotal = 1 + targetSprites.length;
|
||||||
targets: askerSprite, alpha: 0, duration: 200,
|
const isAskerVert = askerSlot === 'left' || askerSlot === 'right';
|
||||||
onComplete: () => { if (askerSprite.active) askerSprite.destroy(); },
|
const allSprites = [
|
||||||
});
|
askerSprite,
|
||||||
}
|
...targetSprites.map(({ sprite }) => sprite),
|
||||||
for (const { sprite } of targetSprites) {
|
].map((sprite, i) => {
|
||||||
|
const x = isAskerVert
|
||||||
|
? askerShow.x
|
||||||
|
: askerShow.x + (i - (nTotal - 1) / 2) * (CARD_W + FGAP);
|
||||||
|
const y = isAskerVert
|
||||||
|
? askerShow.y + (i - (nTotal - 1) / 2) * (CARD_H + FGAP)
|
||||||
|
: askerShow.y;
|
||||||
|
return { sprite, x, y };
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const { sprite, x, y } of allSprites) {
|
||||||
if (!sprite.active) continue;
|
if (!sprite.active) continue;
|
||||||
this.tweens.add({
|
this.tweens.add({ targets: sprite, x, y, duration: 280, ease: 'Cubic.easeOut' });
|
||||||
targets: sprite,
|
|
||||||
x: askerLayout.handCenter.x, y: askerLayout.handCenter.y,
|
|
||||||
alpha: 0, duration: 380, ease: 'Cubic.easeIn',
|
|
||||||
onComplete: () => { if (sprite.active) sprite.destroy(); },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
this.time.delayedCall(420, onComplete);
|
this.time.delayedCall(320, () => onComplete(allSprites));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Fish — flip card back face-down and return it to hand.
|
// Fish — flip card back face-down and return it to hand.
|
||||||
|
|
@ -822,7 +830,30 @@ export default class GoFishGame extends Phaser.Scene {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
animatePairedCards(askerSeat, pairedCards, onComplete) {
|
animatePairedCards(askerSeat, pairedCards, onComplete, preSprites = null) {
|
||||||
|
if (preSprites) {
|
||||||
|
// Cards are already face-up and in position from animateAsk — skip sprite creation.
|
||||||
|
const sprites = preSprites.map(({ sprite, x, y }) => ({ sprite, targetX: x, targetY: y }));
|
||||||
|
playSound(this, SFX.CARD_PLACE);
|
||||||
|
this.time.delayedCall(200, () => {
|
||||||
|
const cx = sprites.reduce((s, { targetX }) => s + targetX, 0) / sprites.length;
|
||||||
|
const cy = sprites.reduce((s, { targetY }) => s + targetY, 0) / sprites.length;
|
||||||
|
this.spawnFireworks(cx, cy);
|
||||||
|
this.time.delayedCall(350, () => this.spawnFireworks(cx, cy));
|
||||||
|
});
|
||||||
|
this.time.delayedCall(1200, () => {
|
||||||
|
for (const { sprite } of sprites) {
|
||||||
|
if (!sprite.active) continue;
|
||||||
|
this.tweens.add({
|
||||||
|
targets: sprite, x: CX, y: CY, alpha: 0, duration: 500, ease: 'Cubic.easeIn',
|
||||||
|
onComplete: () => { if (sprite.active) sprite.destroy(); },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.time.delayedCall(1800, onComplete);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const slot = this.slotForSeat[askerSeat];
|
const slot = this.slotForSeat[askerSeat];
|
||||||
const layout = slotLayout(slot);
|
const layout = slotLayout(slot);
|
||||||
const n = pairedCards.length;
|
const n = pairedCards.length;
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ export function applyAsk(state, targetSeat, rank) {
|
||||||
pairedCards: [],
|
pairedCards: [],
|
||||||
};
|
};
|
||||||
ensureHasCards(next, askerSeat);
|
ensureHasCards(next, askerSeat);
|
||||||
if (!canAsk(next, askerSeat)) advanceTurn(next);
|
advanceTurn(next);
|
||||||
checkGameOver(next);
|
checkGameOver(next);
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue