about summary refs log tree commit diff stats
path: root/html/tower/js/game.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/tower/js/game.js')
-rw-r--r--html/tower/js/game.js227
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