gcj,htg
This commit is contained in:
parent
994692e7b3
commit
2cf088d4b3
@ -413,19 +413,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Wood-UI: Bild + Slot auf gleiche Größe wie Booster-Slots zwingen */
|
/* Wood-UI: Bild + Slot auf gleiche Größe wie Booster-Slots zwingen */
|
||||||
#wood-ui .booster-stage {
|
#wood-ui .booster-stage,
|
||||||
|
#gold-ui .booster-stage {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#wood-ui .booster-left,
|
#wood-ui .booster-left,
|
||||||
#wood-ui .booster-slot {
|
#wood-ui .booster-slot,
|
||||||
|
#gold-ui .booster-left,
|
||||||
|
#gold-ui .booster-slot {
|
||||||
flex: 0 0 calc((100% - 50px) / 6);
|
flex: 0 0 calc((100% - 50px) / 6);
|
||||||
max-width: calc((100% - 50px) / 6);
|
max-width: calc((100% - 50px) / 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.wood-stamp {
|
.wood-stamp,
|
||||||
|
.gold-stamp {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
@ -436,7 +440,7 @@
|
|||||||
color: #e85010;
|
color: #e85010;
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
font-family: "Cinzel", serif;
|
font-family: "Cinzel", serif;
|
||||||
font-size: 14px;
|
font-size: 8px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -445,6 +449,13 @@
|
|||||||
text-shadow: 0 0 6px rgba(200, 64, 10, 0.6);
|
text-shadow: 0 0 6px rgba(200, 64, 10, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gold-stamp {
|
||||||
|
border-color: #b8960a;
|
||||||
|
color: #f0c020;
|
||||||
|
box-shadow: 0 0 6px rgba(200, 160, 10, 0.5);
|
||||||
|
text-shadow: 0 0 6px rgba(200, 160, 10, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
.event-done-overlay {
|
.event-done-overlay {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|||||||
@ -117,6 +117,9 @@ function isBoosterSpinning() {
|
|||||||
const woodUi = document.getElementById("wood-ui");
|
const woodUi = document.getElementById("wood-ui");
|
||||||
if (woodUi && woodUi.style.display !== "none" && woodUi.querySelector(".booster-slot.spinning")) return true;
|
if (woodUi && woodUi.style.display !== "none" && woodUi.querySelector(".booster-slot.spinning")) return true;
|
||||||
|
|
||||||
|
const goldUi = document.getElementById("gold-ui");
|
||||||
|
if (goldUi && goldUi.style.display !== "none" && goldUi.querySelector(".booster-slot.spinning")) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,19 +55,24 @@ export async function loadEvents() {
|
|||||||
{ id: 2, img: "/images/items/runenhaufen.png", label: "Textzeile 2" },
|
{ id: 2, img: "/images/items/runenhaufen.png", label: "Textzeile 2" },
|
||||||
{ id: 3, img: "/images/items/runenhaufen.png", label: "Textzeile 3" },
|
{ id: 3, img: "/images/items/runenhaufen.png", label: "Textzeile 3" },
|
||||||
{ id: 4, img: "/images/items/holz.png", label: "Holz Spenden", type: "wood", woodCost: 100 },
|
{ id: 4, img: "/images/items/holz.png", label: "Holz Spenden", type: "wood", woodCost: 100 },
|
||||||
{ id: 5, img: "/images/items/runenhaufen.png", label: "Textzeile 5" },
|
{ id: 5, img: "/images/items/gold.png", label: "Gold Spenden", type: "gold", goldCost: 100 },
|
||||||
];
|
];
|
||||||
|
|
||||||
// Täglichen Status + Holz-Bestand parallel laden
|
// Täglichen Status + Holz-Bestand parallel laden
|
||||||
let completedToday = [];
|
let completedToday = [];
|
||||||
let playerWood = 0;
|
let playerWood = 0;
|
||||||
|
let playerGold = 0;
|
||||||
try {
|
try {
|
||||||
const [statusRes, hudRes] = await Promise.all([
|
const [statusRes, hudRes] = await Promise.all([
|
||||||
fetch("/api/daily/status"),
|
fetch("/api/daily/status"),
|
||||||
fetch("/api/hud"),
|
fetch("/api/hud"),
|
||||||
]);
|
]);
|
||||||
if (statusRes.ok) completedToday = (await statusRes.json()).completed || [];
|
if (statusRes.ok) completedToday = (await statusRes.json()).completed || [];
|
||||||
if (hudRes.ok) playerWood = (await hudRes.json()).wood || 0;
|
if (hudRes.ok) {
|
||||||
|
const hud = await hudRes.json();
|
||||||
|
playerWood = hud.wood || 0;
|
||||||
|
playerGold = hud.gold || 0;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Laden fehlgeschlagen", e);
|
console.error("Laden fehlgeschlagen", e);
|
||||||
}
|
}
|
||||||
@ -76,14 +81,18 @@ export async function loadEvents() {
|
|||||||
<div class="events-grid" id="events-grid">
|
<div class="events-grid" id="events-grid">
|
||||||
${events.map(ev => {
|
${events.map(ev => {
|
||||||
const done = completedToday.includes(ev.id);
|
const done = completedToday.includes(ev.id);
|
||||||
const locked = !done && ev.woodCost && playerWood < ev.woodCost;
|
const locked = !done && (
|
||||||
|
(ev.woodCost && playerWood < ev.woodCost) ||
|
||||||
|
(ev.goldCost && playerGold < ev.goldCost)
|
||||||
|
);
|
||||||
|
const costLabel = ev.woodCost ? `🪵 ${ev.woodCost} Holz` : ev.goldCost ? `🪙 ${ev.goldCost} Gold` : '';
|
||||||
const cls = done ? ' event-done' : locked ? ' event-locked' : '';
|
const cls = done ? ' event-done' : locked ? ' event-locked' : '';
|
||||||
return `
|
return `
|
||||||
<div class="event-card${cls}" data-event-id="${ev.id}" data-type="${ev.type || ''}" data-done="${done}" data-locked="${locked}">
|
<div class="event-card${cls}" data-event-id="${ev.id}" data-type="${ev.type || ''}" data-done="${done}" data-locked="${locked}">
|
||||||
<div class="event-card-img-wrap">
|
<div class="event-card-img-wrap">
|
||||||
<img src="${ev.img}" alt="${ev.label}" draggable="false">
|
<img src="${ev.img}" alt="${ev.label}" draggable="false">
|
||||||
${done ? `<div class="event-done-overlay"></div>` : ''}
|
${done ? `<div class="event-done-overlay"></div>` : ''}
|
||||||
${locked ? `<div class="event-locked-overlay"><span>🪵 ${ev.woodCost} Holz<br>benötigt</span></div>` : ''}
|
${locked ? `<div class="event-locked-overlay"><span>${costLabel}<br>benötigt</span></div>` : ''}
|
||||||
</div>
|
</div>
|
||||||
<span class="event-card-label">${ev.label}</span>
|
<span class="event-card-label">${ev.label}</span>
|
||||||
</div>`;
|
</div>`;
|
||||||
@ -130,6 +139,27 @@ export async function loadEvents() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Gold-Spenden UI -->
|
||||||
|
<div id="gold-ui" class="booster-ui" style="display:none;">
|
||||||
|
<button class="booster-back-btn" id="gold-back-btn">← Zurück</button>
|
||||||
|
<div class="booster-stage">
|
||||||
|
<div class="booster-left">
|
||||||
|
<div class="wood-btn-wrap">
|
||||||
|
<img id="gold-btn" src="/images/items/gold.png" alt="Gold Spenden" draggable="false" class="booster-stapel-img">
|
||||||
|
<div class="gold-stamp" id="gold-stamp">Bitte Spenden</div>
|
||||||
|
</div>
|
||||||
|
<span class="booster-stapel-hint" id="gold-hint">100 Gold spenden</span>
|
||||||
|
</div>
|
||||||
|
<div class="booster-slots">
|
||||||
|
<div class="booster-slot" id="gold-slot-0">
|
||||||
|
<div class="booster-slot-inner">
|
||||||
|
<img class="booster-slot-img" src="/images/items/rueckseite.png" alt="?" draggable="false">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Standard Detail-Popup -->
|
<!-- Standard Detail-Popup -->
|
||||||
<div id="event-detail-overlay">
|
<div id="event-detail-overlay">
|
||||||
<div id="event-detail-popup">
|
<div id="event-detail-popup">
|
||||||
@ -147,6 +177,7 @@ export async function loadEvents() {
|
|||||||
const edpBody = body.querySelector("#edp-body");
|
const edpBody = body.querySelector("#edp-body");
|
||||||
const boosterUi = body.querySelector("#booster-ui");
|
const boosterUi = body.querySelector("#booster-ui");
|
||||||
const woodUi = body.querySelector("#wood-ui");
|
const woodUi = body.querySelector("#wood-ui");
|
||||||
|
const goldUi = body.querySelector("#gold-ui");
|
||||||
const eventsGrid = body.querySelector("#events-grid");
|
const eventsGrid = body.querySelector("#events-grid");
|
||||||
|
|
||||||
/* ── Event-Karten ── */
|
/* ── Event-Karten ── */
|
||||||
@ -161,6 +192,12 @@ export async function loadEvents() {
|
|||||||
resetBooster();
|
resetBooster();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (card.dataset.type === "gold") {
|
||||||
|
eventsGrid.style.display = "none";
|
||||||
|
goldUi.style.display = "flex";
|
||||||
|
resetGold();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (card.dataset.type === "wood") {
|
if (card.dataset.type === "wood") {
|
||||||
eventsGrid.style.display = "none";
|
eventsGrid.style.display = "none";
|
||||||
woodUi.style.display = "flex";
|
woodUi.style.display = "flex";
|
||||||
@ -229,7 +266,16 @@ export async function loadEvents() {
|
|||||||
isWoodSpinning = false;
|
isWoodSpinning = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// KEIN document.addEventListener("keydown") hier –
|
body.querySelector("#gold-back-btn").addEventListener("click", () => {
|
||||||
|
if (isGoldSpinning) return;
|
||||||
|
const goldCard = body.querySelector('.event-card[data-type="gold"]');
|
||||||
|
const goldDone = goldCard?.dataset.done === "true";
|
||||||
|
if (!goldRevealed && !goldDone) return;
|
||||||
|
eventsGrid.style.display = "";
|
||||||
|
goldUi.style.display = "none";
|
||||||
|
clearGoldIntervals();
|
||||||
|
isGoldSpinning = false;
|
||||||
|
});
|
||||||
// ESC wird zentral in quickmenu.js behandelt (verhindert Listener-Stapelung)
|
// ESC wird zentral in quickmenu.js behandelt (verhindert Listener-Stapelung)
|
||||||
|
|
||||||
/* ── Booster Zustand ── */
|
/* ── Booster Zustand ── */
|
||||||
@ -521,4 +567,121 @@ export async function loadEvents() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
preloadRarity3();
|
preloadRarity3();
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
Gold-Spenden Zustand
|
||||||
|
================================ */
|
||||||
|
let isGoldSpinning = false;
|
||||||
|
let goldRevealed = false;
|
||||||
|
let goldIntervals = {};
|
||||||
|
let goldLocked = {};
|
||||||
|
|
||||||
|
function clearGoldIntervals() {
|
||||||
|
Object.values(goldIntervals).forEach(id => clearInterval(id));
|
||||||
|
goldIntervals = {};
|
||||||
|
goldLocked = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetGold() {
|
||||||
|
clearGoldIntervals();
|
||||||
|
isGoldSpinning = false;
|
||||||
|
goldRevealed = false;
|
||||||
|
|
||||||
|
const inner = body.querySelector(`#gold-slot-0 .booster-slot-inner`);
|
||||||
|
inner.innerHTML = `<img class="booster-slot-img" src="/images/items/rueckseite.png" alt="?" draggable="false">`;
|
||||||
|
body.querySelector(`#gold-slot-0`).classList.remove("revealed", "spinning");
|
||||||
|
|
||||||
|
const btn = body.querySelector("#gold-btn");
|
||||||
|
const stamp = body.querySelector("#gold-stamp");
|
||||||
|
btn.classList.remove("used");
|
||||||
|
btn.style.opacity = "1";
|
||||||
|
btn.style.cursor = "pointer";
|
||||||
|
if (stamp) stamp.style.display = "";
|
||||||
|
body.querySelector("#gold-hint").textContent = "100 Gold spenden";
|
||||||
|
preloadRarity3();
|
||||||
|
}
|
||||||
|
|
||||||
|
function startGoldSlot(index) {
|
||||||
|
goldLocked[index] = false;
|
||||||
|
const slot = body.querySelector(`#gold-slot-${index}`);
|
||||||
|
const inner = slot.querySelector(".booster-slot-inner");
|
||||||
|
slot.classList.add("spinning");
|
||||||
|
|
||||||
|
const iv = setInterval(() => {
|
||||||
|
if (!rarity3Cards.length || goldLocked[index]) return;
|
||||||
|
const rnd = rarity3Cards[Math.floor(Math.random() * rarity3Cards.length)];
|
||||||
|
inner.innerHTML = cardHTML(rnd, true);
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
goldIntervals[index] = iv;
|
||||||
|
}
|
||||||
|
|
||||||
|
function revealGoldSlot(index, card) {
|
||||||
|
goldLocked[index] = true;
|
||||||
|
clearInterval(goldIntervals[index]);
|
||||||
|
delete goldIntervals[index];
|
||||||
|
|
||||||
|
const slot = body.querySelector(`#gold-slot-${index}`);
|
||||||
|
const inner = slot.querySelector(".booster-slot-inner");
|
||||||
|
slot.classList.remove("spinning");
|
||||||
|
slot.classList.add("revealed");
|
||||||
|
inner.innerHTML = cardHTML(card, true);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (slot.classList.contains("revealed")) inner.innerHTML = cardHTML(card, true);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.querySelector("#gold-btn").addEventListener("click", async () => {
|
||||||
|
if (isGoldSpinning) return;
|
||||||
|
if (!rarity3Cards.length) await preloadRarity3();
|
||||||
|
if (!rarity3Cards.length) return;
|
||||||
|
|
||||||
|
isGoldSpinning = true;
|
||||||
|
|
||||||
|
const btn = body.querySelector("#gold-btn");
|
||||||
|
const stamp = body.querySelector("#gold-stamp");
|
||||||
|
btn.classList.add("used");
|
||||||
|
btn.style.opacity = "0.35";
|
||||||
|
btn.style.cursor = "default";
|
||||||
|
if (stamp) stamp.style.display = "none";
|
||||||
|
body.querySelector("#gold-hint").textContent = "Wird verarbeitet...";
|
||||||
|
|
||||||
|
const backBtn = body.querySelector("#gold-back-btn");
|
||||||
|
backBtn.style.opacity = "0.35";
|
||||||
|
backBtn.style.cursor = "not-allowed";
|
||||||
|
|
||||||
|
let drawnCard = null;
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/booster/gold-donate", { method: "POST" });
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok) {
|
||||||
|
body.querySelector("#gold-hint").textContent = data.error || "Fehler";
|
||||||
|
isGoldSpinning = false;
|
||||||
|
btn.classList.remove("used");
|
||||||
|
btn.style.opacity = "1";
|
||||||
|
btn.style.cursor = "pointer";
|
||||||
|
if (stamp) stamp.style.display = "";
|
||||||
|
backBtn.style.opacity = "1";
|
||||||
|
backBtn.style.cursor = "pointer";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drawnCard = data.card;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Gold-Spenden fehlgeschlagen", e);
|
||||||
|
resetGold();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startGoldSlot(0);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
revealGoldSlot(0, drawnCard);
|
||||||
|
body.querySelector("#gold-hint").textContent = "Karte erhalten! ✓";
|
||||||
|
isGoldSpinning = false;
|
||||||
|
goldRevealed = true;
|
||||||
|
markDailyComplete(5);
|
||||||
|
backBtn.style.opacity = "1";
|
||||||
|
backBtn.style.cursor = "pointer";
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,4 +188,44 @@ router.post("/booster/wood-donate", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
POST /api/booster/gold-donate
|
||||||
|
100 Gold bezahlen → 1 zufällige Rarity-3 Karte
|
||||||
|
================================ */
|
||||||
|
router.post("/booster/gold-donate", async (req, res) => {
|
||||||
|
if (!req.session?.user) return res.status(401).json({ error: "Nicht eingeloggt" });
|
||||||
|
|
||||||
|
const userId = req.session.user.id;
|
||||||
|
try {
|
||||||
|
const [[currency]] = await db.query(
|
||||||
|
"SELECT gold FROM account_currency WHERE account_id = ?", [userId]
|
||||||
|
);
|
||||||
|
if (!currency || currency.gold < 100) {
|
||||||
|
return res.status(400).json({ error: "Nicht genug Gold (100 benötigt)" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const [pool] = await db.query(
|
||||||
|
"SELECT id, name, image, icon, max_level, rarity, attack, defends, cooldown FROM cards WHERE rarity = 3"
|
||||||
|
);
|
||||||
|
if (!pool.length) return res.status(400).json({ error: "Keine Rarity-3 Karten verfügbar" });
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
"UPDATE account_currency SET gold = gold - 100 WHERE account_id = ?", [userId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const card = pool[Math.floor(Math.random() * pool.length)];
|
||||||
|
|
||||||
|
await db.query(
|
||||||
|
`INSERT INTO user_cards (user_id, card_id, amount) VALUES (?, ?, 1)
|
||||||
|
ON DUPLICATE KEY UPDATE amount = amount + 1`,
|
||||||
|
[userId, card.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ card });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: "DB Fehler" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user