fertig-classic-games/public/src/scenes/PreloadScene.js

227 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as Phaser from 'phaser';
import { GAME_HEIGHT, GAME_WIDTH, COLORS } from '../config.js';
import { auth } from '../services/auth.js';
export default class PreloadScene extends Phaser.Scene {
constructor() { super('Preload'); }
preload() {
const w = GAME_WIDTH;
const h = GAME_HEIGHT;
const barWidth = 600;
const bg = this.add.rectangle(w / 2, h / 2, barWidth + 8, 28, COLORS.panel)
.setStrokeStyle(2, COLORS.accent);
const bar = this.add.rectangle(w / 2 - barWidth / 2, h / 2, 0, 20, COLORS.accent)
.setOrigin(0, 0.5);
this.add.text(w / 2, h / 2 - 60, 'Loading…', {
fontFamily: '"Julius Sans One"',
fontSize: '32px',
color: COLORS.textHex,
}).setOrigin(0.5);
this.load.on('progress', (p) => bar.width = barWidth * p);
this.load.on('complete', () => { bg.destroy(); bar.destroy(); });
this.load.spritesheet('opponents', '/assets/images/opponents.png', {
frameWidth: 300,
frameHeight: 300,
});
// Forbidden Island tiles: 2 cols (dry, flooded) × 24 rows. Row i → dry frame
// 2i, flooded frame 2i+1 (see IslandData.TILE_FRAME_ROW).
this.load.spritesheet('forbiddenisland-tiles', '/assets/images/forbiddenisland-tiles.png', {
frameWidth: 200,
frameHeight: 200,
});
// Forbidden Island Treasure-deck cards: 4 cols × 2 rows of 320×420 cells.
// Frame order documented in IslandData.CARD_FRAME.
this.load.spritesheet('forbiddenisland-cards', '/assets/images/forbiddenisland-cards.png', {
frameWidth: 320,
frameHeight: 420,
});
this.load.spritesheet('cardbacks', '/assets/images/cardbacks.png', {
frameWidth: 320,
frameHeight: 420,
});
this.load.spritesheet('catan-cards', '/assets/images/catancards.png', {
frameWidth: 270,
frameHeight: 390,
});
this.load.spritesheet('catan-tiles', '/assets/images/catantiles.png', {
frameWidth: 312,
frameHeight: 312,
});
this.load.image('risk-board', '/assets/images/risk-board.png');
this.load.image('catan-robber', '/assets/images/catan-robber.png');
this.load.image('catan-pirate', '/assets/images/catan-pirate.png');
this.load.image('bg-menu', '/assets/images/background-menu.png');
this.load.image('bg-room', '/assets/images/background-room.png');
this.load.image('bg-casino', '/assets/images/background-casino.png');
this.load.image('bg-jewelquest-battle', '/assets/images/background-jewelquest.png');
this.load.image('main-title', '/assets/images/main-title.png');
this.load.json('playfields', '/data/playfields.json');
this.load.json('colored-playfields', '/data/colored-playfields.json');
this.load.json('card-backs', '/data/card-backs.json');
this.load.json('music', '/data/music.json');
this.load.json('rushhour', '/data/rushhour.json');
this.load.json('puddingmonsters', '/data/puddingmonsters.json');
this.load.json('shift-artwork', '/data/shift-artwork.json');
this.load.json('slots-artwork', '/data/slots-artwork.json');
this.load.json('blockfighter', '/data/blockfighter.json');
this.load.json('jewelquest', '/data/jewelquest.json');
this.load.json('zuma', '/data/zuma.json');
this.load.json('dotlink', '/data/dotlink.json');
this.load.json('katamino', '/data/katamino.json');
this.load.audio('sfx-water-splash', '/assets/fx/water-splash.mp3');
this.load.audio('sfx-water-sink', '/assets/fx/water-sink.mp3');
this.load.audio('sfx-water-raise', '/assets/fx/water-raise.mp3');
this.load.audio('sfx-water-dry', '/assets/fx/water-dry.mp3');
this.load.audio('sfx-card-deal', '/assets/fx/card-deal.mp3');
this.load.audio('sfx-card-deal-8bit', '/assets/fx/8bit-card.mp3');
this.load.audio('sfx-card-place', '/assets/fx/card-place.mp3');
this.load.audio('sfx-card-show', '/assets/fx/card-show.mp3');
this.load.audio('sfx-card-show-8bit', '/assets/fx/8bit-select.mp3');
this.load.audio('sfx-card-shuffle', '/assets/fx/card-shuffle.mp3');
this.load.audio('sfx-coins', '/assets/fx/coins.mp3');
this.load.audio('sfx-purchase', '/assets/fx/purchase.mp3');
this.load.audio('sfx-casino-blackjack', '/assets/fx/casino-blackjack.mp3');
this.load.audio('sfx-casino-lose', '/assets/fx/casino-lose.mp3');
this.load.audio('sfx-casino-lose-8bit', '/assets/fx/8bit-lose.mp3');
this.load.audio('sfx-casino-win', '/assets/fx/casino-win.mp3');
this.load.audio('sfx-casino-win-8bit', '/assets/fx/8bit-win.mp3');
this.load.audio('sfx-chip-bet', '/assets/fx/chip-bet.mp3');
this.load.audio('sfx-dice-roll', '/assets/fx/dice-roll.mp3');
this.load.audio('sfx-bingo-balls', '/assets/fx/bingo-balls.mp3');
this.load.audio('sfx-pencil-write', '/assets/fx/pencil-write.mp3');
this.load.audio('sfx-piece-click', '/assets/fx/piece-click.mp3');
this.load.audio('sfx-mastermind-glitch-1', '/assets/fx/mastermind-glitch-01.mp3');
this.load.audio('sfx-mastermind-glitch-2', '/assets/fx/mastermind-glitch-02.mp3');
this.load.audio('sfx-mastermind-place', '/assets/fx/mastermind-place.mp3');
this.load.audio('sfx-mastermind-access-granted', '/assets/fx/mastermind-access-granted.mp3');
this.load.audio('sfx-mastermind-access-denied', '/assets/fx/mastermind-access-denied.mp3');
this.load.audio('sfx-mastermind-color', '/assets/fx/mastermind-color.mp3');
this.load.audio('sfx-mastermind-match', '/assets/fx/mastermind-match.mp3');
this.load.audio('sfx-mastermind-calculate', '/assets/fx/mastermind-calculate.mp3');
this.load.audio('sfx-roulette', '/assets/fx/roulette.mp3');
this.load.audio('sfx-battleship-hit', '/assets/fx/battleship-hit.mp3');
this.load.audio('sfx-battleship-miss', '/assets/fx/battleship-miss.mp3');
this.load.audio('sfx-battleship-launch', '/assets/fx/battleship-launch.mp3');
this.load.audio('sfx-victory-short', '/assets/fx/victory-short.mp3');
this.load.audio('sfx-sword-hit', '/assets/fx/sword-hit.mp3');
this.load.audio('sfx-sword-slice', '/assets/fx/sword-slice.mp3');
this.load.audio('sfx-scifi-launch', '/assets/fx/scifi-launch.mp3');
this.load.audio('sfx-scifi-explode', '/assets/fx/scifi-explode.mp3');
this.load.audio('sfx-scifi-riser', '/assets/fx/scifi-riser.mp3');
this.load.audio('sfx-scifi-reveal', '/assets/fx/scifi-reveal.mp3');
this.load.audio('sfx-scifi-woosh', '/assets/fx/scifi-woosh.mp3');
this.load.audio('sfx-scifi-plink', '/assets/fx/scifi-plink.mp3');
this.load.audio('sfx-scifi-plonk', '/assets/fx/scifi-plonk.mp3');
this.load.audio('sfx-8bit-select', '/assets/fx/8bit-select.mp3');
this.load.audio('sfx-8bit-activate', '/assets/fx/8bit-activate.mp3');
this.load.audio('sfx-8bit-action', '/assets/fx/8bit-action.mp3');
this.load.audio('sfx-8bit-move', '/assets/fx/8bit-move.mp3');
this.load.audio('sfx-squish', '/assets/fx/squish.mp3');
this.load.audio('sfx-squash', '/assets/fx/squash.mp3');
this.load.audio('sfx-woosh', '/assets/fx/woosh.mp3');
this.load.spritesheet('jello-items', '/assets/images/jello-items.png', { frameWidth: 132, frameHeight: 132 });
this.load.audio('sfx-jello-monsters','/assets/fx/jello-monsters.mp3');
this.load.audio('sfx-countdown-tick', '/assets/fx/countdown-01.mp3');
this.load.audio('sfx-countdown-go', '/assets/fx/countdown-02.mp3');
this.load.audio('sfx-ui-pick', '/assets/fx/ui-pick.mp3');
this.load.audio('sfx-ui-activate', '/assets/fx/ui-activate.mp3');
this.load.audio('sfx-ui-flip', '/assets/fx/ui-flip.mp3');
this.load.audio('sfx-ui-place', '/assets/fx/ui-place.mp3');
this.load.audio('sfx-ui-chime', '/assets/fx/ui-chime.mp3');
this.load.audio('sfx-gem-match-1', '/assets/fx/gem-01.mp3');
this.load.audio('sfx-gem-match-2', '/assets/fx/gem-02.mp3');
this.load.audio('sfx-gem-match-4', '/assets/fx/gem-04.mp3');
this.load.audio('sfx-gem-match-5', '/assets/fx/gem-05.mp3');
this.load.audio('sfx-gem-chain', '/assets/fx/gem-chain.mp3');
this.load.audio('sfx-gem-drop', '/assets/fx/gem-drop.mp3');
this.load.audio('sfx-gem-big-drop','/assets/fx/gem-big-drop.mp3');
this.load.spritesheet('catan-special-cards', '/assets/images/catan-special-cards.png', { frameWidth: 270, frameHeight: 390 });
// Dominion card art. One 270×390 cell per card (art fills the top ~60%;
// the title/icon band is drawn at runtime). Optional — the scene falls back
// to procedural placeholders when the sheet is absent.
this.load.spritesheet('dominion-cards', '/assets/images/dominioncards.png', { frameWidth: 270, frameHeight: 390 });
// Prosperity expansion art (frame order documented in expansions/prosperity.js).
// Optional — same procedural fallback applies when the sheet is absent.
this.load.spritesheet('dominion-prosperity', '/assets/images/dominion-prosperity.png', { frameWidth: 270, frameHeight: 390 });
// Prosperity token sprites (1 VP, 5 VP, Gold) — 150×150 each.
this.load.spritesheet('dominion-tokens', '/assets/images/dominion-tokens.png', { frameWidth: 150, frameHeight: 150 });
// Splendor: 28 frames at 270×390. 0-14 = tier×bonus backgrounds, 15-24 = nobles, 25-27 = deck backs.
// Frame order documented in SplendorData.js. Optional — vectors are drawn when absent.
this.load.spritesheet('splendor-cards', '/assets/images/splendor-cards.png', { frameWidth: 270, frameHeight: 390 });
// Splendor gems: 6 frames at 64×64. white(0) blue(1) green(2) red(3) black(4) gold(5).
this.load.spritesheet('splendor-gems', '/assets/images/splendor-gems.png', { frameWidth: 64, frameHeight: 64 });
this.load.spritesheet('ttr-cards', '/assets/images/tickettoride-cards.png', { frameWidth: 270, frameHeight: 390 });
this.load.spritesheet('gofish-cards', '/assets/images/gofish-cards.png', { frameWidth: 270, frameHeight: 390 });
this.load.spritesheet('oldmaid-cards', '/assets/images/oldmaid-cards.png', { frameWidth: 270, frameHeight: 390 });
this.load.spritesheet('tab-icons', '/assets/images/tab-icons.png', { frameWidth: 128, frameHeight: 128 });
this.load.spritesheet('game-icons', '/assets/images/game-icons.png', { frameWidth: 44, frameHeight: 44 });
// Labyrinth. Tile backgrounds: frame 0 = movable, 1 = fixed (corridors are
// drawn in code). Treasure overlays & cards share the treasure index as
// their frame. All optional — the scene draws vector fallbacks when absent.
this.load.spritesheet('labyrinth-tiles', '/assets/images/labyrinth-tiles.png', { frameWidth: 200, frameHeight: 200 });
this.load.spritesheet('labyrinth-treasures', '/assets/images/labyrinth-treasures.png', { frameWidth: 100, frameHeight: 100 });
this.load.spritesheet('labyrinth-cards', '/assets/images/labyrinth-cards.png', { frameWidth: 270, frameHeight: 390 });
// Stratego unit art: 12 transparent frames (0=Flag, 1..10=rank, 11=Bomb),
// 6 cols × 2 rows. Optional — the scene draws vector glyphs when absent.
this.load.spritesheet('stratego-pieces', '/assets/images/stratego-pieces.png', { frameWidth: 140, frameHeight: 140 });
// Monopoly pawns: 4 frames (one per seat) at 80×80. Optional — falls back to colored circles.
this.load.spritesheet('monopoly-pawns', '/assets/images/monopoly-pawns.png', { frameWidth: 80, frameHeight: 80 });
// Monopoly card art: frame 0 = Chance, frame 1 = Community Chest, at 200×300.
this.load.spritesheet('monopoly-cards', '/assets/images/monopoly-cards.png', { frameWidth: 200, frameHeight: 300 });
// Mahjong Match tile-label overlays (128×178 transparent PNGs). The White
// Dragon has no art — the scene draws its traditional empty frame.
const mahjongLabels = [
...Array.from({ length: 9 }, (_, i) => `bamboo${i + 1}`),
...Array.from({ length: 9 }, (_, i) => `circle${i + 1}`),
...Array.from({ length: 15 }, (_, i) => `pinyin${i + 1}`),
'orchid', 'peony', 'chrysanthemum', 'lotus',
'spring', 'summer', 'fall', 'winter',
];
for (const name of mahjongLabels) {
this.load.image(`mahjong-${name}`, `/assets/images/mahjong/${name}.png`);
}
this.load.audio('sfx-monopoly-purchase', '/assets/fx/monopoly-purchase.mp3');
this.load.audio('sfx-monopoly-expense', '/assets/fx/monopoly-expense.mp3');
this.load.audio('sfx-monopoly-pay', '/assets/fx/monopoly-pay.mp3');
this.load.audio('sfx-monopoly-paid', '/assets/fx/monopoly-paid.mp3');
}
async create() {
// Collect all image assets that need loading from JSON configs
const pfd = this.cache.json.get('playfields');
const cbd = this.cache.json.get('card-backs');
const shiftArt = this.cache.json.get('shift-artwork');
const slotsArt = this.cache.json.get('slots-artwork');
const toLoad = [
...(pfd?.playfields ?? []).filter((pf) => pf.path && !this.textures.exists(pf.key)),
...(cbd?.cardBacks ?? []).filter((cb) => cb.path && !this.textures.exists(cb.key)),
...(shiftArt?.artwork ?? []).filter((a) => a.path && a.key && !this.textures.exists(a.key)),
...(slotsArt?.artwork ?? []).filter((a) => a.path && a.key && !this.textures.exists(a.key)),
];
if (toLoad.length > 0) {
for (const asset of toLoad) this.load.image(asset.key, asset.path);
await new Promise((resolve) => {
this.load.once('complete', resolve);
this.load.start();
});
}
// Warm the handwritten Scrabble notepad font so its first paint isn't a
// fallback face. Best effort — never block startup on it.
try { await Promise.race([document.fonts.load('48px YummyCupcakes'), new Promise(r => setTimeout(r, 1500))]); } catch { /* ignore */ }
await auth.refresh();
this.scene.start('Landing');
}
}