grechka-game/src3d/ui/craft.js

115 lines
3.7 KiB
JavaScript

// Craft UI
import { state } from '../core/state.js';
import { BLOCKS } from '../data/blocks.js';
import { ITEMS } from '../data/items.js';
import { TOOLS } from '../data/tools.js';
import { RECIPES } from '../data/recipes.js';
import { addTool } from '../data/tools.js';
import { tex, itemTex } from '../render/textures.js';
import { playSound } from '../audio/sound-engine.js';
import { rebuildHotbar } from './hotbar.js';
import { renderInventory } from './inventory.js';
export function canCraft(r) {
const inv = state.inv;
for (const res in r.cost) {
if ((inv[res] || 0) < r.cost[res]) return false;
}
return true;
}
export function renderCraft() {
const inv = state.inv;
const recipesEl = state.recipesEl;
recipesEl.innerHTML = '';
for (const r of RECIPES) {
const row = document.createElement('div');
row.className = 'recipe';
const icon = document.createElement('div');
icon.className = 'ricon';
// Иконка результата — canvas sprite > emoji
if (tex[r.out]) {
icon.style.backgroundImage = `url(${tex[r.out].toDataURL()})`;
} else if (itemTex[r.out]) {
icon.style.backgroundImage = `url(${itemTex[r.out].toDataURL()})`;
} else if (TOOLS[r.out]) {
icon.textContent = TOOLS[r.out].icon || '🔧';
icon.style.fontSize = '24px';
icon.style.display = 'flex';
icon.style.alignItems = 'center';
icon.style.justifyContent = 'center';
} else if (ITEMS[r.out]) {
icon.textContent = ITEMS[r.out].icon || '📦';
icon.style.fontSize = '24px';
icon.style.display = 'flex';
icon.style.alignItems = 'center';
icon.style.justifyContent = 'center';
}
const info = document.createElement('div');
info.className = 'rinfo';
const nm = document.createElement('div');
nm.className = 'rname';
const itemName = BLOCKS[r.out]?.n || TOOLS[r.out]?.n || ITEMS[r.out]?.n || r.out;
nm.textContent = `${itemName} x${r.qty}`;
const cs = document.createElement('div');
cs.className = 'rcost';
cs.textContent = Object.keys(r.cost).map(x => {
const cn = BLOCKS[x]?.n || TOOLS[x]?.n || ITEMS[x]?.n || x;
return `${cn}: ${(inv[x] || 0)}/${r.cost[x]}`;
}).join(' ');
info.appendChild(nm); info.appendChild(cs);
const btn = document.createElement('button');
btn.className = 'rcraft';
btn.textContent = 'Создать';
btn.disabled = !canCraft(r);
btn.onclick = () => {
if (!canCraft(r)) return;
playSound('click');
for (const res in r.cost) inv[res] -= r.cost[res];
inv[r.out] = (inv[r.out] || 0) + r.qty;
if (TOOLS[r.out]) addTool(r.out);
if (!state.recentItems.includes(r.out)) { state.recentItems.unshift(r.out); state.recentItems = state.recentItems.slice(0, 5); }
rebuildHotbar();
renderCraft();
renderInventory();
};
row.appendChild(icon); row.appendChild(info); row.appendChild(btn);
recipesEl.appendChild(row);
}
}
let craftOpen = false;
let inventoryOpen = false;
export function toggleCraft() {
playSound('click');
craftOpen = !craftOpen;
state.craftPanel.style.display = craftOpen ? 'block' : 'none';
if (craftOpen) {
renderCraft();
inventoryOpen = false;
state.inventoryPanel.style.display = 'none';
}
}
export function closeCraft() {
playSound('click');
craftOpen = false;
state.craftPanel.style.display = 'none';
}
export function toggleInventory() {
playSound('click');
inventoryOpen = true;
state.inventoryPanel.style.display = 'block';
renderInventory();
craftOpen = false;
state.craftPanel.style.display = 'none';
}
export function closeInventory() {
playSound('click');
inventoryOpen = false;
state.inventoryPanel.style.display = 'none';
}