268 lines
12 KiB
JavaScript
268 lines
12 KiB
JavaScript
/* ============================================================
|
||
public/js/buildings/bazaar.js
|
||
============================================================ */
|
||
|
||
const BAZAAR_PER_PAGE = 18;
|
||
let baz_initialized = false;
|
||
let baz_page = 1;
|
||
let baz_wood = 0, baz_iron = 0, baz_gold = 0, baz_gems = 0;
|
||
|
||
const BAZ_SVG_RANGE = `<svg viewBox="0 0 16 16" width="10" height="10" style="display:inline;vertical-align:middle;flex-shrink:0" fill="none" stroke="#e8b84b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 2 Q1 8 4 14"/><line x1="4" y1="2" x2="4" y2="14" stroke-width="0.7" stroke-dasharray="2,1.5"/><line x1="4" y1="8" x2="13" y2="8"/><polyline points="11,6 13,8 11,10"/><line x1="5" y1="7" x2="4" y2="8"/><line x1="5" y1="9" x2="4" y2="8"/></svg>`;
|
||
const BAZ_SVG_RACE = `<svg viewBox="0 0 16 16" width="10" height="10" style="display:inline;vertical-align:middle;flex-shrink:0" fill="none" stroke="#7de87d" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="2.5" r="1.4" fill="#7de87d" stroke="none"/><line x1="9" y1="4" x2="8" y2="9"/><line x1="8" y1="9" x2="10" y2="14"/><line x1="8" y1="9" x2="6" y2="13"/><line x1="8.5" y1="5.5" x2="11" y2="8"/><line x1="8.5" y1="5.5" x2="6" y2="7"/></svg>`;
|
||
|
||
const RARITY_CRYSTALS = {
|
||
1:"roter-cristal.png",2:"blauer-cristal.png",3:"gelber-cristal.png",
|
||
4:"gruener-cristal.png",5:"oranger-cristal.png",6:"violet-cristal.png",7:"pinker-cristal.png"
|
||
};
|
||
function rarityImgs(rarity, size=11) {
|
||
const file = RARITY_CRYSTALS[String(rarity)];
|
||
if (!file) return "";
|
||
const img = `<img src="/images/items/${file}" alt="${rarity}" style="width:${size}px;height:${size}px;object-fit:contain;filter:drop-shadow(0 1px 2px rgba(0,0,0,0.8));">`;
|
||
return img.repeat(parseInt(rarity)||0);
|
||
}
|
||
|
||
function loadCSS() {
|
||
if (!document.querySelector('link[href="/css/bazaar.css"]')) {
|
||
const l = document.createElement("link");
|
||
l.rel = "stylesheet"; l.href = "/css/bazaar.css";
|
||
document.head.appendChild(l);
|
||
}
|
||
}
|
||
|
||
function ensurePopup() {
|
||
if (document.getElementById("bazaar-popup")) return;
|
||
const popup = document.createElement("div");
|
||
popup.id = "bazaar-popup";
|
||
popup.className = "qm-popup";
|
||
popup.innerHTML = `
|
||
<div class="qm-popup-header">
|
||
<span class="qm-popup-title">Bazaar</span>
|
||
<div class="baz-header-right">
|
||
<span class="baz-currency">🪵 <span id="baz-wood">–</span></span>
|
||
<span class="baz-currency">⚙️ <span id="baz-iron">–</span></span>
|
||
<span class="baz-currency">🪙 <span id="baz-gold">–</span></span>
|
||
<span class="baz-currency">💎 <span id="baz-gems">–</span></span>
|
||
<span class="qm-popup-close" id="bazaar-close">✕</span>
|
||
</div>
|
||
</div>
|
||
<div class="mp-body-wrap">
|
||
<aside class="mp-tabs" id="baz-tabs">
|
||
<button class="mp-tab mp-tab-active" data-tab="baz-panel-1">
|
||
<span class="mp-tab-dot"></span><span>Händler</span>
|
||
</button>
|
||
<button class="mp-tab" data-tab="baz-panel-2">
|
||
<span class="mp-tab-dot"></span><span>Auktionen</span>
|
||
</button>
|
||
<button class="mp-tab" data-tab="baz-panel-3">
|
||
<span class="mp-tab-dot"></span><span>Tauschbörse</span>
|
||
</button>
|
||
</aside>
|
||
<div class="mp-content">
|
||
<div class="mp-panel active" id="baz-panel-1">
|
||
<div class="mp-col-header">Händler</div>
|
||
<div class="baz-grid" id="baz-grid"><div class="baz-loading">Lade Karten…</div></div>
|
||
<div class="baz-pagination" id="baz-pagination"></div>
|
||
</div>
|
||
<div class="mp-panel" id="baz-panel-2">
|
||
<div class="mp-col-header">Auktionen</div>
|
||
<div class="mp-panel-body"><span class="mp-coming-soon">Demnächst verfügbar…</span></div>
|
||
</div>
|
||
<div class="mp-panel" id="baz-panel-3">
|
||
<div class="mp-col-header">Tauschbörse</div>
|
||
<div class="mp-panel-body"><span class="mp-coming-soon">Demnächst verfügbar…</span></div>
|
||
</div>
|
||
</div>
|
||
</div>`;
|
||
document.body.appendChild(popup);
|
||
|
||
popup.querySelector("#bazaar-close").addEventListener("click", closeBazaar);
|
||
|
||
popup.querySelectorAll(".mp-tab").forEach((btn) => {
|
||
btn.addEventListener("click", () => {
|
||
popup.querySelectorAll(".mp-tab").forEach(t => t.classList.remove("mp-tab-active"));
|
||
popup.querySelectorAll(".mp-panel").forEach(p => p.classList.remove("active"));
|
||
btn.classList.add("mp-tab-active");
|
||
document.getElementById(btn.dataset.tab)?.classList.add("active");
|
||
});
|
||
});
|
||
|
||
/* Drag */
|
||
const header = popup.querySelector(".qm-popup-header");
|
||
let dragging=false, sx,sy,sl,st;
|
||
header.style.cursor="grab";
|
||
header.addEventListener("mousedown",(e)=>{
|
||
if(e.target.classList.contains("qm-popup-close"))return;
|
||
dragging=true; header.style.cursor="grabbing";
|
||
const r=popup.getBoundingClientRect();
|
||
sx=e.clientX;sy=e.clientY;sl=r.left;st=r.top;
|
||
popup.style.transform="none";
|
||
popup.style.left=sl+"px";popup.style.top=st+"px";
|
||
e.preventDefault();
|
||
});
|
||
document.addEventListener("mousemove",(e)=>{
|
||
if(!dragging)return;
|
||
popup.style.left=(sl+(e.clientX-sx))+"px";
|
||
popup.style.top=(st+(e.clientY-sy))+"px";
|
||
});
|
||
document.addEventListener("mouseup",()=>{dragging=false;header.style.cursor="grab";});
|
||
}
|
||
|
||
function updateCurrencyDisplay() {
|
||
const fmt = n => n.toLocaleString("de-DE");
|
||
const w=document.getElementById("baz-wood"); if(w) w.textContent=fmt(baz_wood);
|
||
const i=document.getElementById("baz-iron"); if(i) i.textContent=fmt(baz_iron);
|
||
const g=document.getElementById("baz-gold"); if(g) g.textContent=fmt(baz_gold);
|
||
const d=document.getElementById("baz-gems"); if(d) d.textContent=fmt(baz_gems);
|
||
}
|
||
|
||
async function loadShopCards() {
|
||
const grid = document.getElementById("baz-grid");
|
||
const pagination = document.getElementById("baz-pagination");
|
||
if (!grid) return;
|
||
grid.innerHTML = `<div class="baz-loading">Lade Karten…</div>`;
|
||
if (pagination) pagination.innerHTML = "";
|
||
|
||
try {
|
||
const res = await fetch(`/api/bazaar/cards?page=${baz_page}&limit=${BAZAAR_PER_PAGE}`);
|
||
if (!res.ok) throw new Error(res.status);
|
||
const data = await res.json();
|
||
|
||
baz_wood=data.wood; baz_iron=data.iron; baz_gold=data.gold; baz_gems=data.gems;
|
||
updateCurrencyDisplay();
|
||
|
||
if (!data.cards.length) {
|
||
grid.innerHTML = `<div class="baz-empty">Keine Karten verfügbar.</div>`;
|
||
return;
|
||
}
|
||
|
||
grid.innerHTML = data.cards.map((c) => {
|
||
const prices = [
|
||
c.price_wood > 0 ? `<span class="baz-price-wood">🪵 ${c.price_wood}</span>` : "",
|
||
c.price_iron > 0 ? `<span class="baz-price-iron">⚙️ ${c.price_iron}</span>` : "",
|
||
c.price_gold > 0 ? `<span class="baz-price-gold">🪙 ${c.price_gold}</span>` : "",
|
||
c.price_gems > 0 ? `<span class="baz-price-gems">💎 ${c.price_gems}</span>` : "",
|
||
].filter(Boolean).join("");
|
||
|
||
return `
|
||
<div class="baz-card" data-card-id="${c.id}" title="${c.name}">
|
||
<img src="/images/cards/${c.image}" alt="${c.name}"
|
||
onerror="this.src='/images/avatar_placeholder.svg'">
|
||
${c.attack != null ? `<span class="baz-stat-atk">${c.attack}</span>` : ""}
|
||
${c.defends != null ? `<span class="baz-stat-def">${c.defends}</span>` : ""}
|
||
${c.cooldown != null ? `<span class="baz-stat-cd">${c.cooldown}</span>` : ""}
|
||
${c.rarity ? `<div class="baz-rarity">${rarityImgs(c.rarity,11)}</div>` : ""}
|
||
${(c.range != null || c.race != null) ? `
|
||
<div class="baz-range-race">
|
||
${c.range != null ? `<span class="baz-badge-range">${BAZ_SVG_RANGE} ${c.range}</span>` : ""}
|
||
${c.race != null ? `<span class="baz-badge-race">${BAZ_SVG_RACE} ${c.race}</span>` : ""}
|
||
</div>` : ""}
|
||
<div class="baz-price">${prices || "<span style='color:#8b6a3c;font-size:8px;'>Kostenlos</span>"}</div>
|
||
</div>`;
|
||
}).join("");
|
||
|
||
grid.querySelectorAll(".baz-card").forEach((el) => {
|
||
el.addEventListener("click", () => {
|
||
const card = data.cards.find(c => c.id === parseInt(el.dataset.cardId));
|
||
if (card) showBuyConfirm(card);
|
||
});
|
||
});
|
||
|
||
renderPagination(pagination, data.totalPages, data.total);
|
||
} catch (err) {
|
||
grid.innerHTML = `<div class="baz-empty">Fehler beim Laden.</div>`;
|
||
console.error("[bazaar]", err);
|
||
}
|
||
}
|
||
|
||
function showBuyConfirm(card) {
|
||
document.getElementById("baz-confirm-modal")?.remove();
|
||
|
||
const canAfford =
|
||
baz_wood >= card.price_wood &&
|
||
baz_iron >= card.price_iron &&
|
||
baz_gold >= card.price_gold &&
|
||
baz_gems >= card.price_gems;
|
||
|
||
const priceStr = [
|
||
card.price_wood > 0 ? `🪵 ${card.price_wood} Holz` : "",
|
||
card.price_iron > 0 ? `⚙️ ${card.price_iron} Eisen` : "",
|
||
card.price_gold > 0 ? `🪙 ${card.price_gold} Gold` : "",
|
||
card.price_gems > 0 ? `💎 ${card.price_gems} Gems` : "",
|
||
].filter(Boolean).join(" ");
|
||
|
||
const modal = document.createElement("div");
|
||
modal.id = "baz-confirm-modal";
|
||
modal.innerHTML = `
|
||
<div class="baz-confirm-backdrop"></div>
|
||
<div class="baz-confirm-box">
|
||
<div class="baz-confirm-img-wrap">
|
||
<img src="/images/cards/${card.image}" alt="${card.name}"
|
||
onerror="this.src='/images/avatar_placeholder.svg'">
|
||
</div>
|
||
<div class="baz-confirm-name">${card.name}</div>
|
||
<div class="baz-confirm-price">${priceStr || "Kostenlos"}</div>
|
||
${!canAfford ? `<div class="baz-confirm-warn">⚠ Nicht genug Ressourcen</div>` : ""}
|
||
<div class="baz-confirm-btns">
|
||
<button class="baz-btn-cancel" id="baz-cancel">Abbrechen</button>
|
||
<button class="baz-btn-buy" id="baz-confirm" ${!canAfford?"disabled":""}>Kaufen</button>
|
||
</div>
|
||
</div>`;
|
||
document.getElementById("bazaar-popup").appendChild(modal);
|
||
|
||
modal.querySelector("#baz-cancel").onclick = () => modal.remove();
|
||
modal.querySelector(".baz-confirm-backdrop").onclick = () => modal.remove();
|
||
modal.querySelector("#baz-confirm").onclick = async () => {
|
||
const btn = modal.querySelector("#baz-confirm");
|
||
btn.disabled = true; btn.textContent = "…";
|
||
try {
|
||
const res = await fetch("/api/bazaar/buy", {
|
||
method:"POST", headers:{"Content-Type":"application/json"},
|
||
body: JSON.stringify({ card_id: card.id }),
|
||
});
|
||
const data = await res.json();
|
||
if (!res.ok) { btn.textContent = data.error||"Fehler"; setTimeout(()=>modal.remove(),2000); return; }
|
||
baz_wood=data.wood; baz_iron=data.iron; baz_gold=data.gold; baz_gems=data.gems;
|
||
updateCurrencyDisplay();
|
||
modal.remove();
|
||
showToast(`✅ ${card.name} gekauft!`);
|
||
await loadShopCards();
|
||
} catch { btn.textContent="Fehler"; setTimeout(()=>modal.remove(),2000); }
|
||
};
|
||
}
|
||
|
||
function renderPagination(el, totalPages, total) {
|
||
if (!el||!totalPages||totalPages<=1) return;
|
||
el.innerHTML = `
|
||
<button class="baz-page-btn" id="baz-prev" ${baz_page===1?"disabled":""}>◀</button>
|
||
${Array.from({length:totalPages},(_,i)=>i+1).map(p=>
|
||
`<button class="baz-page-btn ${p===baz_page?"baz-page-active":""}" data-page="${p}">${p}</button>`
|
||
).join("")}
|
||
<button class="baz-page-btn" id="baz-next" ${baz_page===totalPages?"disabled":""}>▶</button>
|
||
<span class="baz-page-info">${total} Karten</span>`;
|
||
el.querySelector("#baz-prev")?.addEventListener("click",async()=>{if(baz_page>1){baz_page--;await loadShopCards();}});
|
||
el.querySelector("#baz-next")?.addEventListener("click",async()=>{if(baz_page<totalPages){baz_page++;await loadShopCards();}});
|
||
el.querySelectorAll("[data-page]").forEach(btn=>btn.addEventListener("click",async()=>{baz_page=parseInt(btn.dataset.page);await loadShopCards();}));
|
||
}
|
||
|
||
function showToast(msg) {
|
||
const t=document.createElement("div"); t.className="baz-toast"; t.textContent=msg;
|
||
document.body.appendChild(t); setTimeout(()=>t.remove(),2800);
|
||
}
|
||
|
||
function closeBazaar() {
|
||
document.getElementById("bazaar-popup")?.classList.remove("active");
|
||
document.getElementById("qm-overlay")?.classList.remove("active");
|
||
}
|
||
|
||
export function loadBazaar() {
|
||
loadCSS();
|
||
ensurePopup();
|
||
const popup=document.getElementById("bazaar-popup");
|
||
const overlay=document.getElementById("qm-overlay");
|
||
popup.style.left="50%"; popup.style.top="50%";
|
||
popup.style.transform="translate(-50%, -50%) scale(1)";
|
||
popup.classList.add("active");
|
||
overlay?.classList.add("active");
|
||
if (!baz_initialized) { baz_initialized=true; baz_page=1; loadShopCards(); }
|
||
}
|