Erster Commit

This commit is contained in:
2026-08-22 08:40:29 +02:00
commit 875477d425
1961 changed files with 930336 additions and 0 deletions
+654
View File
@@ -0,0 +1,654 @@
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();
}
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
mail.borderville.de {
reverse_proxy 127.0.0.1:8080
}
borderville.de {
reverse_proxy 127.0.0.1:3000
}
game.borderville.de {
reverse_proxy 127.0.0.1:3000
}
panel.borderville.de {
reverse_proxy 127.0.0.1:3001
}