dok/public/js/buildings/character-house.js
2026-03-29 10:45:47 +01:00

375 lines
11 KiB
JavaScript

let inventoryPage = 0;
const slotsPerPage = 32;
let inventoryItems = [];
let totalInventoryPages = 1;
export async function loadCharacterHouse() {
const ui = document.querySelector(".building-ui");
ui.innerHTML = `
<div id="character-ui">
<!-- Linke Spalte -->
<div class="equip-col">
<div class="slot" data-slot="shoulder"><div class="slot-label">Schulter</div></div>
<div class="slot" data-slot="gloves"><div class="slot-label">Handschuhe</div></div>
<div class="slot" data-slot="ring1"><div class="slot-label">Ring 1</div></div>
</div>
<!-- Avatar Mitte -->
<div class="character-center">
<div class="equip-top">
<div class="slot" data-slot="helmet"><div class="slot-label">Helm</div></div>
<div class="slot" data-slot="amulet"><div class="slot-label">Amulett</div></div>
</div>
<div class="avatar-wrapper">
<img class="avatar-base" src="/images/avatar_silhouette.svg" alt="Avatar">
<div class="avatar-overlay" data-slot="helmet"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="amulet"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="shoulder"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="weapon"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="gloves"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="shield"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="belt"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="ring1"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="ring2"><img src="" alt=""></div>
<div class="avatar-overlay" data-slot="boots"><img src="" alt=""></div>
</div>
<div class="equip-bottom">
<div class="slot" data-slot="belt"><div class="slot-label">Guertel</div></div>
<div class="slot" data-slot="boots"><div class="slot-label">Stiefel</div></div>
</div>
</div>
<!-- Rechte Spalte -->
<div class="equip-col">
<div class="slot" data-slot="weapon"><div class="slot-label">Waffe</div></div>
<div class="slot" data-slot="shield"><div class="slot-label">Schild</div></div>
<div class="slot" data-slot="ring2"><div class="slot-label">Ring 2</div></div>
</div>
</div>
<div class="inventory-section-title">Inventar</div>
<div id="inventory-wrapper">
<div id="inventory-grid"></div>
</div>
<div id="inventory-nav">
<button id="inv-left">&#9668;</button>
<div id="inventory-page"></div>
<button id="inv-right">&#9658;</button>
</div>`;
// Variablen zurücksetzen beim Öffnen
inventoryPage = 0;
totalInventoryPages = 1;
inventoryItems = [];
// Buttons zuerst initialisieren
initInventoryButtons();
await loadInventory();
await loadEquipment();
initDrag();
initSlotDrag();
initDrop();
initInventoryDrop();
}
/* ================================
AVATAR OVERLAY AKTUALISIEREN
================================ */
function updateAvatarOverlay(slotName, iconSrc) {
const overlay = document.querySelector(
`.avatar-overlay[data-slot="${slotName}"]`,
);
if (!overlay) return;
const img = overlay.querySelector("img");
if (iconSrc) {
img.src = iconSrc;
overlay.classList.add("visible");
} else {
overlay.classList.remove("visible");
setTimeout(() => {
img.src = "";
}, 250);
}
}
/* ================================
INVENTAR LADEN
================================ */
async function loadInventory() {
const res = await fetch("/api/inventory");
const data = await res.json();
inventoryItems = data.items;
totalInventoryPages = data.totalPages;
renderInventory();
}
/* ================================
INVENTAR RENDERN
================================ */
function renderInventory() {
const grid = document.getElementById("inventory-grid");
let html = "";
const start = inventoryPage * slotsPerPage;
const end = start + slotsPerPage;
for (let i = start; i < end; i++) {
const item = inventoryItems[i];
if (item) {
const icon = item.icon || "/images/items/default.png";
html += `
<div class="inventory-slot"
data-slot="${item.equip_slot || ""}"
data-id="${item.id}"
data-level="${item.level}"
draggable="true">
<img src="${icon}">
</div>`;
} else {
html += `<div class="inventory-slot empty"></div>`;
}
}
grid.innerHTML = html;
document.getElementById("inventory-page").innerText =
"Seite " + (inventoryPage + 1) + " / " + totalInventoryPages;
// Buttons aktivieren/deaktivieren
const btnLeft = document.getElementById("inv-left");
const btnRight = document.getElementById("inv-right");
if (btnLeft) btnLeft.disabled = inventoryPage === 0;
if (btnRight) btnRight.disabled = inventoryPage >= totalInventoryPages - 1;
initDrag();
}
/* ================================
INVENTAR BUTTONS
================================ */
function initInventoryButtons() {
document.getElementById("inv-left").onclick = () => {
if (inventoryPage > 0) {
inventoryPage--;
renderInventory();
}
};
document.getElementById("inv-right").onclick = () => {
if (inventoryPage < totalInventoryPages - 1) {
inventoryPage++;
renderInventory();
}
};
}
/* ================================
EQUIPMENT LADEN
================================ */
async function loadEquipment() {
const res = await fetch("/api/equipment");
const equipment = await res.json();
equipment.forEach((item) => {
if (!item.item_id) return;
const slot = document.querySelector('.slot[data-slot="' + item.slot + '"]');
if (!slot) return;
const label = slot.querySelector(".slot-label");
slot.innerHTML = `<img src="${item.icon}" draggable="true" data-id="${item.item_id}" data-level="${item.item_level_id}">`;
if (label) slot.appendChild(label);
slot.classList.add("has-item");
// Avatar Overlay aktualisieren
updateAvatarOverlay(item.slot, item.icon);
});
}
/* ================================
DRAG INVENTAR
================================ */
function initDrag() {
document.querySelectorAll(".inventory-slot:not(.empty)").forEach((item) => {
item.addEventListener("dragstart", (e) => {
e.dataTransfer.setData("itemId", item.dataset.id);
e.dataTransfer.setData("itemLevel", item.dataset.level);
e.dataTransfer.setData("slot", item.dataset.slot);
e.dataTransfer.setData("source", "inventory");
});
});
}
/* ================================
SLOT DRAG
================================ */
function initSlotDrag() {
document.querySelectorAll(".slot img").forEach((img) => {
img.setAttribute("draggable", "true");
img.addEventListener("dragstart", (e) => {
const slot = img.closest(".slot");
e.dataTransfer.setData("itemId", img.dataset.id);
e.dataTransfer.setData("itemLevel", img.dataset.level);
e.dataTransfer.setData("slot", slot.dataset.slot);
e.dataTransfer.setData("source", "slot");
});
});
}
/* ================================
SLOT KOMPATIBILITÄT PRÜFEN
Ring-Items (equip_slot="ring") passen in ring1 und ring2
================================ */
function isSlotCompatible(itemSlot, targetSlot) {
if (itemSlot === targetSlot) return true;
if (itemSlot === "ring" && (targetSlot === "ring1" || targetSlot === "ring2"))
return true;
return false;
}
/* ================================
SLOT DROP
================================ */
function initDrop() {
document.querySelectorAll(".slot").forEach((slot) => {
slot.addEventListener("dragover", (e) => e.preventDefault());
slot.addEventListener("drop", (e) => {
e.preventDefault();
const itemId = e.dataTransfer.getData("itemId");
const itemLevel = e.dataTransfer.getData("itemLevel");
const itemSlot = e.dataTransfer.getData("slot");
const source = e.dataTransfer.getData("source");
const targetSlot = slot.dataset.slot;
if (!itemSlot || !isSlotCompatible(itemSlot, targetSlot)) return;
let icon;
if (source === "inventory") {
const inventoryItem = document.querySelector(
'.inventory-slot[data-id="' + itemId + '"]',
);
if (!inventoryItem) return;
icon = inventoryItem.querySelector("img").src;
// Item aus dem Array entfernen damit es sich nicht verdoppelt
inventoryItems = inventoryItems.filter(
(item) => String(item.id) !== String(itemId),
);
inventoryItem.classList.add("empty");
inventoryItem.innerHTML = "";
} else {
const slotItem = document.querySelector(
'.slot[data-slot="' + itemSlot + '"] img',
);
if (!slotItem) return;
icon = slotItem.src;
}
const label = slot.querySelector(".slot-label");
slot.innerHTML = `<img src="${icon}" draggable="true" data-id="${itemId}" data-level="${itemLevel}">`;
if (label) slot.appendChild(label);
slot.classList.add("has-item");
// Avatar Overlay anzeigen
updateAvatarOverlay(targetSlot, icon);
initDrag();
initSlotDrag();
saveEquipment(targetSlot, itemId, itemLevel);
});
});
}
/* ================================
INVENTAR DROP
================================ */
function initInventoryDrop() {
const grid = document.getElementById("inventory-grid");
grid.addEventListener("dragover", (e) => e.preventDefault());
grid.addEventListener("drop", (e) => {
e.preventDefault();
const source = e.dataTransfer.getData("source");
if (source !== "slot") return;
const itemId = e.dataTransfer.getData("itemId");
const itemLevel = e.dataTransfer.getData("itemLevel");
const slotName = e.dataTransfer.getData("slot");
const slot = document.querySelector('.slot[data-slot="' + slotName + '"]');
if (!slot) return;
const img = slot.querySelector("img");
if (!img) return;
// Ring-Slots normalisiert als "ring" im Inventar speichern
const normalizedSlot =
slotName === "ring1" || slotName === "ring2" ? "ring" : slotName;
inventoryItems.push({
id: itemId,
level: itemLevel,
equip_slot: normalizedSlot,
icon: img.src,
});
renderInventory();
const label = slot.querySelector(".slot-label");
slot.innerHTML = "";
if (label) slot.appendChild(label);
slot.classList.remove("has-item");
// Avatar Overlay entfernen
updateAvatarOverlay(slotName, null);
saveEquipment(slotName, null, null);
initDrag();
initSlotDrag();
});
}
/* ================================
DB SPEICHERN
================================ */
async function saveEquipment(slot, itemId, itemLevelId) {
await fetch("/api/equip", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ slot, itemId, itemLevelId }),
});
}