diff options
Diffstat (limited to 'html')
-rw-r--r-- | html/tower/js/game.js | 101 |
1 files changed, 83 insertions, 18 deletions
diff --git a/html/tower/js/game.js b/html/tower/js/game.js index 57eaa3a..5c39b8b 100644 --- a/html/tower/js/game.js +++ b/html/tower/js/game.js @@ -1,37 +1,70 @@ +/** Canvas elements for rendering the game */ const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); +/** Game timing variables */ let lastTimestamp = 0; -const ENEMY_SPAWN_INTERVAL = 1000; // 1 second +const ENEMY_SPAWN_INTERVAL = 1000; // 1 second between enemy spawns let lastEnemySpawn = 0; let enemiesRemaining = 0; +/** Drag and drop state tracking */ let draggedTowerType = null; let hoverCell = null; +/** + * Main game loop using requestAnimationFrame + * This is the heart of the game, running approximately 60 times per second + * + * @param {number} timestamp - Current time in milliseconds, provided by requestAnimationFrame + * + * Key concepts: + * - RequestAnimationFrame for smooth animation + * - Delta time for consistent motion regardless of frame rate + * - Game state management + */ function gameLoop(timestamp) { + // Calculate time since last frame for consistent motion const deltaTime = timestamp - lastTimestamp; lastTimestamp = timestamp; + // Clear the canvas for the next frame ctx.clearRect(0, 0, canvas.width, canvas.height); + // Update game state based on current phase if (gameState.phase === GamePhase.COMBAT) { handleCombatPhase(timestamp, deltaTime); } + // Render the current game state renderGame(); + // Schedule the next frame requestAnimationFrame(gameLoop); } +/** + * Handles all combat phase updates including enemy movement, attacks, and collisions + * + * @param {number} timestamp - Current game time in milliseconds + * @param {number} deltaTime - Time elapsed since last frame + * + * Key concepts: + * - Game state updates + * - Entity management (enemies, towers, projectiles) + * - Particle effects + * - Combat mechanics + */ function handleCombatPhase(timestamp, deltaTime) { spawnEnemies(timestamp); updateEnemies(); + // Update particle effects with time-based animation gameState.particles = updateParticles(gameState.particles, timestamp, deltaTime); + // Remove expired projectiles gameState.projectiles = gameState.projectiles.filter(p => timestamp - p.createdAt < p.lifetime); const cellSize = canvas.width / 20; - // Process tower and enemy attacks + // Process combat interactions processTowerAttacks( gameState.towers, gameState.enemies, @@ -49,7 +82,8 @@ function handleCombatPhase(timestamp, deltaTime) { cellSize ); - // Remove dead enemies and destroyed towers + // Remove defeated enemies and destroyed towers + // Uses array filter with a callback that has side effects (awarding currency) gameState.enemies = gameState.enemies.filter(enemy => { if (enemy.currentHealth <= 0) { gameState.awardEnemyDestroyed(); @@ -60,6 +94,16 @@ function handleCombatPhase(timestamp, deltaTime) { gameState.towers = gameState.towers.filter(tower => tower.currentHealth > 0); } +/** + * Spawns new enemies at regular intervals during combat + * + * @param {number} timestamp - Current game time in milliseconds + * + * Key concepts: + * - Time-based game events + * - Enemy creation and management + * - Game balance through spawn timing + */ function spawnEnemies(timestamp) { if (enemiesRemaining > 0 && timestamp - lastEnemySpawn > ENEMY_SPAWN_INTERVAL) { gameState.enemies.push(createEnemy({ x: 0, y: gameState.path[0].y })); @@ -68,13 +112,21 @@ function spawnEnemies(timestamp) { } } +/** + * Renders all game elements to the canvas + * + * Key concepts: + * - Layer-based rendering + * - Canvas drawing order (background to foreground) + * - Separation of rendering and game logic + */ function renderGame() { - renderGrid(ctx, gameState.grid); - renderProjectiles(ctx, gameState.projectiles); - renderEnemies(ctx, gameState.enemies); - renderTowers(ctx, gameState.towers); - renderParticles(ctx, gameState.particles); - renderUI(ctx, gameState); + renderGrid(ctx, gameState.grid); // Background grid + renderProjectiles(ctx, gameState.projectiles); // Projectiles + renderEnemies(ctx, gameState.enemies); // Enemies + renderTowers(ctx, gameState.towers); // Towers + renderParticles(ctx, gameState.particles); // Visual effects + renderUI(ctx, gameState); // User interface } // Start the game @@ -99,13 +151,22 @@ function startCombat() { } } -// Add event listeners for tower placement +/** + * Sets up all event listeners for user interaction + * + * Key concepts: + * - Event-driven programming + * - HTML5 Drag and Drop API + * - DOM manipulation + * - Method decoration (towers.push) + */ function initializeEventListeners() { - // Tower palette drag events + // Set up 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 + // Required for Firefox - must set data for drag operation + e.dataTransfer.setData('text/plain', ''); }); option.addEventListener('dragend', () => { @@ -114,13 +175,15 @@ function initializeEventListeners() { }); }); - // Canvas drag events + // Set up canvas drag and drop handling canvas.addEventListener('dragover', (e) => { - e.preventDefault(); + e.preventDefault(); // Required for drop to work const rect = canvas.getBoundingClientRect(); + // Convert mouse coordinates to grid coordinates const x = Math.floor((e.clientX - rect.left) / (canvas.width / 20)); const y = Math.floor((e.clientY - rect.top) / (canvas.height / 20)); + // Validate grid boundaries if (x >= 0 && x < 20 && y >= 0 && y < 20) { hoverCell = { x, y }; } else { @@ -132,11 +195,13 @@ function initializeEventListeners() { hoverCell = null; }); + // Handle tower placement on drop canvas.addEventListener('drop', (e) => { e.preventDefault(); if (!draggedTowerType || !hoverCell) return; const tower = TowerTypes[draggedTowerType]; + // Validate placement and currency if ( gameState.grid[hoverCell.y][hoverCell.x] === 'empty' && gameState.currency >= tower.cost @@ -146,20 +211,21 @@ function initializeEventListeners() { gameState.currency -= tower.cost; } + // Reset drag state draggedTowerType = null; hoverCell = null; }); - // Replace the space key event with button click + // Combat phase transition document.getElementById('startCombat').addEventListener('click', startCombat); - // Update button state when towers are placed + // Dynamic button state management const updateStartButton = () => { const button = document.getElementById('startCombat'); button.disabled = gameState.towers.length === 0; }; - // Add tower placement observer + // Decorator pattern: Enhance towers.push to update UI const originalPush = gameState.towers.push; gameState.towers.push = function(...args) { const result = originalPush.apply(this, args); @@ -167,6 +233,5 @@ function initializeEventListeners() { return result; }; - // Initial button state updateStartButton(); } \ No newline at end of file |