миграция на pixi
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import '../App.css';
|
||||
import { Analytics, makeAnalyticsGameInfo } from '../analytics';
|
||||
import type { AnalyticsTransport } from '../analytics/AnalyticsTransport';
|
||||
import { GameAnalyticsTransport } from '../analytics/GameAnalyticsTransport';
|
||||
import { NullAnalyticsTransport } from '../analytics/NullAnalyticsTransport';
|
||||
import { GameStore } from '../game/GameStore';
|
||||
import { clearSave, hasSave } from '../game/persistence';
|
||||
import { GameScreen } from './GameScreen';
|
||||
import { createMainMenu } from './MainMenu';
|
||||
|
||||
/** Top-level screen controller (menu <-> game) — the plain-TS replacement for App.tsx. Owns the
|
||||
* GameStore/Analytics singletons for the page's whole lifetime, same as the original module-level
|
||||
* singletons (no React StrictMode double-invocation to guard against here, since nothing
|
||||
* constructs this more than once — see main.ts). */
|
||||
export class App {
|
||||
private readonly root: HTMLElement;
|
||||
private readonly store: GameStore;
|
||||
private readonly analytics: Analytics;
|
||||
private gameScreen: GameScreen | null = null;
|
||||
|
||||
constructor(root: HTMLElement) {
|
||||
this.root = root;
|
||||
|
||||
const gaGameKey = import.meta.env.VITE_GAMEANALYTICS_GAME_KEY;
|
||||
const gaGameSecret = import.meta.env.VITE_GAMEANALYTICS_GAME_SECRET;
|
||||
let transport: AnalyticsTransport;
|
||||
if (!gaGameKey || !gaGameSecret) {
|
||||
console.warn('VITE_GAMEANALYTICS_GAME_KEY/VITE_GAMEANALYTICS_GAME_SECRET not set — analytics disabled (see .env.example).');
|
||||
transport = new NullAnalyticsTransport();
|
||||
} else {
|
||||
transport = new GameAnalyticsTransport(gaGameKey, gaGameSecret);
|
||||
}
|
||||
|
||||
this.analytics = new Analytics(transport, () => makeAnalyticsGameInfo(this.store));
|
||||
this.analytics.start();
|
||||
this.store = new GameStore(this.analytics);
|
||||
|
||||
// Always starts on the menu, regardless of whether a save exists — loading straight into a
|
||||
// resumed game on every page (re)load isn't what was asked for.
|
||||
this.showMenu();
|
||||
}
|
||||
|
||||
private showMenu(): void {
|
||||
this.teardownGameScreen();
|
||||
const menu = createMainMenu(
|
||||
this.analytics,
|
||||
() => this.handleNewGame(),
|
||||
() => this.handleContinue(),
|
||||
);
|
||||
this.root.replaceChildren(menu);
|
||||
}
|
||||
|
||||
private showGame(): void {
|
||||
this.gameScreen = new GameScreen(
|
||||
this.store,
|
||||
() => this.handleExitToMenu(),
|
||||
() => this.handleDeath(),
|
||||
);
|
||||
this.root.replaceChildren(this.gameScreen.el);
|
||||
// Only now is `el` actually attached to the document — GameScreen's canvas sizes itself from
|
||||
// the map div's clientWidth/Height, which reads 0 on a still-detached element.
|
||||
this.gameScreen.mount();
|
||||
}
|
||||
|
||||
private handleNewGame(): void {
|
||||
// A resumable save existing means there's a live (not-dead) game being abandoned by starting
|
||||
// fresh instead of continuing it — record that as the last level's failure. A death already
|
||||
// records its own failure at the moment it happens (see GameStore.landRabbitStrike) and clears
|
||||
// the save there too, so this naturally doesn't double-fire right after a death.
|
||||
if (hasSave())
|
||||
this.analytics.death('restarted');
|
||||
clearSave();
|
||||
this.showGame();
|
||||
}
|
||||
|
||||
private handleContinue(): void {
|
||||
this.showGame();
|
||||
}
|
||||
|
||||
private handleExitToMenu(): void {
|
||||
this.store.save();
|
||||
this.showMenu();
|
||||
}
|
||||
|
||||
// Called after a death overlay is acknowledged: the store already wiped the save itself the
|
||||
// moment the character was caught, so this just returns to the menu without saving over it.
|
||||
private handleDeath(): void {
|
||||
this.showMenu();
|
||||
}
|
||||
|
||||
private teardownGameScreen(): void {
|
||||
this.gameScreen?.destroy();
|
||||
this.gameScreen = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user