миграция на pixi

This commit is contained in:
azykov@mail.ru
2026-09-09 13:45:30 +03:00
parent a3f2f4eeef
commit 4e30c585d1
41 changed files with 1303 additions and 899 deletions
+71
View File
@@ -0,0 +1,71 @@
import { autorun } from 'mobx';
import type { GameStore } from '../game/GameStore';
import type { Mounted } from './Topbar';
/** Russian plural agreement for "день" (day) after a count. */
function dayWord(n: number): string {
const mod10 = n % 10;
const mod100 = n % 100;
if (mod10 === 1 && mod100 !== 11)
return 'день';
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14))
return 'дня';
return 'дней';
}
/** Builds the "КРОВАВАЯ ЛУНА!" banner and the death screen, both absolutely positioned over the
* game map (siblings of the Pixi canvas — see App.ts) — the DOM-side replacement for the
* corresponding pieces of GameMap.tsx. Both stay hidden/empty until the store says otherwise, kept
* in sync via a single mobx autorun. */
export function createGameOverlays(store: GameStore, onDeath: () => void): Mounted {
const root = document.createElement('div');
root.className = 'game-overlays';
const banner = document.createElement('div');
banner.className = 'blood-moon-banner';
banner.textContent = 'КРОВАВАЯ ЛУНА!';
banner.hidden = true;
const gameOver = document.createElement('div');
gameOver.className = 'game-over-overlay';
gameOver.hidden = true;
const message = document.createElement('div');
message.className = 'game-over-overlay__message';
const stats = document.createElement('div');
stats.className = 'game-over-overlay__stats';
const distance = document.createElement('div');
const chopped = document.createElement('div');
const caught = document.createElement('div');
const coins = document.createElement('div');
stats.append(distance, chopped, caught, coins);
const menuBtn = document.createElement('button');
menuBtn.type = 'button';
menuBtn.className = 'menu-btn';
// The whole map underneath (including this overlay) has a pointerdown handler that captures the
// pointer and drives character movement — without stopping it here, that handler fires first on
// bubble-up and swallows the tap before it becomes a click (see GameCanvas).
menuBtn.addEventListener('pointerdown', (e) => e.stopPropagation());
menuBtn.addEventListener('click', onDeath);
menuBtn.textContent = 'В меню';
gameOver.append(message, stats, menuBtn);
root.append(banner, gameOver);
const dispose = autorun(() => {
banner.hidden = !store.bloodMoonBannerVisible;
gameOver.hidden = !store.isDead;
if (!store.isDead)
return;
message.textContent = `Вы прожили ${store.day} ${dayWord(store.day)}, но вас поймали кролики`;
distance.textContent = `Пройдено: ${Math.round(store.character.distanceTraveled)} px`;
chopped.textContent = `Срублено: ${store.treesChoppedCount} деревьев, ${store.bushesChoppedCount} кустов`;
caught.textContent = `Поймано кроликов: ${store.rabbitsCaughtCount}`;
coins.textContent = `Заработано монет: ${store.totalCoinsEarned.toFixed(1)}`;
});
return { el: root, dispose };
}