72 lines
3.0 KiB
TypeScript
72 lines
3.0 KiB
TypeScript
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 };
|
||
}
|