tanktopia-blitz / script.js
karmikovic's picture
i can't see any player or anything, i only see the how to play box
339896e verified
// Help menu toggle
document.getElementById('closeHelp').addEventListener('click', () => {
document.getElementById('helpMenu').classList.add('hidden');
});
// Game constants
const CANVAS_WIDTH = 2000;
const CANVAS_HEIGHT = 2000;
const PLAYER_SIZE = 30;
const BULLET_SIZE = 8;
const FOOD_SIZE = 10;
const MAX_PLAYERS = 50;
const MAX_FOOD = 500;
const MAX_AI = 20;
// Game state
let gameState = {
players: {},
bullets: [],
food: [],
aiTanks: [],
playerId: null,
score: 0,
level: 1,
upgrades: {
health: 0,
damage: 0,
reload: 0,
movement: 0,
bulletSpeed: 0,
penetration: 0
},
upgradePoints: 0
};
// Canvas setup
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Viewport tracking
let viewport = {
x: 0,
y: 0,
width: canvas.width,
height: canvas.height,
target: { x: 0, y: 0 }
};
// Game loop
function gameLoop() {
update();
render();
requestAnimationFrame(gameLoop);
}
function update() {
// Update viewport to follow player
if (gameState.playerId && gameState.players[gameState.playerId]) {
const player = gameState.players[gameState.playerId];
viewport.target.x = player.x - viewport.width / 2;
viewport.target.y = player.y - viewport.height / 2;
// Smooth viewport movement
viewport.x += (viewport.target.x - viewport.x) * 0.1;
viewport.y += (viewport.target.y - viewport.y) * 0.1;
}
}
function render() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw grid background
drawGrid();
// Draw all game objects relative to viewport
drawFood();
drawBullets();
drawPlayers();
drawAI();
// Draw UI
drawUI();
}
function drawGrid() {
const gridSize = 100;
const offsetX = -viewport.x % gridSize;
const offsetY = -viewport.y % gridSize;
// Draw a subtle grid with slightly more visible lines
ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
ctx.lineWidth = 1.5;
// Vertical lines
for (let x = offsetX; x < canvas.width; x += gridSize) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
// Horizontal lines
for (let y = offsetY; y < canvas.height; y += gridSize) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
}
function drawPlayers() {
for (const id in gameState.players) {
const player = gameState.players[id];
const isCurrentPlayer = id === gameState.playerId;
// Calculate screen position
const screenX = player.x - viewport.x;
const screenY = player.y - viewport.y;
// Draw player tank
ctx.save();
ctx.translate(screenX, screenY);
ctx.rotate(player.angle);
// Tank body
ctx.fillStyle = isCurrentPlayer ? '#4F46E5' : player.color;
ctx.beginPath();
ctx.rect(-PLAYER_SIZE/2, -PLAYER_SIZE/2, PLAYER_SIZE, PLAYER_SIZE);
ctx.fill();
// Tank barrel
ctx.fillStyle = '#D1D5DB';
ctx.beginPath();
ctx.rect(PLAYER_SIZE/2 - 5, -3, 20, 6);
ctx.fill();
ctx.restore();
// Draw player name and health
if (isCurrentPlayer || Math.random() < 0.1) { // Only draw names occasionally for performance
ctx.fillStyle = '#FFFFFF';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.fillText(player.name, screenX, screenY - PLAYER_SIZE - 5);
// Health bar
const healthWidth = 30;
const healthPercent = player.health / player.maxHealth;
ctx.fillStyle = '#FF0000';
ctx.fillRect(screenX - healthWidth/2, screenY - PLAYER_SIZE - 15, healthWidth, 3);
ctx.fillStyle = '#00FF00';
ctx.fillRect(screenX - healthWidth/2, screenY - PLAYER_SIZE - 15, healthWidth * healthPercent, 3);
}
}
}
function drawBullets() {
for (const bullet of gameState.bullets) {
const screenX = bullet.x - viewport.x;
const screenY = bullet.y - viewport.y;
ctx.fillStyle = bullet.color;
ctx.beginPath();
ctx.arc(screenX, screenY, BULLET_SIZE, 0, Math.PI * 2);
ctx.fill();
}
}
function drawFood() {
ctx.shadowBlur = 5;
for (const food of gameState.food) {
const screenX = food.x - viewport.x;
const screenY = food.y - viewport.y;
// Only draw food that's visible in viewport
if (screenX > -50 && screenX < canvas.width + 50 &&
screenY > -50 && screenY < canvas.height + 50) {
ctx.shadowColor = food.color;
ctx.fillStyle = food.color;
ctx.beginPath();
ctx.arc(screenX, screenY, food.size, 0, Math.PI * 2);
ctx.fill();
}
}
ctx.shadowBlur = 0;
}
function drawAI() {
// Similar to drawPlayers but with different styling
}
function drawUI() {
// Draw current player stats
if (gameState.playerId && gameState.players[gameState.playerId]) {
const player = gameState.players[gameState.playerId];
// Mini stats in top left with better visibility
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
ctx.fillRect(10, 10, 180, 100);
ctx.strokeStyle = '#4F46E5';
ctx.lineWidth = 2;
ctx.strokeRect(10, 10, 180, 100);
ctx.fillStyle = '#FFFFFF';
ctx.font = 'bold 16px Arial';
ctx.textAlign = 'left';
ctx.fillText(`Player: ${player.name}`, 20, 30);
ctx.fillText(`Score: ${gameState.score}`, 20, 55);
ctx.fillText(`Level: ${gameState.level}`, 20, 80);
ctx.fillText(`Upgrades: ${gameState.upgradePoints}`, 20, 105);
// Show help prompt if help menu is hidden
if (document.getElementById('helpMenu').classList.contains('hidden')) {
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)';
ctx.font = '14px Arial';
ctx.textAlign = 'center';
ctx.fillText('Press H for help', canvas.width/2, 30);
}
}
}
// Initialize game
function initGame() {
// Generate initial food with brighter colors
for (let i = 0; i < MAX_FOOD; i++) {
gameState.food.push({
x: Math.random() * CANVAS_WIDTH,
y: Math.random() * CANVAS_HEIGHT,
color: `hsl(${Math.random() * 360}, 90%, 60%)`, // More saturated colors
value: 1,
size: FOOD_SIZE
});
}
// Generate AI tanks with distinct colors and positions
for (let i = 0; i < MAX_AI; i++) {
const aiId = 'ai_' + i;
gameState.players[aiId] = {
x: Math.random() * CANVAS_WIDTH,
y: Math.random() * CANVAS_HEIGHT,
angle: Math.random() * Math.PI * 2,
health: 100,
maxHealth: 100,
name: 'AI-' + (i+1),
color: `hsl(${i * 36}, 90%, 50%)`, // Distinct colors for each AI
score: Math.floor(Math.random() * 500)
};
}
// Start game loop
gameLoop();
// Connect to server (simulated)
// Create player immediately
gameState.playerId = 'player_' + Math.random().toString(36).substr(2, 9);
gameState.players[gameState.playerId] = {
x: CANVAS_WIDTH / 2,
y: CANVAS_HEIGHT / 2,
angle: 0,
health: 100,
maxHealth: 100,
name: 'Player' + Math.floor(Math.random() * 1000),
color: '#4F46E5', // Consistent blue color for player
score: 0
};
// Show upgrade menu when player reaches level 2
if (gameState.level >= 2) {
document.getElementById('upgradeMenu').classList.remove('hidden');
}
}, 1000);
}
// Event listeners
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
viewport.width = canvas.width;
viewport.height = canvas.height;
});
// Keyboard controls
const keys = {};
const movementKeys = ['w', 'a', 's', 'd', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
window.addEventListener('keydown', (e) => {
keys[e.key] = true;
// Space to shoot
if (e.key === ' ' && gameState.playerId) {
shootBullet();
}
// H to toggle help
if (e.key.toLowerCase() === 'h') {
const helpMenu = document.getElementById('helpMenu');
if (helpMenu.classList.contains('hidden')) {
helpMenu.classList.remove('hidden');
} else {
helpMenu.classList.add('hidden');
}
}
// Movement handling
if (gameState.playerId && movementKeys.includes(e.key.toLowerCase())) {
handleMovement();
}
});
function handleMovement() {
if (!gameState.playerId) return;
const player = gameState.players[gameState.playerId];
const moveSpeed = 5 + gameState.upgrades.movement * 0.5;
if (keys['w'] || keys['ArrowUp']) player.y -= moveSpeed;
if (keys['s'] || keys['ArrowDown']) player.y += moveSpeed;
if (keys['a'] || keys['ArrowLeft']) player.x -= moveSpeed;
if (keys['d'] || keys['ArrowRight']) player.x += moveSpeed;
// Boundary checks
player.x = Math.max(PLAYER_SIZE/2, Math.min(CANVAS_WIDTH - PLAYER_SIZE/2, player.x));
player.y = Math.max(PLAYER_SIZE/2, Math.min(CANVAS_HEIGHT - PLAYER_SIZE/2, player.y));
}
window.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
// Game loop update to handle continuous movement
function update() {
// Update viewport to follow player
if (gameState.playerId && gameState.players[gameState.playerId]) {
handleMovement();
const player = gameState.players[gameState.playerId];
viewport.target.x = player.x - viewport.width / 2;
viewport.target.y = player.y - viewport.height / 2;
// Smooth viewport movement
viewport.x += (viewport.target.x - viewport.x) * 0.1;
viewport.y += (viewport.target.y - viewport.y) * 0.1;
}
}
function shootBullet() {
if (!gameState.playerId) return;
const player = gameState.players[gameState.playerId];
const bulletSpeed = 10 + gameState.upgrades.bulletSpeed * 2;
gameState.bullets.push({
x: player.x + Math.cos(player.angle) * (PLAYER_SIZE/2 + 20),
y: player.y + Math.sin(player.angle) * (PLAYER_SIZE/2 + 20),
dx: Math.cos(player.angle) * bulletSpeed,
dy: Math.sin(player.angle) * bulletSpeed,
color: player.color,
damage: 10 + gameState.upgrades.damage * 2,
owner: gameState.playerId,
penetration: 1 + gameState.upgrades.penetration
});
}
// Mouse controls
canvas.addEventListener('mousemove', (e) => {
if (!gameState.playerId) return;
const player = gameState.players[gameState.playerId];
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left + viewport.x;
const mouseY = e.clientY - rect.top + viewport.y;
player.angle = Math.atan2(mouseY - player.y, mouseX - player.x);
});
canvas.addEventListener('click', (e) => {
if (gameState.playerId) {
shootBullet();
}
});
// Upgrade buttons
document.querySelectorAll('.upgrade-btn').forEach(btn => {
btn.addEventListener('click', () => {
const stat = btn.dataset.stat;
if (gameState.upgradePoints > 0) {
gameState.upgrades[stat]++;
gameState.upgradePoints--;
updatePlayerStats();
}
});
});
function updatePlayerStats() {
if (!gameState.playerId) return;
const player = gameState.players[gameState.playerId];
player.maxHealth = 100 + gameState.upgrades.health * 20;
// Apply other stat upgrades...
}
// Show initial help for 5 seconds then auto-hide
setTimeout(() => {
document.getElementById('helpMenu').classList.add('hidden');
}, 5000);
// Start the game
initGame();
// Simulate multiplayer updates
setInterval(() => {
if (!gameState.playerId) return;
// Simulate other players moving and shooting
for (const id in gameState.players) {
if (id !== gameState.playerId) {
const player = gameState.players[id];
const targetPlayer = gameState.players[gameState.playerId];
if (!targetPlayer) continue;
// Move toward player with some randomness
const angleToPlayer = Math.atan2(
targetPlayer.y - player.y,
targetPlayer.x - player.x
);
const moveSpeed = 2;
player.x += Math.cos(angleToPlayer) * moveSpeed + (Math.random() - 0.5);
player.y += Math.sin(angleToPlayer) * moveSpeed + (Math.random() - 0.5);
player.angle = angleToPlayer;
// Shoot more frequently when close
const distance = Math.sqrt(
Math.pow(targetPlayer.x - player.x, 2) +
Math.pow(targetPlayer.y - player.y, 2)
);
if (distance < 300 && Math.random() < 0.05) {
shootBullet(id);
}
}
}
// Update bullets
for (let i = gameState.bullets.length - 1; i >= 0; i--) {
const bullet = gameState.bullets[i];
bullet.x += bullet.dx;
bullet.y += bullet.dy;
// Remove bullets that go out of bounds
if (bullet.x < 0 || bullet.x > CANVAS_WIDTH ||
bullet.y < 0 || bullet.y > CANVAS_HEIGHT) {
gameState.bullets.splice(i, 1);
continue;
}
// Check for collisions with players
for (const id in gameState.players) {
const player = gameState.players[id];
if (id !== bullet.owner) {
const dx = player.x - bullet.x;
const dy = player.y - bullet.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < PLAYER_SIZE/2 + BULLET_SIZE) {
player.health -= bullet.damage;
bullet.penetration--;
if (bullet.penetration <= 0) {
gameState.bullets.splice(i, 1);
}
if (player.health <= 0) {
// Player died
if (bullet.owner === gameState.playerId) {
gameState.score += 100;
checkLevelUp();
}
delete gameState.players[id];
}
break;
}
}
}
}
// Check for food collection
if (gameState.playerId) {
const player = gameState.players[gameState.playerId];
for (let i = gameState.food.length - 1; i >= 0; i--) {
const food = gameState.food[i];
const dx = player.x - food.x;
const dy = player.y - food.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < PLAYER_SIZE/2 + FOOD_SIZE) {
gameState.score += food.value;
gameState.food.splice(i, 1);
checkLevelUp();
// Add new food to maintain count
gameState.food.push({
x: Math.random() * CANVAS_WIDTH,
y: Math.random() * CANVAS_HEIGHT,
color: `hsl(${Math.random() * 360}, 70%, 60%)`,
value: 1
});
}
}
}
// Update leaderboard
updateLeaderboard();
}, 1000 / 60);
function checkLevelUp() {
const needed = gameState.level * 1000;
if (gameState.score >= needed) {
gameState.level++;
gameState.upgradePoints++;
if (gameState.level >= 2) {
document.getElementById('upgradeMenu').classList.remove('hidden');
}
}
}
function updateLeaderboard() {
const leaderboardList = document.getElementById('leaderboardList');
leaderboardList.innerHTML = '';
// Sort players by score
const players = Object.values(gameState.players);
players.sort((a, b) => (b.score || 0) - (a.score || 0));
// Display top 10
players.slice(0, 10).forEach(player => {
const li = document.createElement('li');
li.className = 'flex justify-between';
li.innerHTML = `
<span class="player-name">${player.name}</span>
<span class="player-score">${player.score || 0}</span>
`;
leaderboardList.appendChild(li);
});
}
// Simulated shoot function for other players
function shootBullet(playerId) {
const player = gameState.players[playerId];
if (!player) return;
const isPlayer = playerId === gameState.playerId;
const baseSpeed = isPlayer ? 10 + gameState.upgrades.bulletSpeed * 2 : 8;
const baseDamage = isPlayer ? 10 + gameState.upgrades.damage * 2 : 8;
gameState.bullets.push({
x: player.x + Math.cos(player.angle) * (PLAYER_SIZE/2 + 20),
y: player.y + Math.sin(player.angle) * (PLAYER_SIZE/2 + 20),
dx: Math.cos(player.angle) * baseSpeed,
dy: Math.sin(player.angle) * baseSpeed,
color: player.color,
damage: baseDamage,
owner: playerId,
penetration: isPlayer ? 1 + gameState.upgrades.penetration : 1
});
}
// Initialize with more visible AI tanks
function initGame() {
// Generate initial food
for (let i = 0; i < MAX_FOOD; i++) {
gameState.food.push({
x: Math.random() * CANVAS_WIDTH,
y: Math.random() * CANVAS_HEIGHT,
color: '#00FF00',
value: 1
});
}
// Generate AI tanks with distinct colors
for (let i = 0; i < MAX_AI; i++) {
const aiId = 'ai_' + i;
gameState.players[aiId] = {
x: Math.random() * CANVAS_WIDTH,
y: Math.random() * CANVAS_HEIGHT,
angle: Math.random() * Math.PI * 2,
health: 100,
maxHealth: 100,
name: 'AI-' + i,
color: `hsl(${i * 36}, 80%, 50%)`,
score: Math.floor(Math.random() * 500)
};
}