This commit is contained in:
Cay 2026-03-13 15:58:25 +00:00
parent 640f3c1549
commit 88705b5a06
8 changed files with 224 additions and 156 deletions

2
app.js
View File

@ -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

View File

@ -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%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -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 = `<img src="${item.icon}">`;
});
}
async function loadInventory() {
@ -48,7 +64,9 @@ async function loadInventory() {
html += `
<div class="inventory-slot"
data-slot="${item.equip_slot || ""}">
data-slot="${item.equip_slot}"
data-id="${item.id}"
data-level="${item.level}">
<img src="${icon}">
</div>
`;
@ -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 = `<img src="${icon}">`;
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,
}),
});
}

View File

@ -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 });

23
routes/equipment.js Normal file
View File

@ -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;