diff options
Diffstat (limited to 'html/tower/js/game.js')
-rw-r--r-- | html/tower/js/game.js | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/html/tower/js/game.js b/html/tower/js/game.js new file mode 100644 index 0000000..e36c291 --- /dev/null +++ b/html/tower/js/game.js @@ -0,0 +1,227 @@ +const canvas = document.getElementById('gameCanvas'); +const ctx = canvas.getContext('2d'); + +// Initialize game state +let gameState = { + grid: Array(20).fill().map(() => Array(20).fill('empty')), + path: [], + towers: [], + enemies: [], + playerCurrency: 100, + phase: 'placement', + isGameOver: false, + particles: [], + projectiles: [] +}; + +let lastTimestamp = 0; +const ENEMY_SPAWN_INTERVAL = 1000; // 1 second +let lastEnemySpawn = 0; +let enemiesRemaining = 0; + +let draggedTowerType = null; +let hoverCell = null; + +function gameLoop(timestamp) { + const deltaTime = timestamp - lastTimestamp; + lastTimestamp = timestamp; + + // Clear canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + if (gameState.phase === GamePhase.COMBAT) { + // Spawn enemies + if (enemiesRemaining > 0 && timestamp - lastEnemySpawn > ENEMY_SPAWN_INTERVAL) { + gameState.enemies.push(createEnemy({ x: 0, y: gameState.path[0].y })); + lastEnemySpawn = timestamp; + enemiesRemaining--; + } + + // Update enemy positions + gameState.enemies.forEach(enemy => { + if (enemy.pathIndex < gameState.path.length - 1) { + const targetPos = gameState.path[enemy.pathIndex + 1]; + const dx = targetPos.x - enemy.position.x; + const dy = targetPos.y - enemy.position.y; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < enemy.speed * deltaTime / 1000) { + enemy.position = { ...targetPos }; + enemy.pathIndex++; + } else { + enemy.position.x += (dx / distance) * enemy.speed * deltaTime / 1000; + enemy.position.y += (dy / distance) * enemy.speed * deltaTime / 1000; + } + } + }); + + // Update particles + gameState.particles = gameState.particles.filter(particle => { + const age = timestamp - particle.createdAt; + if (age > particle.lifetime) return false; + + particle.position.x += particle.velocity.x * deltaTime; + particle.position.y += particle.velocity.y * deltaTime; + return true; + }); + + // Update projectiles + gameState.projectiles = gameState.projectiles.filter(projectile => { + return timestamp - projectile.createdAt < projectile.lifetime; + }); + + // Process tower attacks + gameState.towers.forEach(tower => { + if (timestamp - tower.lastAttackTime > 1000 / tower.attackSpeed) { + const enemiesInRange = gameState.enemies.filter(enemy => { + const dx = enemy.position.x - tower.position.x; + const dy = enemy.position.y - tower.position.y; + return Math.sqrt(dx * dx + dy * dy) <= tower.range; + }); + + if (enemiesInRange.length > 0) { + const target = enemiesInRange[0]; + + // Create projectile + gameState.projectiles.push({ + startPos: tower.position, + targetPos: target.position, + createdAt: timestamp, + lifetime: 300 // 300ms travel time + }); + + // Apply damage + target.currentHealth -= tower.damage; + tower.lastAttackTime = timestamp; + + // Create death particles if enemy dies + if (target.currentHealth <= 0) { + const cellSize = canvas.width / 20; + const centerX = (target.position.x + 0.5) * cellSize; + const centerY = (target.position.y + 0.5) * cellSize; + + // Create explosion particles + for (let i = 0; i < 12; i++) { + const angle = (Math.PI * 2 * i) / 12; + gameState.particles.push( + createParticle( + ParticleTypes.DEATH_PARTICLE, + { x: centerX, y: centerY }, + angle + ) + ); + } + } + } + } + }); + + // Remove dead enemies + gameState.enemies = gameState.enemies.filter(enemy => enemy.currentHealth > 0); + } + + // Render game state + renderGrid(ctx, gameState.grid); + renderProjectiles(ctx, gameState.projectiles); + renderEnemies(ctx, gameState.enemies); + renderTowers(ctx, gameState.towers); + renderParticles(ctx, gameState.particles); + renderUI(ctx, gameState); + + // Request next frame + requestAnimationFrame(gameLoop); +} + +// Start the game +generatePath(gameState.grid).then(path => { + gameState.path = path; + enemiesRemaining = Math.floor(Math.random() * 26) + 5; // 5-30 enemies + initializeEventListeners(); + requestAnimationFrame(gameLoop); +}); + +function startCombat() { + if (gameState.phase === GamePhase.PLACEMENT && gameState.towers.length > 0) { + gameState.phase = GamePhase.COMBAT; + document.getElementById('startCombat').disabled = true; + + // Disable tower palette + document.querySelectorAll('.tower-option').forEach(option => { + option.draggable = false; + option.style.cursor = 'not-allowed'; + option.style.opacity = '0.5'; + }); + } +} + +// Add event listeners for tower placement +function initializeEventListeners() { + // Tower palette drag events + document.querySelectorAll('.tower-option').forEach(option => { + option.addEventListener('dragstart', (e) => { + draggedTowerType = e.target.dataset.towerType; + e.dataTransfer.setData('text/plain', ''); // Required for Firefox + }); + + option.addEventListener('dragend', () => { + draggedTowerType = null; + hoverCell = null; + }); + }); + + // Canvas drag events + canvas.addEventListener('dragover', (e) => { + e.preventDefault(); + const rect = canvas.getBoundingClientRect(); + const x = Math.floor((e.clientX - rect.left) / (canvas.width / 20)); + const y = Math.floor((e.clientY - rect.top) / (canvas.height / 20)); + + if (x >= 0 && x < 20 && y >= 0 && y < 20) { + hoverCell = { x, y }; + } else { + hoverCell = null; + } + }); + + canvas.addEventListener('dragleave', () => { + hoverCell = null; + }); + + canvas.addEventListener('drop', (e) => { + e.preventDefault(); + if (!draggedTowerType || !hoverCell) return; + + const tower = TowerTypes[draggedTowerType]; + if ( + gameState.grid[hoverCell.y][hoverCell.x] === 'empty' && + gameState.playerCurrency >= tower.cost + ) { + gameState.grid[hoverCell.y][hoverCell.x] = 'tower'; + gameState.towers.push(createTower(draggedTowerType, { ...hoverCell })); + gameState.playerCurrency -= tower.cost; + } + + draggedTowerType = null; + hoverCell = null; + }); + + // Replace the space key event with button click + document.getElementById('startCombat').addEventListener('click', startCombat); + + // Update button state when towers are placed + const updateStartButton = () => { + const button = document.getElementById('startCombat'); + button.disabled = gameState.towers.length === 0; + }; + + // Add tower placement observer + const originalPush = gameState.towers.push; + gameState.towers.push = function(...args) { + const result = originalPush.apply(this, args); + updateStartButton(); + return result; + }; + + // Initial button state + updateStartButton(); +} \ No newline at end of file |