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

827 lines
21 KiB
Plaintext

const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
canvas.width = 1280;
canvas.height = 720;
const loginBox = document.getElementById("loginBox");
const registerBox = document.getElementById("registerBox");
const loginUser = document.getElementById("loginUser");
const loginPass = document.getElementById("loginPass");
const loginBtn = document.getElementById("loginBtn");
const regUser = document.getElementById("regUser");
const regPass = document.getElementById("regPass");
const regBtn = document.getElementById("regBtn");
const switchToRegister = document.getElementById("switchToRegister");
const switchToLogin = document.getElementById("switchToLogin");
const shopWindow = document.getElementById("shopWindow");
const shopList = document.getElementById("shopList");
let ws = null;
let maps = {};
let tileConfig = {};
let lastServerUpdate = 0;
let player = {
id: null,
x: 100,
y: 100,
world: "stadt"
};
player.health = 100;
player.hunger = 100;
player.thirst = 100;
player.inventory = [];
player.money = 1000;
let loggedIn = false;
let otherPlayers = [];
let keys = {};
let shops = [];
let atms = [];
let cars = [];
let drivingCarId = null;
let objectConfig = {};
fetch("/objectConfig.json")
.then(res => res.json())
.then(data => objectConfig = data);
// -------------------------------------------------------------
// Tiles laden
// -------------------------------------------------------------
fetch("/tiles.json")
.then(res => res.json())
.then(data => {
tileConfig = data;
console.log("TileConfig geladen:", tileConfig);
});
// -------------------------------------------------------------
// REGISTER
// -------------------------------------------------------------
regBtn.onclick = async () => {
const username = regUser.value;
const password = regPass.value;
const res = await fetch("/api/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password })
});
const data = await res.json();
if (!data.ok) {
alert(data.error);
return;
}
alert("Registrierung erfolgreich!");
registerBox.style.display = "none";
loginBox.style.display = "block";
};
// -------------------------------------------------------------
// LOGIN
// -------------------------------------------------------------
loginBtn.onclick = async () => {
const username = loginUser.value;
const password = loginPass.value;
const res = await fetch("/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password })
});
const data = await res.json();
if (!data.ok) {
alert(data.error);
return;
}
loginBox.style.display = "none";
canvas.style.display = "block";
startMultiplayer(data.token);
};
// Hilfsfunktion: Spieler holen
function getPlayer(playerId) {
return playersOnline.get(playerId);
}
// BANK: PIN prüfen
async function handleBankPinCheck(playerId, data, ws) {
const p = getPlayer(playerId);
if (!p) return;
const pin = Number(data.pin) || 0;
const [rows] = await db.query("SELECT pin FROM players WHERE id=?", [playerId]);
if (!rows.length) return;
if (rows[0].pin === pin) {
ws.send(JSON.stringify({ type: "bank_pin_ok" }));
} else {
ws.send(JSON.stringify({ type: "bank_pin_fail" }));
}
}
// BANK: Fenster öffnen (Kontostand + Bargeld)
async function handleBankOpen(playerId, ws) {
const p = getPlayer(playerId);
if (!p) return;
ws.send(JSON.stringify({
type: "bank_open",
money: p.state.money,
bank: p.state.bank
}));
}
// BANK: Einzahlen
async function handleBankDeposit(playerId, data, ws) {
const p = getPlayer(playerId);
if (!p) return;
const amount = Number(data.amount) || 0;
if (amount <= 0) return;
if (p.state.money < amount) return;
p.state.money -= amount;
p.state.bank += amount;
await db.query(
"UPDATE players SET money=?, bank=? WHERE id=?",
[p.state.money, p.state.bank, playerId]
);
ws.send(JSON.stringify({
type: "bank_update",
money: p.state.money,
bank: p.state.bank
}));
}
// BANK: Auszahlen
async function handleBankWithdraw(playerId, data, ws) {
const p = getPlayer(playerId);
if (!p) return;
const amount = Number(data.amount) || 0;
if (amount <= 0) return;
if (p.state.bank < amount) return;
p.state.bank -= amount;
p.state.money += amount;
await db.query(
"UPDATE players SET money=?, bank=? WHERE id=?",
[p.state.money, p.state.bank, playerId]
);
ws.send(JSON.stringify({
type: "bank_update",
money: p.state.money,
bank: p.state.bank
}));
}
// BANK: Geld an anderen Spieler senden (Bank → Bank)
async function handleBankTransfer(playerId, data, ws) {
const p = getPlayer(playerId);
if (!p) return;
const targetId = Number(data.targetId);
const amount = Number(data.amount) || 0;
if (amount <= 0) return;
const t = getPlayer(targetId);
if (!t) return;
if (p.state.bank < amount) return;
p.state.bank -= amount;
t.state.bank += amount;
await db.query("UPDATE players SET bank=? WHERE id=?", [p.state.bank, playerId]);
await db.query("UPDATE players SET bank=? WHERE id=?", [t.state.bank, targetId]);
ws.send(JSON.stringify({
type: "bank_update",
money: p.state.money,
bank: p.state.bank
}));
t.ws.send(JSON.stringify({
type: "bank_update",
money: t.state.money,
bank: t.state.bank
}));
}
// -------------------------------------------------------------
// UI SWITCH
// -------------------------------------------------------------
switchToRegister.onclick = () => {
loginBox.style.display = "none";
registerBox.style.display = "block";
canvas.style.display = "none";
};
switchToLogin.onclick = () => {
registerBox.style.display = "none";
loginBox.style.display = "block";
canvas.style.display = "none";
};
// -------------------------------------------------------------
// MULTIPLAYER STARTEN
// -------------------------------------------------------------
function startMultiplayer(token) {
ws = new WebSocket("ws://45.81.233.187:5555");
ws.onopen = () => {
ws.send(JSON.stringify({ type: "auth", token }));
};
ws.onmessage = ev => {
const msg = JSON.parse(ev.data);
if (msg.type === "auth_ok") {
player.id = msg.id;
maps = msg.maps;
document.getElementById("topMenu").style.display = "flex";
document.getElementById("statusWindow").style.display = "block";
document.getElementById("inventoryWindow").style.display = "block";
player.inventory = msg.inventory || [];
document.getElementById("inventoryWindow").style.display = "block";
renderInventoryWindow();
canvas.style.display = "block";
console.log("MAPS GELADEN:", maps);
const map = maps[player.world];
if (map && map.spawn) {
player.x = map.spawn.x;
player.y = map.spawn.y;
}
//shops=map.shops;
loggedIn = true;
return;
}
if (msg.type === "state") {
lastServerUpdate = Date.now();
const me = msg.players.find(p => p.id === player.id);
if (me) {
// nur setzen, wenn wir NICHT gerade bewegen
if (!keys["w"] && !keys["a"] && !keys["s"] && !keys["d"]) {
player.x = me.x;
player.y = me.y;
}
player.world = me.world;
player.money = me.money;
player.bank = me.bank;
player.health = me.health;
player.hunger = me.hunger;
player.thirst = me.thirst;
player.inventory = me.inventory;
//console.log("geld:", player.money);
}
otherPlayers = msg.players.filter(p => p.id !== player.id);
renderInventoryWindow();
updateStatusUI();
}
if (msg.type === "npc_dialog") {
alert("NPC sagt: " + msg.text);
}
if (msg.type === "shop_open") {
const shopWindow = document.getElementById("shopWindow");
const shopList = document.getElementById("shopList");
shopWindow.style.display = "block";
shopList.innerHTML = "";
msg.items.forEach(item => {
const div = document.createElement("div");
div.style.padding = "5px";
div.style.borderBottom = "1px solid #333";
div.innerHTML = `
${item.name} - ${item.price}$
<button onclick="buyItem('${item.id}')">Kaufen</button>
`;
shopList.appendChild(div);
});
}
if (msg.type === "shop_error") {
alert(msg.msg);
}
if (msg.type === "item_used") {
console.log(msg.hunger)
console.log(msg.thirst)
player.hunger = msg.hunger;
player.thirst = msg.thirst;
player.health = msg.health;
player.inventory = msg.inventory;
updateStatusWindow();
renderInventoryWindow();
}
if (msg.type === "atm_open") {
const pin = prompt("PIN eingeben");
ws.send(JSON.stringify({ type: "bank_pin_check", pin }));
}
if (msg.type === "bank_pin_ok") {
console.log("bank_pin_ok KOMMT AN");
ws.send(JSON.stringify({ type: "bank_open" }));
}
if (msg.type === "bank_pin_fail") {
alert("Falsche PIN!");
}
if (msg.type === "bank_open") {
console.log("BANK_OPEN KOMMT AN");
openATMWindow(msg.money, msg.bank);
}
if (msg.type === "bank_update") {
document.getElementById("atmMoney").innerText = msg.money;
document.getElementById("atmBank").innerText = msg.bank;
}
// im ws.onmessage ergänzen:
if (msg.type === "cars") {
cars = msg.cars;
const mine = cars.find(c => c.driverId === player.id);
drivingCarId = mine ? mine.id : null;
}
if (msg.type === "object_interact") {
if (msg.action === "loot") {
alert("Du hast die Kiste geöffnet!");
}
}
// Map-Daten
if (msg.type === "map_data") {
tiles = msg.tiles;
doors = msg.doors || [];
objects = msg.objects || [];
shops = msg.shops || []; // <-- WICHTIG
atms = msg.atms || [];
spawn = msg.spawn;
console.log("shops:", shops);
console.log("atms:", atms);
return;
}
};
}
function openATMWindow(money, bank) {
document.getElementById("atmMoney").innerText = money;
document.getElementById("atmBank").innerText = bank;
document.getElementById("atmBox").style.display = "block";
}
function closeATM() {
document.getElementById("atmBox").style.display = "none";
}
function atmDeposit() {
const amount = Number(document.getElementById("atmAmount").value);
ws.send(JSON.stringify({ type: "bank_deposit", amount }));
}
function atmWithdraw() {
const amount = Number(document.getElementById("atmAmount").value);
ws.send(JSON.stringify({ type: "bank_withdraw", amount }));
}
function atmTransfer() {
const amount = Number(document.getElementById("atmAmount").value);
const targetId = Number(document.getElementById("atmTarget").value);
ws.send(JSON.stringify({ type: "bank_transfer", amount, targetId }));
}
function updateStatusUI() {
const hungerBar = document.getElementById("hungerValue");
const thirstBar = document.getElementById("thirstValue");
const moneyLabel = document.getElementById("moneyValue");
if (hungerBar) hungerBar.textContent = player.hunger;
if (thirstBar) thirstBar.textContent = player.thirst;
if (moneyLabel) moneyLabel.textContent = player.money;
}
function openATM(atm) {
ws.send(JSON.stringify({
type: "bank_pin_check",
pin: prompt("PIN eingeben")
}));
}
function useItem(itemId) {
console.log("useItem wurde ausgeführt mit:", itemId);
ws.send(JSON.stringify({
type: "use_item",
itemId
}));
}
function buyItem(itemId) {
ws.send(JSON.stringify({
type: "shop_buy",
itemId
}));
}
// -------------------------------------------------------------
// CAMERA (mit Zentrierung bei kleinen Maps)
// -------------------------------------------------------------
function getCamera() {
const map = maps[player.world];
if (!map || !map.tiles || map.tiles.length === 0) {
return { x: 0, y: 0 };
}
const mapWidth = map.tiles[0].length * 32;
const mapHeight = map.tiles.length * 32;
let camX = player.x - canvas.width / 2;
let camY = player.y - canvas.height / 2;
if (mapWidth < canvas.width) {
camX = -(canvas.width / 2 - mapWidth / 2);
} else {
if (camX < 0) camX = 0;
if (camX > mapWidth - canvas.width)
camX = mapWidth - canvas.width;
}
if (mapHeight < canvas.height) {
camY = -(canvas.height / 2 - mapHeight / 2);
} else {
if (camY < 0) camY = 0;
if (camY > mapHeight - canvas.height)
camY = mapHeight - canvas.height;
}
return { x: camX, y: camY };
}
// -------------------------------------------------------------
// MOVEMENT + Collision
// -------------------------------------------------------------
document.addEventListener("keydown", e => {
keys[e.key] = true;
if (!ws) return;
if (e.key.toLowerCase() === "e") {
ws.send(JSON.stringify({
type: "interact",
x: player.x,
y: player.y,
world: player.world
}));
}
});
// Shop schließen (ESC)
document.addEventListener("keydown", e => {
if (e.key === "Escape") {
shopWindow.style.display = "none";
}
});
document.addEventListener("keyup", e => {
keys[e.key] = false;
});
function canMoveTo(nx, ny) {
const map = maps[player.world];
if (!map || !map.tiles) return true;
const tileX = Math.floor(nx / 32);
const tileY = Math.floor(ny / 32);
// TILE COLLISION
const tileId = map.tiles[tileY]?.[tileX];
const tile = tileConfig[tileId];
if (tile && tile.collision) return false;
// OBJECT COLLISION
if (map.objects) {
for (const obj of map.objects) {
const cfg = objectConfig[obj.type];
if (!cfg || !cfg.collision) continue;
const ox = obj.x;
const oy = obj.y - (cfg.height - 32); // Objekt höher als Tile
const ow = cfg.width;
const oh = cfg.height;
// Spieler-Hitbox (30x30)
const px = nx;
const py = ny;
const pw = 30;
const ph = 30;
const hit =
px < ox + ow &&
px + pw > ox &&
py < oy + oh &&
py + ph > oy;
if (hit) return false;
}
}
return true;
}
function updateNeeds() {
player.hunger -= 0.002;
player.thirst -= 0.004;
if (player.hunger < 0) player.hunger = 0;
if (player.thirst < 0) player.thirst = 0;
if (player.hunger === 0 || player.thirst === 0) {
player.health -= 0.01;
if (player.health < 0) player.health = 0;
}
}
function updateStatusWindow() {
const status = document.getElementById("statusWindow");
status.innerHTML = `
Leben: ${Math.round(player.health)}<br>
Hunger: ${Math.round(player.hunger)}<br>
Durst: ${Math.round(player.thirst)}<br>
Geld: ${player.money}$
`;
}
function updateMovement() {
let moved = false;
let nx = player.x;
let ny = player.y;
/*if (keys["w"]) ny -= 5;
if (keys["s"]) ny += 5;
if (keys["a"]) nx -= 5;
if (keys["d"]) nx += 5;*/
if (keys["w"]) { ny -= 5; moved = true; }
if (keys["s"]) { ny += 5; moved = true; }
if (keys["a"]) { nx -= 5; moved = true; }
if (keys["d"]) { nx += 5; moved = true; }
if (canMoveTo(nx, ny)) {
player.x = nx;
player.y = ny;
// FIX: nur senden, wenn WS existiert und verbunden ist
if (ws && moved && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
type: "move",
x: nx,
y: ny,
//health: player.health,
//hunger: player.hunger,
//thirst: player.thirst
}));
}
}
if (ws && player.id && moved) {
ws.send(JSON.stringify({
type: "move",
x: player.x,
y: player.y
}));
}
}
function renderInventoryWindow() {
// console.log("renderInventoryWindow wurde ausgeführt");
const list = document.getElementById("inventoryList");
if (!list) return;
list.innerHTML = "";
(player.inventory || []).forEach(item => {
const div = document.createElement("div");
div.style.display = "flex";
div.style.justifyContent = "space-between";
div.style.padding = "5px";
div.style.borderBottom = "1px solid #333";
div.innerHTML = `
<span>${item.name || item.id} x${item.amount}</span>
<button class="useItemBtn" data-id="${item.id}">Benutzen</button>
`;
list.appendChild(div);
});
const buttons = document.querySelectorAll(".useItemBtn");
//console.log("Buttons gefunden:", buttons.length);
//console.log("Buttons gefunden:", document.querySelectorAll(".useItemBtn").length);
document.querySelectorAll(".useItemBtn").forEach(btn => {
btn.onclick = () => {
//console.log("Button wurde geklickt:", btn.dataset.id);
//console.log("Buttons gefunden:", document.querySelectorAll(".useItemBtn").length);
useItem(btn.dataset.id);
};
});
}
// -------------------------------------------------------------
// RENDERING
// -------------------------------------------------------------
function renderMap() {
const map = maps[player.world];
if (!map || !map.tiles) return;
const cam = getCamera();
for (let y = 0; y < map.tiles.length; y++) {
for (let x = 0; x < map.tiles[y].length; x++) {
const tileId = map.tiles[y][x];
const tile = tileConfig[tileId];
if (!tile) continue;
ctx.fillStyle = tile.color;
ctx.fillRect(
x * 32 - cam.x,
y * 32 - cam.y,
32,
32
);
}
}
}
function renderDoors() {
const map = maps[player.world];
if (!map || !map.doors) return;
const cam = getCamera();
map.doors.forEach(d => {
ctx.fillStyle = "orange";
ctx.fillRect(
d.x - cam.x,
d.y - cam.y,
30,
30
);
});
}
function renderPlayers() {
const cam = getCamera();
ctx.fillStyle = "yellow";
ctx.fillRect(
player.x - cam.x,
player.y - cam.y,
20,
20
);
otherPlayers.forEach(p => {
if (p.world === player.world) {
ctx.fillStyle = "cyan";
ctx.fillRect(
p.x - cam.x,
p.y - cam.y,
20,
20
);
}
});
}
function renderObjects() {
const map = maps[player.world];
if (!map || !map.objects) return;
const cam = getCamera();
map.objects.forEach(o => {
const cfg = objectConfig[o.type];
if (!cfg) return;
ctx.fillStyle = cfg.color;
ctx.fillRect(
o.x - cam.x,
o.y - (cfg.height - 32) - cam.y,
cfg.width,
cfg.height
);
});
}
function renderShops() {
const cam = getCamera();
shops.forEach(s => {
const px = s.x * 32 - cam.x;
const py = s.y * 32 - cam.y;
ctx.fillStyle = "yellow";
ctx.fillRect(px, py, 32, 32);
ctx.strokeStyle = "black";
ctx.strokeRect(px, py, 32, 32);
});
}
function renderATMs() {
const cam = getCamera();
atms.forEach(a => {
const px = a.x * 32 - cam.x; // klein!
const py = a.y * 32 - cam.y; // klein!
ctx.fillStyle = "blue";
ctx.fillRect(px, py, 32, 32);
ctx.strokeStyle = "black"; // optional, wie bei Shops
ctx.strokeRect(px, py, 32, 32);
});
}
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
renderMap();
renderObjects();
renderShops();
renderATMs();
renderDoors();
renderPlayers();
requestAnimationFrame(render);
}
// -------------------------------------------------------------
// GAME LOOP
// -------------------------------------------------------------
function gameLoop() {
updateMovement();
setTimeout(gameLoop, 16);
updateStatusWindow();
//updateNeeds();
}
render();
gameLoop();