217 lines
7.2 KiB
JavaScript
217 lines
7.2 KiB
JavaScript
import { MatchBanner } from './MatchBanner.js';
|
|
|
|
export class WinAnimation {
|
|
// playerBoxCenter, lordBoxCenter: { x, y } screen positions of fund boxes
|
|
play(scene, originX, originY, playerBoxCenter, lordBoxCenter, symbol, onComplete) {
|
|
new MatchBanner().play(scene, symbol.label, () => {
|
|
// Flash the screen gold
|
|
scene.cameras.main.flash(1200, 255, 215, 0, true);
|
|
|
|
const totalCoins = 20;
|
|
const playerCoins = Math.round(totalCoins * 0.6);
|
|
const lordCoins = totalCoins - playerCoins;
|
|
let finished = 0;
|
|
|
|
// --- Split labels ---
|
|
const spawnSplitLabel = (lx, topLine, pctLine, borderColor, textColor) => {
|
|
const ly = 158;
|
|
const PW = 240, PH = 86, PR = 10;
|
|
const ctr = scene.add.container(lx, ly).setScale(0).setAlpha(0).setDepth(5);
|
|
|
|
const bg = scene.add.graphics();
|
|
// Panel fill
|
|
bg.fillStyle(0x0d0520, 0.96);
|
|
bg.fillRoundedRect(-PW / 2, -PH / 2, PW, PH, PR);
|
|
// Inner gold border
|
|
bg.lineStyle(3, borderColor, 1);
|
|
bg.strokeRoundedRect(-PW / 2, -PH / 2, PW, PH, PR);
|
|
// Outer glow
|
|
bg.lineStyle(12, borderColor, 0.2);
|
|
bg.strokeRoundedRect(-PW / 2 - 6, -PH / 2 - 6, PW + 12, PH + 12, PR + 6);
|
|
// Arrow tip pointing up toward the box above
|
|
bg.fillStyle(borderColor, 1);
|
|
bg.fillTriangle(-11, -PH / 2 + 2, 11, -PH / 2 + 2, 0, -PH / 2 - 15);
|
|
|
|
const t1 = scene.add.text(0, -20, topLine, {
|
|
fontSize: '13px',
|
|
fontFamily: 'Georgia, serif',
|
|
color: '#a89878',
|
|
letterSpacing: 2,
|
|
}).setOrigin(0.5, 0.5);
|
|
|
|
const t2 = scene.add.text(0, 18, pctLine, {
|
|
fontSize: '36px',
|
|
fontFamily: 'Georgia, serif',
|
|
fontStyle: 'bold',
|
|
color: textColor,
|
|
stroke: '#05020e',
|
|
strokeThickness: 5,
|
|
shadow: { offsetX: 0, offsetY: 0, color: textColor, blur: 16, fill: true },
|
|
}).setOrigin(0.5, 0.5);
|
|
|
|
ctr.add([bg, t1, t2]);
|
|
|
|
// Punch in
|
|
scene.tweens.add({
|
|
targets: ctr,
|
|
scale: 1,
|
|
alpha: 1,
|
|
duration: 380,
|
|
delay: 120,
|
|
ease: 'Back.easeOut',
|
|
easeParams: [3.5],
|
|
onComplete: () => {
|
|
// Vigorous shake
|
|
scene.tweens.add({
|
|
targets: ctr,
|
|
x: { from: lx - 11, to: lx + 11 },
|
|
duration: 55,
|
|
yoyo: true,
|
|
repeat: 8,
|
|
ease: 'Sine.easeInOut',
|
|
onComplete: () => {
|
|
// Pulse the percentage number
|
|
scene.tweens.add({
|
|
targets: t2,
|
|
scale: 1.3,
|
|
duration: 120,
|
|
yoyo: true,
|
|
repeat: 3,
|
|
ease: 'Sine.easeInOut',
|
|
});
|
|
// Gentle breathe on the whole panel
|
|
scene.tweens.add({
|
|
targets: ctr,
|
|
scale: 1.05,
|
|
duration: 500,
|
|
yoyo: true,
|
|
repeat: -1,
|
|
ease: 'Sine.easeInOut',
|
|
});
|
|
},
|
|
});
|
|
},
|
|
});
|
|
|
|
return { ctr, stopBreath: () => scene.tweens.killTweensOf(ctr) };
|
|
};
|
|
|
|
const { ctr: playerLabel, stopBreath: stopPlayer } = spawnSplitLabel(
|
|
playerBoxCenter.x, 'YOUR WINNINGS', '60%', 0x55cc77, '#55cc77'
|
|
);
|
|
const { ctr: lordLabel, stopBreath: stopLord } = spawnSplitLabel(
|
|
lordBoxCenter.x, "THE LORD'S TITHE", '40%', 0xaa88ff, '#aa88ff'
|
|
);
|
|
|
|
const spawnCoin = (targetX, targetY) => {
|
|
const radius = 26;
|
|
const gfx = scene.add.graphics();
|
|
// Main coin body
|
|
gfx.fillStyle(0xffd700, 1);
|
|
gfx.fillCircle(0, 0, radius);
|
|
// Outer ring
|
|
gfx.lineStyle(3, 0xffa500, 1);
|
|
gfx.strokeCircle(0, 0, radius);
|
|
// Inner highlight
|
|
gfx.fillStyle(0xffe980, 0.6);
|
|
gfx.fillCircle(-6, -6, radius * 0.38);
|
|
|
|
const coinLabel = scene.add.text(0, 1, '$', {
|
|
fontSize: '22px',
|
|
fontFamily: 'Georgia, serif',
|
|
fontStyle: 'bold',
|
|
color: '#5a3000'
|
|
}).setOrigin(0.5, 0.5);
|
|
|
|
const startX = originX + Phaser.Math.Between(-70, 70);
|
|
const startY = originY + Phaser.Math.Between(-30, 30);
|
|
const container = scene.add.container(startX, startY, [gfx, coinLabel]);
|
|
container.setScale(0.1);
|
|
|
|
const delay = Phaser.Math.Between(0, 700);
|
|
// Arc peak: shoot upward between origin and target, then fall to box
|
|
const peakX = startX + (targetX - startX) * 0.35 + Phaser.Math.Between(-100, 100);
|
|
const peakY = Math.min(startY, targetY) - Phaser.Math.Between(180, 340);
|
|
const spinDir = Phaser.Math.Between(0, 1) ? 1 : -1;
|
|
|
|
// Phase 1: pop in at origin
|
|
scene.tweens.add({
|
|
targets: container,
|
|
scale: 1.5,
|
|
duration: 220,
|
|
delay,
|
|
ease: 'Back.easeOut',
|
|
onComplete: () => {
|
|
// Phase 2: arc up to peak
|
|
scene.tweens.add({
|
|
targets: container,
|
|
x: peakX,
|
|
y: peakY,
|
|
angle: spinDir * Phaser.Math.Between(120, 200),
|
|
duration: 480,
|
|
ease: 'Cubic.easeOut',
|
|
onComplete: () => {
|
|
// Phase 3: fall to target box, shrink and fade at arrival
|
|
scene.tweens.add({
|
|
targets: container,
|
|
x: targetX,
|
|
y: targetY,
|
|
scale: 0.4,
|
|
angle: `+=${spinDir * Phaser.Math.Between(200, 400)}`,
|
|
alpha: { from: 1, to: 0 },
|
|
duration: 900,
|
|
ease: 'Cubic.easeIn',
|
|
onComplete: () => {
|
|
container.destroy();
|
|
finished++;
|
|
if (finished === totalCoins) {
|
|
stopPlayer();
|
|
stopLord();
|
|
scene.tweens.add({
|
|
targets: [playerLabel, lordLabel],
|
|
alpha: 0,
|
|
scale: 0.7,
|
|
duration: 380,
|
|
ease: 'Cubic.easeIn',
|
|
onComplete: () => {
|
|
playerLabel.destroy();
|
|
lordLabel.destroy();
|
|
if (onComplete) onComplete();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
// Halo glow ring expanding from center
|
|
const halo = scene.add.graphics();
|
|
halo.lineStyle(6, 0xffd700, 0.8);
|
|
halo.strokeCircle(0, 0, 10);
|
|
halo.setPosition(originX, originY);
|
|
|
|
scene.tweens.add({
|
|
targets: halo,
|
|
scaleX: 8, scaleY: 8,
|
|
alpha: 0,
|
|
duration: 1800,
|
|
ease: 'Cubic.easeOut',
|
|
onComplete: () => halo.destroy()
|
|
});
|
|
|
|
// Spawn coins to player box (60%)
|
|
for (let i = 0; i < playerCoins; i++) {
|
|
spawnCoin(playerBoxCenter.x, playerBoxCenter.y);
|
|
}
|
|
// Spawn coins to lord box (40%)
|
|
for (let i = 0; i < lordCoins; i++) {
|
|
spawnCoin(lordBoxCenter.x, lordBoxCenter.y);
|
|
}
|
|
});
|
|
}
|
|
}
|