/** * InventoryManager — инвентарь игрока (9 слотов hot-bar). * * Каждый слот хранит {id, kind, modelTypeId, name, params} или null. * kind: * 'weapon' — оружие (бластер/меч/...). params: { damage, fireRate, range, ammo, ... } * 'tool' — инструмент. * 'item' — обычный предмет (зелье и т.п.). * * События: * change → onChange callback (для GUI hot-bar в React). */ const SLOTS = 5; let _seq = 1; export class InventoryManager { constructor() { /** @type {Array} */ this.slots = new Array(SLOTS).fill(null); this.activeIndex = 0; this._onChange = null; } setOnChange(cb) { this._onChange = cb; } _notify() { if (this._onChange) try { this._onChange(); } catch (e) { /* ignore */ } } /** Положить предмет в первый свободный слот. Возвращает индекс или -1. */ add(item) { const idx = this.slots.findIndex(s => s == null); if (idx < 0) return -1; this.slots[idx] = this._normalize(item); this._notify(); return idx; } /** Положить в конкретный слот (заменяет содержимое). */ setSlot(index, item) { if (index < 0 || index >= SLOTS) return; this.slots[index] = item ? this._normalize(item) : null; this._notify(); } removeSlot(index) { if (index < 0 || index >= SLOTS) return; this.slots[index] = null; this._notify(); } getActive() { return this.slots[this.activeIndex] || null; } setActive(index) { if (index < 0 || index >= SLOTS) return; this.activeIndex = index; this._notify(); } serialize() { return { slots: this.slots.map(s => s ? { ...s } : null), activeIndex: this.activeIndex, }; } loadFromArray(data) { if (!data) { this.slots = new Array(SLOTS).fill(null); this.activeIndex = 0; this._notify(); return; } if (Array.isArray(data.slots)) { this.slots = new Array(SLOTS).fill(null); for (let i = 0; i < Math.min(SLOTS, data.slots.length); i++) { this.slots[i] = data.slots[i] ? this._normalize(data.slots[i]) : null; } } if (Number.isFinite(data.activeIndex)) { this.activeIndex = Math.max(0, Math.min(SLOTS - 1, data.activeIndex)); } this._notify(); } clear() { this.slots = new Array(SLOTS).fill(null); this.activeIndex = 0; this._notify(); } _normalize(item) { return { id: item.id || `inv_${Date.now()}_${(_seq++).toString(36)}`, kind: item.kind || 'item', modelTypeId: item.modelTypeId || null, name: item.name || 'Предмет', params: item.params ? { ...item.params } : {}, }; } } InventoryManager.SLOTS = SLOTS;