From 57622d79c79544b00d6498a30cd19c775f5fb2ab Mon Sep 17 00:00:00 2001
From: Serverlele30 <46974281+Serverlele30@users.noreply.github.com>
Date: Wed, 17 Dec 2025 19:00:33 +0100
Subject: [PATCH] Add files via upload
---
fly.toml | 17 +
package.json | 23 +
public/index.html | 1443 +++++++++++++++++++++++++++++++++++++++++++++
railway.json | 11 +
render.yaml | 11 +
server.js | 570 ++++++++++++++++++
6 files changed, 2075 insertions(+)
create mode 100644 fly.toml
create mode 100644 package.json
create mode 100644 public/index.html
create mode 100644 railway.json
create mode 100644 render.yaml
create mode 100644 server.js
diff --git a/fly.toml b/fly.toml
new file mode 100644
index 0000000..ea6b635
--- /dev/null
+++ b/fly.toml
@@ -0,0 +1,17 @@
+app = "tank-warfare"
+primary_region = "fra"
+
+[build]
+
+[http_service]
+ internal_port = 3000
+ force_https = true
+ auto_stop_machines = true
+ auto_start_machines = true
+ min_machines_running = 1
+ processes = ["app"]
+
+[[vm]]
+ cpu_kind = "shared"
+ cpus = 1
+ memory_mb = 256
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..1dfacb3
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "tank-warfare",
+ "version": "1.0.0",
+ "description": "Multiplayer tank battle game with adaptive AI",
+ "main": "server.js",
+ "scripts": {
+ "start": "node server.js",
+ "dev": "nodemon server.js"
+ },
+ "keywords": ["game", "multiplayer", "tank", "pvp"],
+ "author": "",
+ "license": "MIT",
+ "dependencies": {
+ "express": "^4.18.2",
+ "socket.io": "^4.6.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..e702d8f
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,1443 @@
+
+
+
+
+
+ Tank Warfare - Multiplayer Battle Arena
+
+
+
+
+
+
+
+
+
Spieler
+
๐ฎ Online
+
+
Kills: 0 | Deaths: 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Verbinde...
+
+
+
+
+
โ๏ธ TANK WARFARE โ๏ธ
+
๐ MULTIPLAYER ARENA
+
+
+
+
๐ฎ Steuerung
+
Maus bewegen โ Tank folgt
+
Linke Maustaste โ Schieรen
+
ESC โ Pause
+
+ ๐บ๏ธ 3000x3000 Riesen-Map | ๐ Mit Sound | ๐ค 10 Adaptive Bots | ๐ PvP
+
+
+
+
+
+
+
+
๐ ELIMINIERT ๐
+
+
+
+
+
+
+
+
+
diff --git a/railway.json b/railway.json
new file mode 100644
index 0000000..46bcfcf
--- /dev/null
+++ b/railway.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://railway.app/railway.schema.json",
+ "build": {
+ "builder": "NIXPACKS"
+ },
+ "deploy": {
+ "startCommand": "npm start",
+ "restartPolicyType": "ON_FAILURE",
+ "restartPolicyMaxRetries": 10
+ }
+}
diff --git a/render.yaml b/render.yaml
new file mode 100644
index 0000000..a21991c
--- /dev/null
+++ b/render.yaml
@@ -0,0 +1,11 @@
+services:
+ - type: web
+ name: tank-warfare
+ env: node
+ plan: free
+ buildCommand: npm install
+ startCommand: npm start
+ healthCheckPath: /
+ envVars:
+ - key: NODE_ENV
+ value: production
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..6de4bcd
--- /dev/null
+++ b/server.js
@@ -0,0 +1,570 @@
+const express = require('express');
+const http = require('http');
+const socketIO = require('socket.io');
+const path = require('path');
+
+const app = express();
+const server = http.createServer(app);
+const io = socketIO(server, {
+ cors: { origin: "*", methods: ["GET", "POST"] }
+});
+
+app.use(express.static(path.join(__dirname, 'public')));
+
+const WORLD_WIDTH = 3000;
+const WORLD_HEIGHT = 3000;
+const PLAYER_SIZE = 30;
+const BULLET_SPEED = 12;
+const MAX_HEALTH = 100;
+const MAX_BOTS = 10; // More bots for larger map
+const TICK_RATE = 60;
+
+const gameState = {
+ players: new Map(),
+ bots: new Map(),
+ bullets: [],
+ powerups: [],
+ nextBulletId: 0,
+ nextBotId: 0,
+ averagePlayerScore: 0
+};
+
+function calculateBotDifficulty() {
+ const players = Array.from(gameState.players.values());
+ if (players.length === 0) return 1.0;
+ const avgScore = players.reduce((sum, p) => sum + p.score, 0) / players.length;
+ gameState.averagePlayerScore = avgScore;
+ return Math.min(2.0, 0.5 + (avgScore / 500));
+}
+
+class Player {
+ constructor(id, name, x, y, isBot = false) {
+ this.id = id;
+ this.name = name;
+ this.x = x;
+ this.y = y;
+ this.targetX = x;
+ this.targetY = y;
+ this.vx = 0;
+ this.vy = 0;
+ this.angle = 0;
+ this.health = MAX_HEALTH;
+ this.maxHealth = MAX_HEALTH;
+ this.score = 0;
+ this.kills = 0;
+ this.deaths = 0;
+ this.isBot = isBot;
+ this.isDead = false;
+ this.respawnTime = 0;
+ this.color = isBot ? this.randomColor() : '#00ff00';
+ this.baseSpeed = 3.5;
+ this.speed = 3.5;
+ this.lastShot = Date.now();
+ this.fireRate = 250;
+ this.baseDamage = 25;
+ this.damage = 25;
+ this.shield = false;
+ this.shieldTime = 0;
+ this.rapidFire = false;
+ this.rapidFireTime = 0;
+ this.damageBoost = false;
+ this.damageBoostTime = 0;
+ this.invisible = false;
+ this.invisibleTime = 0;
+ this.moveTimer = 0;
+ this.difficulty = 1.0;
+ this.lastTarget = null;
+ this.strategyTimer = 0;
+ this.strategy = 'aggressive';
+ this.lastKiller = null;
+ }
+
+ randomColor() {
+ const colors = ['#ff6b6b', '#4ecdc4', '#45b7d1', '#f7b731', '#5f27cd', '#ff9ff3', '#ee5a6f', '#00d2ff'];
+ return colors[Math.floor(Math.random() * colors.length)];
+ }
+
+ takeDamage(amount, attackerId) {
+ if (this.shield || this.isDead) return false;
+ this.health -= amount;
+ if (this.health <= 0) {
+ this.deaths++;
+ this.isDead = true;
+ this.respawnTime = 300;
+ this.lastKiller = attackerId;
+ return true;
+ }
+ return false;
+ }
+
+ update() {
+ if (this.isDead) {
+ if (this.respawnTime > 0) this.respawnTime--;
+ return;
+ }
+
+ if (this.isBot) {
+ this.difficulty = calculateBotDifficulty();
+ this.updateBot();
+ }
+
+ const dx = this.targetX - this.x;
+ const dy = this.targetY - this.y;
+ const dist = Math.hypot(dx, dy);
+
+ if (dist > 5) {
+ const moveSpeed = Math.min(this.speed, dist * 0.15);
+ this.vx = (dx / dist) * moveSpeed;
+ this.vy = (dy / dist) * moveSpeed;
+ } else {
+ this.vx *= 0.9;
+ this.vy *= 0.9;
+ }
+
+ this.x += this.vx;
+ this.y += this.vy;
+ this.x = Math.max(PLAYER_SIZE, Math.min(WORLD_WIDTH - PLAYER_SIZE, this.x));
+ this.y = Math.max(PLAYER_SIZE, Math.min(WORLD_HEIGHT - PLAYER_SIZE, this.y));
+
+ if (this.shield) {
+ this.shieldTime--;
+ if (this.shieldTime <= 0) this.shield = false;
+ }
+
+ if (this.rapidFire) {
+ this.rapidFireTime--;
+ if (this.rapidFireTime <= 0) {
+ this.rapidFire = false;
+ this.fireRate = 250;
+ }
+ }
+
+ if (this.damageBoost) {
+ this.damageBoostTime--;
+ if (this.damageBoostTime <= 0) {
+ this.damageBoost = false;
+ this.damage = this.baseDamage;
+ }
+ }
+
+ if (this.invisible) {
+ this.invisibleTime--;
+ if (this.invisibleTime <= 0) this.invisible = false;
+ }
+ }
+
+ updateBot() {
+ this.moveTimer++;
+ this.strategyTimer++;
+
+ if (this.strategyTimer > 600) {
+ const strategies = ['aggressive', 'defensive', 'balanced'];
+ this.strategy = strategies[Math.floor(Math.random() * strategies.length)];
+ this.strategyTimer = 0;
+ }
+
+ const allTargets = [
+ ...Array.from(gameState.players.values()),
+ ...Array.from(gameState.bots.values())
+ ].filter(t => t.id !== this.id && !t.isDead);
+
+ if (this.moveTimer % 30 === 0) {
+ let bestTarget = null;
+ let bestScore = -Infinity;
+
+ allTargets.forEach(target => {
+ const dist = Math.hypot(target.x - this.x, target.y - this.y);
+ const healthRatio = target.health / target.maxHealth;
+
+ let score = 0;
+ switch(this.strategy) {
+ case 'aggressive':
+ score = (1 - healthRatio) * 100 - dist * 0.5;
+ break;
+ case 'defensive':
+ score = -dist * 2;
+ break;
+ case 'balanced':
+ score = (1 - healthRatio) * 50 - dist * 0.3 + (target.isBot ? -20 : 30);
+ break;
+ }
+
+ if (score > bestScore) {
+ bestScore = score;
+ bestTarget = target;
+ }
+ });
+
+ let nearestPowerup = null;
+ let powerupDist = Infinity;
+ gameState.powerups.forEach(powerup => {
+ const dist = Math.hypot(powerup.x - this.x, powerup.y - this.y);
+ if (dist < powerupDist && dist < 400) {
+ powerupDist = dist;
+ nearestPowerup = powerup;
+ }
+ });
+
+ if (this.health < 40 && nearestPowerup && nearestPowerup.type === 'health') {
+ this.targetX = nearestPowerup.x;
+ this.targetY = nearestPowerup.y;
+ } else if (nearestPowerup && Math.random() < 0.4) {
+ this.targetX = nearestPowerup.x;
+ this.targetY = nearestPowerup.y;
+ } else if (bestTarget) {
+ const distToTarget = Math.hypot(bestTarget.x - this.x, bestTarget.y - this.y);
+ const optimalDist = this.strategy === 'defensive' ? 350 : this.strategy === 'aggressive' ? 200 : 250;
+
+ if (distToTarget > optimalDist) {
+ this.targetX = this.x + (bestTarget.x - this.x) * 0.5 + (Math.random() - 0.5) * 100 * this.difficulty;
+ this.targetY = this.y + (bestTarget.y - this.y) * 0.5 + (Math.random() - 0.5) * 100 * this.difficulty;
+ } else if (distToTarget < optimalDist * 0.7) {
+ this.targetX = this.x - (bestTarget.x - this.x) * 0.3 + (Math.random() - 0.5) * 100;
+ this.targetY = this.y - (bestTarget.y - this.y) * 0.3 + (Math.random() - 0.5) * 100;
+ } else {
+ const perpAngle = Math.atan2(bestTarget.y - this.y, bestTarget.x - this.x) + Math.PI / 2;
+ this.targetX = this.x + Math.cos(perpAngle) * 100;
+ this.targetY = this.y + Math.sin(perpAngle) * 100;
+ }
+ this.lastTarget = bestTarget;
+ } else {
+ this.targetX = Math.random() * WORLD_WIDTH;
+ this.targetY = Math.random() * WORLD_HEIGHT;
+ }
+
+ this.targetX = Math.max(PLAYER_SIZE, Math.min(WORLD_WIDTH - PLAYER_SIZE, this.targetX));
+ this.targetY = Math.max(PLAYER_SIZE, Math.min(WORLD_HEIGHT - PLAYER_SIZE, this.targetY));
+ }
+
+ if (this.lastTarget && !this.lastTarget.isDead && this.lastTarget.health > 0) {
+ const leadAmount = this.difficulty * 0.3;
+ const targetVx = this.lastTarget.vx || 0;
+ const targetVy = this.lastTarget.vy || 0;
+
+ const aimX = this.lastTarget.x + targetVx * leadAmount * 10;
+ const aimY = this.lastTarget.y + targetVy * leadAmount * 10;
+
+ this.angle = Math.atan2(aimY - this.y, aimX - this.x) + (Math.random() - 0.5) * (0.4 / this.difficulty);
+
+ const dist = Math.hypot(this.lastTarget.x - this.x, this.lastTarget.y - this.y);
+ const fireRate = 1200 / this.difficulty;
+ const accuracy = 0.15 + (this.difficulty * 0.25);
+
+ if (dist < 400 && Math.random() < accuracy) {
+ if (Date.now() - this.lastShot > fireRate) {
+ shootBullet(this);
+ }
+ }
+ }
+ }
+
+ respawn() {
+ const edge = Math.floor(Math.random() * 4);
+ switch(edge) {
+ case 0: this.x = Math.random() * WORLD_WIDTH; this.y = 50; break;
+ case 1: this.x = WORLD_WIDTH - 50; this.y = Math.random() * WORLD_HEIGHT; break;
+ case 2: this.x = Math.random() * WORLD_WIDTH; this.y = WORLD_HEIGHT - 50; break;
+ case 3: this.x = 50; this.y = Math.random() * WORLD_HEIGHT; break;
+ }
+ this.health = MAX_HEALTH;
+ this.shield = true;
+ this.shieldTime = 300;
+ this.isDead = false;
+ this.respawnTime = 0;
+ this.lastKiller = null;
+ }
+
+ toJSON() {
+ return {
+ id: this.id,
+ name: this.name,
+ x: this.x,
+ y: this.y,
+ angle: this.angle,
+ health: this.health,
+ maxHealth: this.maxHealth,
+ score: this.score,
+ kills: this.kills,
+ deaths: this.deaths,
+ isBot: this.isBot,
+ color: this.color,
+ shield: this.shield,
+ rapidFire: this.rapidFire,
+ damageBoost: this.damageBoost,
+ invisible: this.invisible,
+ isDead: this.isDead,
+ respawnTime: this.respawnTime
+ };
+ }
+}
+
+class Bullet {
+ constructor(id, x, y, angle, owner) {
+ this.id = id;
+ this.x = x;
+ this.y = y;
+ this.vx = Math.cos(angle) * BULLET_SPEED;
+ this.vy = Math.sin(angle) * BULLET_SPEED;
+ this.ownerId = owner.id;
+ this.damage = owner.damage || 25;
+ this.life = 120;
+ }
+
+ update() {
+ this.x += this.vx;
+ this.y += this.vy;
+ this.life--;
+
+ const allEntities = [...gameState.players.values(), ...gameState.bots.values()];
+
+ for (let entity of allEntities) {
+ if (entity.id === this.ownerId || entity.isDead) continue;
+
+ const dist = Math.hypot(entity.x - this.x, entity.y - this.y);
+ if (dist < PLAYER_SIZE) {
+ if (entity.takeDamage(this.damage, this.ownerId)) {
+ const owner = gameState.players.get(this.ownerId) || gameState.bots.get(this.ownerId);
+ if (owner) {
+ owner.kills++;
+ owner.score += 100;
+ }
+
+ io.emit('killFeed', {
+ killer: owner ? owner.name : 'Unknown',
+ victim: entity.name,
+ killerColor: owner ? owner.color : '#ffffff',
+ victimColor: entity.color
+ });
+
+ if (entity.isBot) {
+ setTimeout(() => {
+ if (gameState.bots.has(entity.id)) {
+ entity.respawn();
+ }
+ }, 3000);
+ } else {
+ io.to(entity.id).emit('playerDied', {
+ score: entity.score,
+ kills: entity.kills,
+ deaths: entity.deaths,
+ killerName: owner ? owner.name : 'Unknown'
+ });
+ }
+ }
+ return false;
+ }
+ }
+
+ if (this.x < 0 || this.x > WORLD_WIDTH || this.y < 0 || this.y > WORLD_HEIGHT || this.life <= 0) {
+ return false;
+ }
+ return true;
+ }
+
+ toJSON() {
+ return { x: this.x, y: this.y, vx: this.vx, vy: this.vy };
+ }
+}
+
+class PowerUp {
+ constructor(x, y, type) {
+ this.x = x;
+ this.y = y;
+ this.type = type;
+ }
+
+ toJSON() {
+ return { x: this.x, y: this.y, type: this.type };
+ }
+}
+
+function spawnBot() {
+ if (gameState.bots.size >= MAX_BOTS) return;
+ const edge = Math.floor(Math.random() * 4);
+ let x, y;
+ switch(edge) {
+ case 0: x = Math.random() * WORLD_WIDTH; y = 50; break;
+ case 1: x = WORLD_WIDTH - 50; y = Math.random() * WORLD_HEIGHT; break;
+ case 2: x = Math.random() * WORLD_WIDTH; y = WORLD_HEIGHT - 50; break;
+ case 3: x = 50; y = Math.random() * WORLD_HEIGHT; break;
+ }
+
+ const ranks = ['Cpt.', 'Lt.', 'Sgt.', 'Pvt.', 'Col.', 'Maj.'];
+ const adjectives = ['Iron', 'Steel', 'Shadow', 'Ghost', 'Thunder', 'Viper', 'Hawk', 'Wolf', 'Tiger', 'Dragon'];
+ const nouns = ['Tank', 'Hunter', 'Warrior', 'Striker', 'Guardian', 'Phantom', 'Reaper', 'Titan'];
+
+ const rank = ranks[Math.floor(Math.random() * ranks.length)];
+ const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
+ const noun = nouns[Math.floor(Math.random() * nouns.length)];
+ const name = `${rank} ${adj} ${noun}`;
+
+ const botId = 'bot_' + gameState.nextBotId++;
+ const bot = new Player(botId, name, x, y, true);
+ gameState.bots.set(botId, bot);
+}
+
+function removeBot() {
+ if (gameState.bots.size > 0) {
+ const botId = Array.from(gameState.bots.keys())[0];
+ gameState.bots.delete(botId);
+ }
+}
+
+function spawnPowerUp() {
+ const types = ['health', 'shield', 'speed', 'rapid_fire', 'damage_boost', 'regen', 'invisible'];
+ const type = types[Math.floor(Math.random() * types.length)];
+ const x = Math.random() * (WORLD_WIDTH - 100) + 50;
+ const y = Math.random() * (WORLD_HEIGHT - 100) + 50;
+ gameState.powerups.push(new PowerUp(x, y, type));
+}
+
+function shootBullet(player) {
+ player.lastShot = Date.now();
+ const bulletId = gameState.nextBulletId++;
+ const bullet = new Bullet(
+ bulletId,
+ player.x + Math.cos(player.angle) * PLAYER_SIZE,
+ player.y + Math.sin(player.angle) * PLAYER_SIZE,
+ player.angle,
+ player
+ );
+ gameState.bullets.push(bullet);
+}
+
+io.on('connection', (socket) => {
+ console.log('Player connected:', socket.id);
+
+ socket.on('joinGame', (data) => {
+ const playerName = data.name || 'Spieler';
+ removeBot();
+ const player = new Player(socket.id, playerName, WORLD_WIDTH / 2, WORLD_HEIGHT / 2, false);
+ gameState.players.set(socket.id, player);
+
+ socket.emit('initGame', {
+ playerId: socket.id,
+ worldSize: { width: WORLD_WIDTH, height: WORLD_HEIGHT },
+ players: Array.from(gameState.players.values()).map(p => p.toJSON()),
+ bots: Array.from(gameState.bots.values()).map(b => b.toJSON()),
+ bullets: gameState.bullets.map(b => b.toJSON()),
+ powerups: gameState.powerups.map(p => p.toJSON())
+ });
+ });
+
+ socket.on('updatePlayer', (data) => {
+ const player = gameState.players.get(socket.id);
+ if (player && !player.isDead) {
+ player.targetX = data.targetX;
+ player.targetY = data.targetY;
+ player.angle = data.angle;
+ }
+ });
+
+ socket.on('shoot', () => {
+ const player = gameState.players.get(socket.id);
+ if (player && !player.isDead) {
+ const fireRate = player.rapidFire ? player.fireRate : 250;
+ if (Date.now() - player.lastShot > fireRate) {
+ shootBullet(player);
+ }
+ }
+ });
+
+ socket.on('collectPowerUp', (index) => {
+ const player = gameState.players.get(socket.id);
+ const powerup = gameState.powerups[index];
+ if (player && powerup && !player.isDead) {
+ const dist = Math.hypot(player.x - powerup.x, player.y - powerup.y);
+ if (dist < PLAYER_SIZE + 20) {
+ switch(powerup.type) {
+ case 'health':
+ player.health = Math.min(player.maxHealth, player.health + 50);
+ break;
+ case 'shield':
+ player.shield = true;
+ player.shieldTime = 300;
+ break;
+ case 'speed':
+ player.speed = player.baseSpeed * 1.5;
+ setTimeout(() => player.speed = player.baseSpeed, 5000);
+ break;
+ case 'rapid_fire':
+ player.rapidFire = true;
+ player.rapidFireTime = 300;
+ player.fireRate = 150; // 1.66x faster instead of 2.5x
+ break;
+ case 'damage_boost':
+ player.damageBoost = true;
+ player.damageBoostTime = 300;
+ player.damage = player.baseDamage * 1.5; // 1.5x instead of 2x
+ break;
+ case 'regen':
+ let regenTicks = 300;
+ const regenInterval = setInterval(() => {
+ if (player.health < player.maxHealth && !player.isDead) {
+ player.health = Math.min(player.health + 0.25, player.maxHealth); // 75 HP total instead of 120
+ }
+ regenTicks--;
+ if (regenTicks <= 0) clearInterval(regenInterval);
+ }, 1000 / 60);
+ break;
+ case 'invisible':
+ player.invisible = true;
+ player.invisibleTime = 150; // 2.5s instead of 3s
+ break;
+ }
+ gameState.powerups.splice(index, 1);
+ io.emit('powerUpCollected', { index, type: powerup.type });
+ }
+ }
+ });
+
+ socket.on('respawn', () => {
+ const player = gameState.players.get(socket.id);
+ if (player) player.respawn();
+ });
+
+ socket.on('disconnect', () => {
+ const player = gameState.players.get(socket.id);
+ if (player) {
+ console.log(`${player.name} disconnected`);
+ gameState.players.delete(socket.id);
+ setTimeout(() => spawnBot(), 1000);
+ }
+ });
+});
+
+function gameLoop() {
+ gameState.players.forEach(player => player.update());
+ gameState.bots.forEach(bot => bot.update());
+ gameState.bullets = gameState.bullets.filter(bullet => bullet.update());
+
+ if (gameState.powerups.length < 16 && Math.random() < 0.015) {
+ spawnPowerUp();
+ }
+
+ const state = {
+ players: Array.from(gameState.players.values()).map(p => p.toJSON()),
+ bots: Array.from(gameState.bots.values()).map(b => b.toJSON()),
+ bullets: gameState.bullets.map(b => b.toJSON()),
+ powerups: gameState.powerups.map(p => p.toJSON()),
+ averageScore: gameState.averagePlayerScore
+ };
+
+ io.emit('gameState', state);
+}
+
+function initGame() {
+ for (let i = 0; i < MAX_BOTS; i++) spawnBot();
+ for (let i = 0; i < 16; i++) spawnPowerUp();
+ setInterval(gameLoop, 1000 / TICK_RATE);
+ console.log('๐ฎ Tank Warfare Server Started!');
+ console.log(`๐ World: ${WORLD_WIDTH}x${WORLD_HEIGHT}`);
+ console.log(`โก Tick rate: ${TICK_RATE} updates/sec`);
+}
+
+const PORT = process.env.PORT || 3000;
+
+server.listen(PORT, () => {
+ console.log(`๐ Server running on port ${PORT}`);
+ initGame();
+});