import { playSfx } from '../data/sfxSpecs';
import { MenuList, MenuSceneBase, Panel, PixelText } from '@rpg/engine';
import { Game } from '../Game';
import { questLog } from '../data/quests';
import { itemName, useItemLine } from '../data/items';
/**
* Сумка и журнал квестов (панель поверх локации, push/pop).
* Содержимое собирается из флагов/варов GameState при каждом открытии.
* Список сумки с курсором: Enter — применить предмет (часы показывают время,
* время в сумке заморожено — локация не тикается). Журнал — текст без фокуса.
*/
export class InventoryScene extends MenuSceneBase {
constructor(
private game: Game,
private onBack: () => void
) {
super(
{ input: game.engine.input, inputBlocked: () => game.scenes.transitioning },
{ up: 'up', down: 'down', confirm: 'advance', cancel: 'menu', extra: ['inventory'] }
);
}
protected build(): void {
const panel = new Panel({ width: 300, height: 222 });
panel.position.set((Game.VIRTUAL_W - 300) / 2, (Game.VIRTUAL_H - 222) / 2);
const title = new PixelText({ text: 'ЗВОНАРЬ', size: 14, color: 0xd8c79a });
title.anchor.set(0.5);
title.position.set(150, 14);
panel.addChild(title);
const addLine = (text: string, color: number, x: number, y: number): void => {
const t = new PixelText({ text, size: 10, color });
t.position.set(x, y);
panel.addChild(t);
};
// Статус-строка: подсказка до первого применения, результат — после.
const result = new PixelText({ text: 'Enter — применить, Esc — назад', size: 9, color: 0xf2d49a });
result.position.set(16, 28);
panel.addChild(result);
// Сумка: список с курсором и скроллом (5 строк), Enter — применить.
addLine('Сумка', 0x999988, 16, 40);
const slots = this.game.inventory.all;
if (slots.length > 0) {
this.menu = new MenuList({ width: 260, height: 14, gap: 1, size: 10, visibleRows: 5 });
this.menu.position.set(20, 52);
this.menu.setItems(
slots.map((slot) => ({
label: slot.count > 1 ? `${itemName(slot.id)} ×${slot.count}` : itemName(slot.id),
onSelect: () => this.useItem(slot.id, result)
}))
);
panel.addChild(this.menu);
} else {
addLine('— пусто —', 0x777788, 24, 54);
}
// Журнал квестов — текущие задачи (незавершённые достигнутые стадии),
// без фокуса; перенос по ширине панели, шаг — по фактической высоте.
addLine('Журнал', 0x999988, 16, 132);
let y = 146;
let shown = 0;
for (const entry of questLog(this.game.state)) {
if (entry.done) continue;
const line = new PixelText({
text: `· ${entry.text}`,
size: 10,
color: 0xaaaaaa,
wordWrapWidth: 264
});
line.position.set(24, y);
panel.addChild(line);
y += Math.ceil(line.height) + 3;
shown++;
}
if (shown === 0) addLine('— текущих задач нет —', 0x777788, 24, 146);
this.view.addChild(panel);
this.game.renderer.uiRoot.addChild(this.view);
}
/** Применить предмет: юза́бельные дают строку-результат, остальные молчат. */
private useItem(id: string, result: PixelText): void {
const line = useItemLine(id, { label: this.game.clock.label });
if (line === null) return;
playSfx(this.game.audio, 'sfx/ui_click');
result.text = line;
}
/** Esc или клавиша инвентаря — назад в локацию. */
protected onCancel(): void {
this.close();
}
protected onAction(action: string): void {
if (action === 'inventory') this.close();
}
private close(): void {
playSfx(this.game.audio, 'sfx/ui_click');
this.onBack();
}
}