diff --git a/app.js b/app.js index fabaa06..e0e1350 100644 --- a/app.js +++ b/app.js @@ -19,6 +19,7 @@ const buildingRoutes = require("./routes/buildings"); const inventory = require("./routes/inventory"); const avatar = require("./routes/avatar"); const equip = require("./routes/equip"); +const equipment = require("./routes/equipment"); const app = express(); app.set("trust proxy", 1); @@ -191,6 +192,7 @@ app.use("/", buildingRoutes); app.use("/api/inventory", inventory); app.use("/api/avatar", avatar); app.use("/api/equip", equip); +app.use("/api/equipment", equipment); /* ======================== 404 Handler diff --git a/public/css/building.css b/public/css/building.css index 9d52cdd..c91ca5c 100644 --- a/public/css/building.css +++ b/public/css/building.css @@ -1,3 +1,7 @@ +/* ========================= + Base +========================= */ + body { margin: 0; height: 100vh; @@ -29,18 +33,16 @@ body { .building-popup { position: fixed; - left: 50%; top: 50%; - transform: translate(-50%, -50%); - width: 460px; + width: 900px; + max-width: 90vw; background: url("/images/parchment.png") center / cover no-repeat; border: 4px solid #6b4b2a; - border-radius: 10px; box-shadow: @@ -48,7 +50,6 @@ body { inset 0 0 25px rgba(0, 0, 0, 0.5); display: none; - z-index: 1000; animation: popupFade 0.25s ease; @@ -60,9 +61,7 @@ body { .popup-header { padding: 14px; - font-family: "Tangerine", serif; - font-size: 36px; background: linear-gradient(#6b4b2a, #3c2414); @@ -70,11 +69,9 @@ body { border-bottom: 2px solid #8b6a3c; display: flex; - justify-content: space-between; color: #f0d9a6; - text-shadow: 0 2px 4px black; } @@ -88,27 +85,22 @@ body { .popup-tabs { display: flex; - border-bottom: 2px solid #8b6a3c; } .popup-tabs button { flex: 1; - padding: 10px; font-family: "Cinzel", serif; - font-weight: bold; background: linear-gradient(#5c3b20, #3a2513); border: 1px solid #8b6a3c; - color: #e7d9b4; cursor: pointer; - transition: 0.2s; } @@ -118,7 +110,6 @@ body { .popup-tabs button.active { background: linear-gradient(#d4b97a, #9c7a3a); - color: #2b1b0f; } @@ -129,35 +120,23 @@ body { .popup-content { padding: 20px; - font-family: "Cinzel", serif; - font-size: 20px; - line-height: 1.6; - letter-spacing: 0.5px; background: rgba(255, 255, 255, 0.08); - border-radius: 6px; color: #2b1b0f; } -/* Titel im Inhalt */ - .popup-content h3 { font-family: "Tangerine", serif; - font-size: 32px; - color: #3b2412; - margin-top: 0; } -/* Text */ - .popup-content p { color: #3a2413; } @@ -184,28 +163,164 @@ body { background: linear-gradient(#7a5a2a, #caa24b); border: 1px solid #e0c67b; - padding: 10px 18px; color: #1a1206; - font-weight: bold; - font-family: "Cinzel", serif; - cursor: pointer; - transition: 0.2s; } .popup-content button:hover { transform: scale(1.05); - box-shadow: 0 0 10px #ffd66b, 0 0 25px #caa24b; } +/* ========================= + Tooltip +========================= */ + +#map-tooltip { + position: fixed; + pointer-events: none; + + background: rgba(0, 0, 0, 0.85); + color: #fff; + + padding: 8px 12px; + border-radius: 6px; + + font-size: 14px; + + display: none; + z-index: 9999; + + max-width: 200px; + + box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); +} + +#map-tooltip strong { + color: #ffd700; +} + +/* ========================= + Character Window +========================= */ + +#character-ui { + display: flex; + justify-content: center; + gap: 60px; + + margin-bottom: 40px; + margin-top: -20px; +} + +.equip-left, +.equip-right { + display: flex; + flex-direction: column; + gap: 12px; +} + +.character img { + height: 280px; + margin-top: -10px; +} + +/* ========================= + Equipment Slots +========================= */ + +.equip-slot { + width: 70px; + height: 70px; + + background-image: url("/images/items/slot_mittel.png"); + background-size: cover; + background-position: center; + + display: flex; + align-items: center; + justify-content: center; +} + +/* ========================= + Inventory +========================= */ + +#inventory-grid { + display: grid; + + grid-template-columns: repeat(8, 70px); + grid-template-rows: repeat(4, 70px); + + gap: 8px; + + justify-content: center; + + margin-top: 30px; +} + +.inventory-slot { + width: 70px; + height: 70px; + + background-image: url("/images/items/slot_mittel.png"); + background-size: cover; + background-position: center; + + display: flex; + align-items: center; + justify-content: center; + + cursor: pointer; +} + +.item-common { + border: 2px solid #9d9d9d; +} + +.item-rare { + border: 2px solid #0070dd; +} + +.item-epic { + border: 2px solid #a335ee; +} + +.item-legendary { + border: 2px solid #ff8000; +} + +.inventory-slot img, +.equip-slot img { + width: 80%; + height: 80%; + object-fit: contain; +} + +.inventory-slot:hover { + filter: brightness(1.2); +} + +/* ========================= + Tooltip (Items) +========================= */ + +#item-tooltip { + position: absolute; + background: #111; + border: 1px solid #555; + padding: 6px; + color: white; + display: none; +} + /* ========================= Animation ========================= */ @@ -221,115 +336,3 @@ body { transform: translate(-50%, -50%); } } - -/* ========================= - Popup Titel -========================= */ - -#popup-title { - font-weight: bold; -} - -#map-tooltip { - position: fixed; - pointer-events: none; - background: rgba(0, 0, 0, 0.85); - color: #fff; - padding: 8px 12px; - border-radius: 6px; - font-size: 14px; - display: none; - z-index: 9999; - max-width: 200px; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); -} - -#map-tooltip strong { - color: #ffd700; -} - -#inventory-grid { - display: grid; - grid-template-columns: repeat(6, 64px); - gap: 6px; - margin-top: 10px; -} - -.inventory-slot { - width: 64px; - height: 64px; - background: #1b1b1b; - border: 1px solid #8b6f3d; - position: relative; - box-shadow: 0 0 4px #000 inset; -} - -.inventory-slot img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.item-amount { - position: absolute; - bottom: 2px; - right: 4px; - font-size: 12px; - color: white; -} - -#item-tooltip { - position: absolute; - background: #111; - border: 1px solid #555; - padding: 6px; - color: white; - display: none; -} - -#character-ui { - display: flex; - justify-content: center; - gap: 40px; - margin-bottom: 20px; -} - -.equip-left, -.equip-right { - display: flex; - flex-direction: column; - gap: 10px; -} - -.equip-slot { - width: 64px; - height: 64px; - border: 2px solid #8b6f3d; - background: #111; -} - -.equip-slot img { - width: 100%; -} - -.character img { - height: 260px; -} - -#inventory-grid { - display: grid; - grid-template-columns: repeat(8, 64px); - gap: 6px; -} - -.inventory-slot { - width: 64px; - height: 64px; - border: 1px solid #555; - background: #1b1b1b; - cursor: pointer; -} - -.inventory-slot img { - width: 100%; -} diff --git a/public/images/items/slot_gross.png b/public/images/items/slot_gross.png new file mode 100644 index 0000000..44de5c6 Binary files /dev/null and b/public/images/items/slot_gross.png differ diff --git a/public/images/items/slot_klein.png b/public/images/items/slot_klein.png new file mode 100644 index 0000000..fa079d7 Binary files /dev/null and b/public/images/items/slot_klein.png differ diff --git a/public/images/items/slot_mittel.png b/public/images/items/slot_mittel.png new file mode 100644 index 0000000..e94c8a1 Binary files /dev/null and b/public/images/items/slot_mittel.png differ diff --git a/public/js/buildings/wohnhaus.js b/public/js/buildings/wohnhaus.js index 179466a..2a2caf3 100644 --- a/public/js/buildings/wohnhaus.js +++ b/public/js/buildings/wohnhaus.js @@ -33,6 +33,22 @@ export async function loadWohnhaus() { `; loadInventory(); + loadEquipment(); +} + +async function loadEquipment() { + const res = await fetch("/api/equipment"); + const equipment = await res.json(); + + equipment.forEach((item) => { + const slot = document.querySelector( + '.equip-slot[data-slot="' + item.slot + '"]', + ); + + if (!slot) return; + + slot.innerHTML = ``; + }); } async function loadInventory() { @@ -48,7 +64,9 @@ async function loadInventory() { html += `
+data-slot="${item.equip_slot}" +data-id="${item.id}" +data-level="${item.level}">
`; @@ -61,16 +79,34 @@ data-slot="${item.equip_slot || ""}"> function initEquip() { document.querySelectorAll(".inventory-slot").forEach((item) => { + const icon = item.querySelector("img").src; + item.addEventListener("click", () => { - const slot = item.dataset.slot; + const slotType = item.dataset.slot; const target = document.querySelector( - '.equip-slot[data-slot="' + slot + '"]', + '.equip-slot[data-slot="' + slotType + '"]', ); if (!target) return; - target.innerHTML = item.innerHTML; + target.innerHTML = ``; + + saveEquipment(slotType, item.dataset.id, item.dataset.level); }); }); } + +async function saveEquipment(slot, itemId, itemLevelId) { + await fetch("/api/equip", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + slot: slot, + itemId: itemId, + itemLevelId: itemLevelId, + }), + }); +} diff --git a/routes/equip.js b/routes/equip.js index 177575e..162db6b 100644 --- a/routes/equip.js +++ b/routes/equip.js @@ -3,16 +3,20 @@ const router = express.Router(); const db = require("../database/database"); router.post("/", async (req, res) => { - const userId = 1; - const { itemId, itemLevelId } = req.body; + const userId = req.session.user.id; + + const { slot, itemId, itemLevelId } = req.body; await db.query( ` -REPLACE INTO avatar_equipment -(user_id,slot,item_id,item_level_id) -VALUES (?,?,?,?) -`, - [userId, "weapon", itemId, itemLevelId], + INSERT INTO avatar_equipment + (user_id,slot,item_id,item_level_id) + VALUES (?,?,?,?) + ON DUPLICATE KEY UPDATE + item_id = VALUES(item_id), + item_level_id = VALUES(item_level_id) + `, + [userId, slot, itemId, itemLevelId], ); res.json({ success: true }); diff --git a/routes/equipment.js b/routes/equipment.js new file mode 100644 index 0000000..ae9edbc --- /dev/null +++ b/routes/equipment.js @@ -0,0 +1,23 @@ +const express = require("express"); +const router = express.Router(); +const db = require("../database/database"); + +router.get("/", async (req, res) => { + const userId = req.session.user.id; + + const [rows] = await db.query( + ` +SELECT +ae.slot, +items.icon +FROM avatar_equipment ae +LEFT JOIN items ON items.id = ae.item_id +WHERE ae.user_id=? +`, + [userId], + ); + + res.json(rows); +}); + +module.exports = router;