grechka-game/src3d/ui/hotbar.js

151 lines
5.2 KiB
JavaScript

// Hotbar UI — 3-slot Minecraft-style bottom bar + selected item indicator
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 { tex, itemTex } from '../render/textures.js';
import { playSound } from '../audio/sound-engine.js';
const HOTBAR_SLOTS = 3;
let selectedItemEl = null;
export function addToRecentItems(id) {
if (!id) return;
state.recentItems = state.recentItems.filter(item => item !== id);
state.recentItems.unshift(id);
state.recentItems = state.recentItems.slice(0, 5);
}
function renderSlotIcon(el, id) {
if (BLOCKS[id] && tex[id]) {
el.style.backgroundImage = `url(${tex[id].toDataURL()})`;
el.style.backgroundSize = 'cover';
} else if (itemTex[id]) {
el.style.backgroundImage = `url(${itemTex[id].toDataURL()})`;
el.style.backgroundSize = 'cover';
} else if (ITEMS[id]) {
el.textContent = ITEMS[id].icon || '📦';
} else if (TOOLS[id]) {
el.textContent = TOOLS[id].icon || '🔧';
} else if (id === 'iron_armor') {
el.textContent = '🛡️';
el.style.background = 'linear-gradient(135deg, #95a5a6 0%, #7f8c8d 100%)';
} else {
el.textContent = id.charAt(0).toUpperCase();
el.style.background = '#555';
el.style.color = '#fff';
el.style.fontWeight = '700';
el.style.fontSize = '14px';
}
}
function updateSelectedItem() {
if (!selectedItemEl) selectedItemEl = document.getElementById('selectedItem');
if (!selectedItemEl) return;
const sel = state.selected;
if (!sel || !state.inv[sel] || state.inv[sel] <= 0) {
selectedItemEl.style.display = 'none';
return;
}
selectedItemEl.style.display = 'flex';
// Clear previous content
selectedItemEl.textContent = '';
selectedItemEl.style.backgroundImage = '';
selectedItemEl.style.background = 'rgba(0,0,0,0.45)';
// Render icon
if (BLOCKS[sel] && tex[sel]) {
selectedItemEl.style.backgroundImage = `url(${tex[sel].toDataURL()})`;
selectedItemEl.style.backgroundSize = 'cover';
} else if (itemTex[sel]) {
selectedItemEl.style.backgroundImage = `url(${itemTex[sel].toDataURL()})`;
selectedItemEl.style.backgroundSize = 'cover';
} else if (ITEMS[sel]) {
selectedItemEl.textContent = ITEMS[sel].icon || '📦';
} else if (TOOLS[sel]) {
selectedItemEl.textContent = TOOLS[sel].icon || '🔧';
} else {
selectedItemEl.textContent = sel.charAt(0).toUpperCase();
}
}
export function rebuildHotbar() {
const hotbarEl = state.hotbarEl;
const inv = state.inv;
const selected = state.selected;
const toolDurability = state.toolDurability;
hotbarEl.innerHTML = '';
// Get up to 3 recent items with count > 0
const items = state.recentItems.filter(id => inv[id] > 0).slice(0, HOTBAR_SLOTS);
// If selected item not in list and has count > 0, push to front
if (inv[selected] > 0 && !items.includes(selected)) {
items.unshift(selected);
if (items.length > HOTBAR_SLOTS) items.pop();
}
// Always render HOTBAR_SLOTS slots (fill empties)
for (let si = 0; si < HOTBAR_SLOTS; si++) {
const id = items[si] || null;
const s = document.createElement('div');
s.className = 'slot' + (id && id === selected ? ' sel' : '');
// Slot number indicator (top-left)
const numEl = document.createElement('div');
numEl.style.cssText = 'position:absolute;top:1px;left:3px;font-size:10px;color:rgba(255,255,255,0.55);font-weight:700;pointer-events:none;text-shadow:1px 1px 1px rgba(0,0,0,0.7);';
numEl.textContent = (si + 1).toString();
s.appendChild(numEl);
if (id) {
renderSlotIcon(s, id);
const c = document.createElement('div');
c.className = 'count';
c.textContent = inv[id];
s.appendChild(c);
s.onclick = () => {
playSound('click');
state.selected = id;
// Move to front of recentItems
state.recentItems = state.recentItems.filter(item => item !== id);
state.recentItems.unshift(id);
rebuildHotbar();
};
if (id === 'iron_armor' && state.player.equippedArmor === 'iron_armor') {
const equipped = document.createElement('div');
equipped.className = 'equipped-indicator';
equipped.textContent = '✓';
s.appendChild(equipped);
}
// Durability bar
if (TOOLS[id] && inv[id] > 0) {
let curDur = 0, maxDur = TOOLS[id].durability;
for (const [tid, dur] of toolDurability) {
if (dur.type === id) {
curDur = dur.current;
maxDur = dur.max;
break;
}
}
if (maxDur > 0) {
const bar = document.createElement('div');
bar.style.cssText = `position:absolute;bottom:0;left:0;right:0;height:3px;background:#333;border-radius:0 0 6px 6px;overflow:hidden;`;
const fill = document.createElement('div');
const pct = curDur / maxDur;
const color = pct > 0.5 ? '#2ecc71' : pct > 0.25 ? '#f39c12' : '#e74c3c';
fill.style.cssText = `width:${pct * 100}%;height:100%;background:${color};`;
bar.appendChild(fill);
s.appendChild(bar);
}
}
}
hotbarEl.appendChild(s);
}
// Update selected item indicator above hotbar
updateSelectedItem();
}