import {
Engine,
SaveManager,
AssetLoader,
AudioManager,
GameState,
Settings,
type Renderer,
type SceneManager
} from '@rpg/engine';
// Шрифт идёт с движком (OFL): Vite кладёт файл в сборку и даёт готовый URL.
import fontUrl from '@rpg/engine/assets/fonts/VT323-Regular.ttf?url';
/**
* Игровой контекст: движок, сохранения, загрузчик ассетов, состояние, настройки.
* Движок ничего не знает об этом классе — он только потребляет его API.
*/
export class Game {
/** Виртуальное разрешение (пиксель-арт). */
static readonly VIRTUAL_W = 480;
static readonly VIRTUAL_H = 270;
readonly saves: SaveManager;
readonly assets: AssetLoader;
/** Звуки и музыка (WAV из tools/audio/gen.mjs). */
readonly audio = new AudioManager((key) => `${import.meta.env.BASE_URL}audio/${key}.wav`);
/** Флаги и переменные прохождения. */
readonly state = new GameState();
/** Настройки игрока (громкости и т.п.) — вне сейвов. */
readonly settings = new Settings(window.localStorage);
/** Текстуры тайлов и одиночные спрайты NPC. */
static readonly ASSET_KEYS = [
'tiles/grass',
'tiles/path',
'tiles/water',
'tiles/ash',
'tiles/bellflower',
'tiles/tree',
'chars/elder_irwin',
'chars/trader_mila'
];
/** Ключи аудио (WAV): sfx + амбиент-лупы. */
static readonly AUDIO_KEYS = [
'sfx/bell_hit',
'sfx/bell_low',
'sfx/ash_hiss',
'sfx/ash_die',
'sfx/spit',
'sfx/hurt',
'sfx/step',
'sfx/ui_click',
'sfx/chime',
'music/meadows',
'music/ponds'
];
constructor(readonly engine: Engine) {
this.saves = new SaveManager(window.localStorage);
// Ключи без расширения -> .png; ключи .json (атласы) резолвятся как есть.
this.assets = new AssetLoader((key) =>
key.endsWith('.json') ? `${import.meta.env.BASE_URL}${key}` : `${import.meta.env.BASE_URL}${key}.png`
);
this.settings.load();
// Настройки громкости -> шины аудио (работает и до unlock — запомнится).
this.settings.onChange((s) => {
this.audio.setBusVolume('master', s.master);
this.audio.setBusVolume('music', s.music);
this.audio.setBusVolume('sfx', s.sfx);
});
// Применить текущие значения сразу.
this.audio.setBusVolume('master', this.settings.data.master);
this.audio.setBusVolume('music', this.settings.data.music);
this.audio.setBusVolume('sfx', this.settings.data.sfx);
}
/** URL пиксельного шрифта (VT323 идёт вместе с движком, OFL). */
static readonly FONT_URL: string = fontUrl;
get scenes(): SceneManager {
return this.engine.scenes;
}
get renderer(): Renderer {
return this.engine.renderer;
}
get camera() {
return this.engine.camera;
}
get worldRoot() {
return this.engine.renderer.worldRoot;
}
/** Музыка с кроссфейдом; повторный вызов с тем же треком не перезапускает его. */
playMusic(key: string, volume = 0.7): void {
if (this.musicKey === key) return;
this.musicKey = key;
void this.audio.playMusic(key, { fade: 1.5, volume });
}
/** Текущий трек (защита от перезапуска того же ключа). */
private musicKey: string | null = null;
}