const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); const mapNameInput = document.getElementById("mapName"); const mapSelect = document.getElementById("mapSelect"); const newMapBtn = document.getElementById("newMapBtn"); const saveMapBtn = document.getElementById("saveMapBtn"); const modeSelect = document.getElementById("modeSelect"); const doorTargetMap = document.getElementById("doorTargetMap"); const doorTargetX = document.getElementById("doorTargetX"); const doorTargetY = document.getElementById("doorTargetY"); const mapWidthInput = document.getElementById("mapWidth"); const mapHeightInput = document.getElementById("mapHeight"); const resizeMapBtn = document.getElementById("resizeMapBtn"); const tilePalette = document.getElementById("tilePalette"); const objectPalette = document.getElementById("objectPalette"); let objectConfig = {}; let selectedObjectType = null; let objects = []; let tileConfig = {}; let selectedTile = 1; let tileSize = 32; let tiles = []; let doors = []; let spawn = { x: 32, y: 32 }; let cols = 20; let rows = 20; let shops = []; // SHOP SYSTEM let atms = []; let currentShop = null; // ------------------------------------------------------------- // Tiles laden // ------------------------------------------------------------- fetch("/tiles.json") .then(res => res.json()) .then(data => { tileConfig = data; buildTilePalette(); loadMapList(); newMap(); }); fetch("/objectConfig.json") .then(res => res.json()) .then(data => { objectConfig = data; buildObjectPalette(); }); // ------------------------------------------------------------- // Tile-Palette bauen // ------------------------------------------------------------- function buildTilePalette() { tilePalette.innerHTML = ""; Object.entries(tileConfig).forEach(([id, tile]) => { const div = document.createElement("div"); div.style.background = tile.color; div.title = `${id}: ${tile.name} (collision: ${tile.collision})`; div.dataset.id = id; div.onclick = () => { selectedTile = parseInt(id); }; tilePalette.appendChild(div); }); } // ------------------------------------------------------------- // Object-Palette bauen // ------------------------------------------------------------- function buildObjectPalette() { objectPalette.innerHTML = ""; Object.entries(objectConfig).forEach(([type, obj]) => { const div = document.createElement("div"); div.style.width = "32px"; div.style.height = "32px"; div.style.background = obj.color; div.style.border = "2px solid #000"; div.style.display = "inline-block"; div.style.margin = "4px"; div.style.cursor = "pointer"; div.title = `${type}: ${obj.name}`; div.onclick = () => { selectedObjectType = type; }; objectPalette.appendChild(div); }); } // ------------------------------------------------------------- // Neue Map // ------------------------------------------------------------- function newMap() { tiles = []; for (let y = 0; y < rows; y++) { tiles[y] = []; for (let x = 0; x < cols; x++) { tiles[y][x] = 1; } } doors = []; objects = []; shops = []; spawn = { x: 32, y: 32 }; render(); } // ------------------------------------------------------------- // Map-Liste laden // ------------------------------------------------------------- function loadMapList() { fetch("/api/get_maps") .then(res => res.json()) .then(data => { mapSelect.innerHTML = ``; Object.keys(data.maps).forEach(name => { const opt = document.createElement("option"); opt.value = name; opt.textContent = name; mapSelect.appendChild(opt); }); }); } // ------------------------------------------------------------- // Map laden // ------------------------------------------------------------- mapSelect.onchange = () => { const name = mapSelect.value; if (!name) return; fetch("/api/get_maps") .then(res => res.json()) .then(data => { const map = data.maps[name]; mapNameInput.value = map.name; tiles = map.tiles; doors = map.doors || []; objects = map.objects || []; shops = map.shops || []; atms = map.atms || []; atms = atms || []; spawn = map.spawn; rows = tiles.length; cols = tiles[0].length; render(); }); }; // ------------------------------------------------------------- // Map speichern // ------------------------------------------------------------- saveMapBtn.onclick = () => { const name = mapNameInput.value.trim(); if (!name) return alert("Map-Name fehlt!"); const mapData = { name, spawn, tiles, doors, objects, shops, atms }; fetch("/api/save_map", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, data: mapData }) }) .then(res => res.json()) .then(data => { if (data.ok) { alert("Map gespeichert!"); loadMapList(); } else { alert("Fehler: " + data.error); } }); }; function saveMap() { const name = mapNameInput.value.trim(); if (!name) return alert("Map-Name fehlt!"); const mapData = { name, spawn, tiles, doors, objects, shops, atms }; fetch("/api/save_map", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, data: mapData }) }) .then(res => res.json()) .then(data => { if (data.ok) { alert("Map gespeichert!"); loadMapList(); } else { alert("Fehler: " + data.error); } }); } // ------------------------------------------------------------- // Map-Größe ändern // ------------------------------------------------------------- resizeMapBtn.onclick = () => { const newW = parseInt(mapWidthInput.value); const newH = parseInt(mapHeightInput.value); if (!newW || !newH) return; const newTiles = []; for (let y = 0; y < newH; y++) { newTiles[y] = []; for (let x = 0; x < newW; x++) { newTiles[y][x] = tiles[y]?.[x] ?? 1; } } tiles = newTiles; cols = newW; rows = newH; render(); }; // ------------------------------------------------------------- // Canvas Click // ------------------------------------------------------------- canvas.addEventListener("contextmenu", e => e.preventDefault()); /* canvas.addEventListener("mousedown", e => { const rect = canvas.getBoundingClientRect(); const mx = e.clientX - rect.left; const my = e.clientY - rect.top; const x = Math.floor(mx / tileSize); const y = Math.floor(my / tileSize); if (x < 0 || y < 0 || x >= cols || y >= rows) return; const mode = modeSelect.value; // TILES if (mode === "tile") { if (e.button === 0) tiles[y][x] = selectedTile; if (e.button === 2) tiles[y][x] = 0; } if (mode === "shop") { map.objects.push({ type: "shop", x: tx, y: ty }); saveMap(); } // SPAWN if (mode === "spawn") { spawn.x = x * tileSize; spawn.y = y * tileSize; } // DOORS if (mode === "door") { if (e.button === 0) { doors.push({ x: x * tileSize, y: y * tileSize, targetMap: doorTargetMap.value, targetX: parseInt(doorTargetX.value), targetY: parseInt(doorTargetY.value) }); } if (e.button === 2) { doors = doors.filter(d => !(d.x === x * tileSize && d.y === y * tileSize)); } } // OBJECTS if (mode === "object") { const ox = x * tileSize; const oy = y * tileSize; if (e.button === 0) { if (!selectedObjectType) return; objects.push({ type: selectedObjectType, x: ox, y: oy }); } if (e.button === 2) { objects = objects.filter(o => !(o.x === ox && o.y === oy)); } } // SHOPS if (mode === "shop") { const tileX = Math.floor(mx / tileSize); const tileY = Math.floor(my / tileSize); // Wenn Linksklick auf existierenden Shop → Editor öffnen if (e.button === 0) { const existing = shops.find(s => s.x === tileX && s.y === tileY); if (existing) { openShopEditor(existing); return; } // neuen Shop setzen shops.push({ id: "shop_" + Date.now(), x: tileX, y: tileY, items: [] }); } // Shop löschen if (e.button === 2) { shops = shops.filter(s => !(s.x === tileX && s.y === tileY)); if (currentShop && currentShop.x === tileX && currentShop.y === tileY) { currentShop = null; document.getElementById("shopEditor").style.display = "none"; } } if (mode === "atm") { map.atms.push({ id: "atm_" + Date.now(), x: tileX, y: tileY }); saveMap(); } } render(); }); */ canvas.addEventListener("mousedown", e => { e.preventDefault(); const rect = canvas.getBoundingClientRect(); const mx = e.clientX - rect.left; const my = e.clientY - rect.top; const tileX = Math.floor(mx / tileSize); const tileY = Math.floor(my / tileSize); if (tileX < 0 || tileY < 0 || tileX >= cols || tileY >= rows) return; const mode = modeSelect.value; // TILES if (mode === "tile") { if (e.button === 0) tiles[tileY][tileX] = selectedTile; if (e.button === 2) tiles[tileY][tileX] = 0; render(); return; } // SPAWN if (mode === "spawn") { if (e.button === 0) { spawn.x = tileX * tileSize; spawn.y = tileY * tileSize; //saveMap(); render(); } return; } // DOORS if (mode === "door") { if (e.button === 0) { doors.push({ x: tileX * tileSize, y: tileY * tileSize, targetMap: doorTargetMap.value, targetX: parseInt(doorTargetX.value), targetY: parseInt(doorTargetY.value) }); //saveMap(); render(); } if (e.button === 2) { doors = doors.filter(d => !(d.x === tileX * tileSize && d.y === tileY * tileSize)); //saveMap(); render(); } return; } // OBJECTS if (mode === "object") { const ox = tileX * tileSize; const oy = tileY * tileSize; if (e.button === 0) { if (!selectedObjectType) return; objects.push({ type: selectedObjectType, x: ox, y: oy }); //saveMap(); render(); } if (e.button === 2) { objects = objects.filter(o => !(o.x === ox && o.y === oy)); //saveMap(); render(); } return; } // SHOPS if (mode === "shop") { if (e.button === 0) { const name = prompt("Shop-Name:"); if (!name) return; fetch("/api/admin/shops", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, world: mapNameInput.value, x: tileX, y: tileY }) }).then(() => alert("Shop angelegt – Items im Admin-Panel zuweisen")); } return; } /*if (mode === "shop") { if (e.button === 0) { const existing = shops.find(s => s.x === tileX && s.y === tileY); if (existing) { openShopEditor(existing); return; } shops.push({ id: "shop_" + Date.now(), x: tileX, y: tileY, items: [] }); //saveMap(); render(); } if (e.button === 2) { shops = shops.filter(s => !(s.x === tileX && s.y === tileY)); //saveMap(); render(); } return; }*/ // ATMS (wie Shops) if (mode === "atm") { if (e.button === 0) { const existing = atms.find(a => a.x === tileX && a.y === tileY); if (existing) return; atms.push({ id: "atm_" + Date.now(), x: tileX, y: tileY }); //saveMap(); render(); } if (e.button === 2) { atms = atms.filter(a => !(a.x === tileX && a.y === tileY)); //saveMap(); render(); } return; } }); // ------------------------------------------------------------- // Render Shops // ------------------------------------------------------------- function renderShops() { shops.forEach(s => { ctx.fillStyle = "yellow"; ctx.fillRect(s.x * tileSize, s.y * tileSize, tileSize, tileSize); ctx.strokeStyle = "black"; ctx.strokeRect(s.x * tileSize, s.y * tileSize, tileSize, tileSize); }); } function renderATMs() { atms.forEach(a => { const px = a.x * tileSize; const py = a.y * tileSize; ctx.fillStyle = "blue"; ctx.fillRect(px, py, tileSize, tileSize); }); } // ------------------------------------------------------------- // shop // ------------------------------------------------------------- function openShopEditor(shop) { currentShop = shop; const editor = document.getElementById("shopEditor"); const info = document.getElementById("shopInfo"); editor.style.display = "block"; info.innerHTML = `ID: ${shop.id}
Position: (${shop.x}, ${shop.y})`; renderShopItemList(); } function renderShopItemList() { const list = document.getElementById("shopItemList"); list.innerHTML = ""; if (!currentShop) return; currentShop.items.forEach(item => { const div = document.createElement("div"); div.innerHTML = ` ${item.name} (${item.price}$) `; list.appendChild(div); }); } function addShopItem() { if (!currentShop) return; const id = prompt("Item ID:"); if (!id) return; const name = prompt("Item Name:"); if (!name) return; const priceStr = prompt("Preis:"); const price = parseInt(priceStr, 10); if (isNaN(price)) return; currentShop.items.push({ id, name, price }); renderShopItemList(); } function removeShopItem(id) { if (!currentShop) return; currentShop.items = currentShop.items.filter(i => i.id !== id); renderShopItemList(); } // ------------------------------------------------------------- // Render // ------------------------------------------------------------- function render() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Tiles for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { const id = tiles[y][x]; const tile = tileConfig[id]; ctx.fillStyle = tile ? tile.color : "#000"; ctx.fillRect(x * tileSize, y * tileSize, tileSize, tileSize); ctx.strokeStyle = "#333"; ctx.strokeRect(x * tileSize, y * tileSize, tileSize, tileSize); } } // Spawn ctx.fillStyle = "yellow"; ctx.fillRect(spawn.x, spawn.y, tileSize, tileSize); // Doors ctx.fillStyle = "orange"; doors.forEach(d => { ctx.fillRect(d.x, d.y, tileSize, tileSize); }); // Objects objects.forEach(o => { const cfg = objectConfig[o.type]; if (!cfg) return; ctx.fillStyle = cfg.color; ctx.fillRect( o.x, o.y - (cfg.height - tileSize), cfg.width, cfg.height ); }); // Shops renderShops(); renderATMs(); }