Files
bordervillegame/backup/backup-map-editor.js.txt
2026-08-22 08:40:29 +02:00

655 lines
16 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = `<option value="">Map laden...</option>`;
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}<br>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}$)
<button onclick="removeShopItem('${item.id}')">X</button>
`;
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();
}