diff --git a/src/games/advancewars/AdvanceWarsDayBanner.js b/src/games/advancewars/AdvanceWarsDayBanner.js new file mode 100644 index 0000000..e49e4a2 --- /dev/null +++ b/src/games/advancewars/AdvanceWarsDayBanner.js @@ -0,0 +1,85 @@ +// Advance Wars "Day N" banner — shown once per full day cycle, right as +// control returns to the player after the last enemy army's turn. Each +// letter flies in on its own (alternating in from above/below with a +// dramatic overshoot), the full word holds for 1.5s, then every letter +// flies back out the way it came. Tap to skip. + +import { GAME_WIDTH, GAME_HEIGHT } from '../../config.js'; +import { FONT } from './AdvanceWarsUI.js'; + +const SIZE = 150; +const HOLD_MS = 1500; +const STAGGER_MS = 70; +const FLY_DIST = 420; +const IN_DURATION = 420; +const OUT_DURATION = 380; + +export function playDayBanner(scene, day, onDone) { + const text = `DAY ${day}`; + const cy = GAME_HEIGHT / 2; + const objs = []; + let finished = false; + + const dim = scene.add.rectangle(GAME_WIDTH / 2, cy, GAME_WIDTH, 260, 0x000000, 0.55) + .setDepth(60).setInteractive(); + objs.push(dim); + + const style = { + fontFamily: FONT, fontSize: `${SIZE}px`, color: '#ffffff', + stroke: '#000000', strokeThickness: 10, + }; + const spaceWidth = SIZE * 0.45; + const gap = 6; + + // lay every non-space letter out at its final centered position first + // (measuring natural glyph width as each is created), then animate each + // one in from off-screen to that spot. + const letters = []; + let cursor = 0; + for (const ch of text) { + if (ch === ' ') { cursor += spaceWidth + gap; continue; } + const t = scene.add.text(0, cy, ch, style).setOrigin(0, 0.5).setDepth(61).setAlpha(0); + letters.push({ t, x: cursor }); + cursor += t.width + gap; + objs.push(t); + } + const totalWidth = cursor - gap; + const startX = GAME_WIDTH / 2 - totalWidth / 2; + for (const l of letters) l.x += startX; + + const cleanup = () => { + if (finished) return; + finished = true; + for (const o of objs) o.destroy(); + onDone?.(); + }; + dim.on('pointerdown', cleanup); + + letters.forEach(({ t, x }, i) => { + const fromAbove = i % 2 === 0; + t.setPosition(x, cy + (fromAbove ? -FLY_DIST : FLY_DIST)); + t.setScale(2.2); + t.setRotation(fromAbove ? -0.6 : 0.6); + scene.tweens.add({ + targets: t, x, y: cy, scale: 1, rotation: 0, alpha: 1, + delay: i * STAGGER_MS, duration: IN_DURATION, ease: 'Back.easeOut', + }); + }); + + const inTotal = (letters.length - 1) * STAGGER_MS + IN_DURATION; + scene.time.delayedCall(inTotal + HOLD_MS, () => { + if (finished) return; + letters.forEach(({ t }, i) => { + const toAbove = i % 2 !== 0; + scene.tweens.add({ + targets: t, y: cy + (toAbove ? -FLY_DIST : FLY_DIST), scale: 2.2, + rotation: toAbove ? 0.6 : -0.6, alpha: 0, + delay: i * STAGGER_MS, duration: OUT_DURATION, ease: 'Back.easeIn', + }); + }); + const outTotal = (letters.length - 1) * STAGGER_MS + OUT_DURATION; + scene.time.delayedCall(outTotal + 80, cleanup); + }); + + return { skip: cleanup }; +} diff --git a/src/games/advancewars/AdvanceWarsGame.js b/src/games/advancewars/AdvanceWarsGame.js index f957ff6..141c0ed 100644 --- a/src/games/advancewars/AdvanceWarsGame.js +++ b/src/games/advancewars/AdvanceWarsGame.js @@ -17,6 +17,7 @@ import { AdvanceWarsHUD, ActionMenu, DamagePreview, ProductionMenu, TileInfo, mk import * as Screens from './AdvanceWarsScreens.js'; import { playBattleAnim } from './AdvanceWarsBattleAnim.js'; import { playCaptureAnim } from './AdvanceWarsCaptureAnim.js'; +import { playDayBanner } from './AdvanceWarsDayBanner.js'; const SAVE_KEY = 'advancewars-save'; const ANIM_KEY = 'advancewars-battle-anims'; @@ -507,6 +508,10 @@ export default class AdvanceWarsGame extends Phaser.Scene { await this.replayEvents(log, { enemy: true }); } if (this.run !== run) return; + if (!run.state.result) { + await new Promise((resolve) => playDayBanner(this, run.state.day, resolve)); + } + if (this.run !== run) return; run.busy = false; this.refreshAll(); if (run.state.result) { this.onMissionOver(); return; }