Open-source веб-студия для создания игр Рублокса, двойная лицензия AGPL-3.0 + Коммерческая. Главное: - Vite 5 + React 18 + Babylon 7.54.3 + Monaco Editor + Colyseus 0.16 - Самодостаточный движок ~28к строк (66 файлов): BlockManager, TerrainVoxelBuilder, ModelManager, DecoManager, PlayerController, ScriptSandboxWorker, MultiplayerSync, 30+ GD-гейммодов - Главный редактор KubikonEditor (~37к строк) + панели, ScriptEditor (Monaco) - Витрина игр (KubikonFeed, KubikonStudio, KubikonDocs, KubikonLearn) - Geometry Dash sub-app (GdMenu, GdShop, GdRules, GdCoverArt) - 10 admin-preview каталогов для дизайнеров (скины, музыка, порталы и т.д.) - Конфигурируемый бэкенд через VITE_API_BASE — работает со staging (dev-api.rublox.pro) без настройки - Standalone-режим (VITE_STANDALONE=true) — открыть пустой редактор без бэка - Полная документация (на русском): README, ARCHITECTURE, CONTRIBUTING, SECURITY, CHANGELOG - ESLint + Prettier + EditorConfig - Legal: LICENSE (AGPL-3.0), LICENSE-COMMERCIAL.md, CLA.md, COPYRIGHT.md - Issue templates: bug_report, feature_request, security_disclosure Перед публикацией: - Все импорты из minecraftia заменены на локальные - Все хардкоды URL (minecraftia-school.ru) и внутренних IP убраны → env - Admin-эндпоинты Kubikon3DService вырезаны (остаются в приватном репо) - AdminKubikonModeration не публикуется (модерация — в team.rublox.pro) - 93 МБ ассетов public/kubikon-assets вынесены в .gitignore (раздаются через release artifact)
105 lines
3.1 KiB
JavaScript
105 lines
3.1 KiB
JavaScript
/**
|
|
* 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<object|null>} */
|
|
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;
|