Virtue-Slots/objects/WinAnimation.js

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);
}
});
}
}