about summary refs log tree commit diff stats
path: root/html/tower/docs/game.js.html
diff options
context:
space:
mode:
Diffstat (limited to 'html/tower/docs/game.js.html')
-rw-r--r--html/tower/docs/game.js.html537
1 files changed, 537 insertions, 0 deletions
diff --git a/html/tower/docs/game.js.html b/html/tower/docs/game.js.html
new file mode 100644
index 0000000..b34bcf8
--- /dev/null
+++ b/html/tower/docs/game.js.html
@@ -0,0 +1,537 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>JSDoc: Source: game.js</title>
+
+    <script src="scripts/prettify/prettify.js"> </script>
+    <script src="scripts/prettify/lang-css.js"> </script>
+    <!--[if lt IE 9]>
+      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
+    <![endif]-->
+    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
+    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
+</head>
+
+<body>
+
+<div id="main">
+
+    <h1 class="page-title">Source: game.js</h1>
+
+    
+
+
+
+    
+    <section>
+        <article>
+            <pre class="prettyprint source linenums"><code>// generate updated docs
+// jsdoc js -d docs
+
+/**
+ * Main game entry point
+ * Initializes the game state and starts the game loop
+ * 
+ * @module game
+ */
+
+/** 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 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) {
+    const deltaTime = timestamp - lastTimestamp;
+    lastTimestamp = timestamp;
+    
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    
+    if (!gameState.isGameOver) {
+        if (gameState.phase === GamePhase.COMBAT) {
+            handleCombatPhase(timestamp, deltaTime);
+            
+            // Check for level completion
+            if (gameState.checkLevelComplete()) {
+                handleLevelComplete();
+            }
+        }
+    }
+    
+    renderGame();
+    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 &lt; p.lifetime);
+    
+    const cellSize = canvas.width / 20;
+    
+    // Process combat interactions
+    processTowerAttacks(
+        gameState.towers,
+        gameState.enemies,
+        gameState.projectiles,
+        gameState.particles,
+        timestamp,
+        cellSize
+    );
+    
+    processEnemyAttacks(
+        gameState.enemies,
+        gameState.towers,
+        gameState.particles,
+        timestamp,
+        cellSize
+    );
+    
+    // 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 &lt;= 0) {
+            gameState.awardEnemyDestroyed();
+            return false;
+        }
+        return true;
+    });
+    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 &amp;&amp; timestamp - lastEnemySpawn > ENEMY_SPAWN_INTERVAL) {
+        gameState.enemies.push(createEnemy({ x: 0, y: gameState.path[0].y }));
+        lastEnemySpawn = timestamp;
+        enemiesRemaining--;
+    }
+}
+
+/**
+ * Renders all game elements to the canvas using a layered approach.
+ * This function demonstrates several key game development patterns:
+ * 
+ * 1. Canvas State Management:
+ *    - Uses save()/restore() to isolate rendering contexts
+ *    - Resets transform matrix to prevent state leaks
+ *    - Maintains clean state between rendering phases
+ * 
+ * 2. Layered Rendering Pattern:
+ *    - Renders in specific order (background → entities → UI)
+ *    - Each layer builds on top of previous layers
+ *    - Separates rendering concerns for easier maintenance
+ * 
+ * 3. Separation of Concerns:
+ *    - Each render function handles one specific type of game element
+ *    - UI rendering is isolated from game element rendering
+ *    - Clear boundaries between different rendering responsibilities
+ * 
+ * The rendering order is important:
+ * 1. Grid (background)
+ * 2. Particles (effects under entities)
+ * 3. Projectiles (dynamic game elements)
+ * 4. Towers (static game entities)
+ * 5. Enemies (moving game entities)
+ * 6. UI (top layer)
+ */
+function renderGame() {
+    // Reset the canvas transform matrix to identity
+    // This prevents any previous transformations from affecting new renders
+    ctx.setTransform(1, 0, 0, 1, 0, 0);
+    
+    // Clear the entire canvas to prevent ghosting
+    // This is crucial for animation smoothness
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    
+    // Save the initial clean state
+    // This is part of the state stack pattern used in canvas rendering
+    ctx.save();
+    
+    // Render game world elements in specific order
+    // This creates the layered effect common in 2D games
+    renderGrid(ctx, gameState.grid);          // Background layer
+    renderParticles(ctx, gameState.particles); // Effect layer
+    renderProjectiles(ctx, gameState.projectiles); // Dynamic elements
+    renderTowers(ctx, gameState.towers);       // Static entities
+    renderEnemies(ctx, gameState.enemies);     // Moving entities
+    
+    // Restore to clean state before UI rendering
+    // This ensures UI rendering isn't affected by game world rendering
+    ctx.restore();
+    ctx.save();
+    
+    // Render UI elements last so they appear on top
+    // UI is rendered with its own clean state to prevent interference
+    renderUI(ctx, gameState);
+    
+    // Final state restoration
+    // Ensures clean state for next frame
+    ctx.restore();
+}
+
+/**
+ * Initializes the game by:
+ * 1. Generating the path for enemies to follow
+ * 2. Setting up initial enemy count
+ * 3. Binding event listeners
+ * 4. Starting the game loop
+ * 
+ * Uses Promise-based path generation to handle async initialization
+ */
+generatePath(gameState.grid).then(path => {
+    gameState.path = path;
+    // Random enemy count between 5-30 for variety
+    enemiesRemaining = Math.floor(Math.random() * 26) + 5;
+    initializeEventListeners();
+    // Start the game loop using requestAnimationFrame for smooth animation
+    requestAnimationFrame(gameLoop);
+});
+
+/**
+ * Transitions the game from placement to combat phase.
+ * Demonstrates state machine pattern commonly used in games.
+ * 
+ * Side effects:
+ * - Updates game phase
+ * - Disables UI elements
+ * - Updates visual feedback
+ */
+function startCombat() {
+    if (gameState.phase === GamePhase.PLACEMENT &amp;&amp; gameState.towers.length > 0) {
+        // State transition
+        gameState.phase = GamePhase.COMBAT;
+        
+        // UI updates
+        document.getElementById('startCombat').disabled = true;
+        
+        // Visual feedback for disabled state
+        document.querySelectorAll('.tower-option').forEach(option => {
+            option.draggable = false;
+            option.style.cursor = 'not-allowed';
+            option.style.opacity = '0.5';
+        });
+    }
+}
+
+/**
+ * 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() {
+    // Add this at the beginning of the function
+    populateTowerPalette();
+    
+    // Set up tower palette drag events
+    document.querySelectorAll('.tower-option').forEach(option => {
+        option.addEventListener('dragstart', (e) => {
+            draggedTowerType = e.target.dataset.towerType;
+            // Required for Firefox - must set data for drag operation
+            e.dataTransfer.setData('text/plain', '');
+        });
+        
+        option.addEventListener('dragend', () => {
+            draggedTowerType = null;
+            hoverCell = null;
+        });
+    });
+
+    // Set up canvas drag and drop handling
+    canvas.addEventListener('dragover', (e) => {
+        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 &amp;&amp; x &lt; 20 &amp;&amp; y >= 0 &amp;&amp; y &lt; 20) {
+            hoverCell = { x, y };
+        } else {
+            hoverCell = null;
+        }
+    });
+
+    canvas.addEventListener('dragleave', () => {
+        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' &amp;&amp;
+            gameState.currency >= tower.cost
+        ) {
+            gameState.grid[hoverCell.y][hoverCell.x] = 'tower';
+            gameState.towers.push(createTower(draggedTowerType, { ...hoverCell }));
+            gameState.currency -= tower.cost;
+        }
+        
+        // Reset drag state
+        draggedTowerType = null;
+        hoverCell = null;
+    });
+
+    // Combat phase transition
+    document.getElementById('startCombat').addEventListener('click', startCombat);
+    
+    // Dynamic button state management
+    const updateStartButton = () => {
+        const button = document.getElementById('startCombat');
+        button.disabled = gameState.towers.length === 0;
+    };
+    
+    // Decorator pattern: Enhance towers.push to update UI
+    const originalPush = gameState.towers.push;
+    gameState.towers.push = function(...args) {
+        const result = originalPush.apply(this, args);
+        updateStartButton();
+        return result;
+    };
+    
+    updateStartButton();
+}
+
+/**
+ * Handles the transition between levels
+ * Shows completion message and sets up next level
+ */
+function handleLevelComplete() {
+    // Pause the game briefly
+    gameState.phase = GamePhase.TRANSITION;
+    
+    // Calculate ammo bonus
+    let ammoBonus = 0;
+    gameState.towers.forEach(tower => {
+        ammoBonus += tower.ammo * 0.25;
+    });
+    ammoBonus = Math.floor(ammoBonus);
+    
+    // Show level complete message with modal
+    const message = `
+        Level ${gameState.level} Complete!
+        
+        Stats:
+        - Enemies Destroyed: ${gameState.enemiesDestroyed}
+        - Enemies Escaped: ${gameState.enemiesEscaped}
+        
+        Bonuses:
+        - Current Money: $${gameState.currency}
+        - Remaining Ammo Bonus: +$${ammoBonus}
+        
+        Total After Bonuses: $${gameState.currency + ammoBonus + 10}
+        
+        Ready for Level ${gameState.level + 1}?
+    `;
+    
+    // Use setTimeout to allow the final frame to render
+    setTimeout(() => {
+        if (confirm(message)) {
+            startNextLevel();
+        }
+    }, 100);
+}
+
+/**
+ * Sets up the next level
+ * Increases difficulty and resets the game state while preserving currency
+ */
+function startNextLevel() {
+    gameState.advanceToNextLevel();
+    
+    // Generate new path
+    generatePath(gameState.grid).then(path => {
+        gameState.path = path;
+        
+        // Exponential enemy scaling
+        const baseEnemies = 5;
+        const scalingFactor = 1.5;  // Each level increases by 50%
+        enemiesRemaining = Math.floor(baseEnemies * Math.pow(scalingFactor, gameState.level - 1));
+        
+        // Re-enable tower palette
+        document.querySelectorAll('.tower-option').forEach(option => {
+            option.draggable = true;
+            option.style.cursor = 'grab';
+            option.style.opacity = '1';
+        });
+        
+        // Reset start button
+        const startButton = document.getElementById('startCombat');
+        startButton.disabled = false;
+        startButton.textContent = `Start Level ${gameState.level}`;
+    });
+}
+
+// Update the renderUI function to show current level
+function renderUI(ctx, gameState) {
+    ctx.fillStyle = 'black';
+    ctx.font = '20px Arial';
+    ctx.fillText(`Level: ${gameState.level}`, 10, 30);
+    ctx.fillText(`Currency: $${gameState.currency}`, 10, 60);
+    ctx.fillText(`Phase: ${gameState.phase}`, 10, 90);
+    ctx.fillText(`Destroyed: ${gameState.enemiesDestroyed}`, 10, 120);
+    ctx.fillText(`Escaped: ${gameState.enemiesEscaped}`, 10, 150);
+}
+
+/**
+ * Dynamically populates the tower palette based on TowerTypes
+ */
+function populateTowerPalette() {
+    const palette = document.querySelector('.tower-palette');
+    // Clear existing tower options
+    palette.innerHTML = '';
+    
+    // Create tower options dynamically
+    Object.entries(TowerTypes).forEach(([type, tower]) => {
+        const towerOption = document.createElement('div');
+        towerOption.className = 'tower-option';
+        towerOption.draggable = true;
+        towerOption.dataset.towerType = type;
+        
+        towerOption.innerHTML = `
+            &lt;div class="tower-preview" style="background: ${tower.color};">&lt;/div>
+            &lt;div class="tower-info">
+                &lt;div class="tower-name">${tower.name}&lt;/div>
+                &lt;div class="tower-cost">Cost: $${tower.cost}&lt;/div>
+                &lt;div class="tower-ammo">Ammo: ${tower.maxAmmo}&lt;/div>
+            &lt;/div>
+        `;
+        
+        palette.appendChild(towerOption);
+    });
+    
+    // Add start combat button
+    const startButton = document.createElement('button');
+    startButton.id = 'startCombat';
+    startButton.className = 'start-button';
+    startButton.textContent = 'Start Run';
+    palette.appendChild(startButton);
+}
+
+/**
+ * Handles game over state and prompts for restart
+ */
+function handleGameOver() {
+    gameState.phase = GamePhase.TRANSITION;
+    gameState.isGameOver = true;
+    
+    const message = `
+        Game Over!
+        
+        Final Stats:
+        Level Reached: ${gameState.level}
+        Enemies Destroyed: ${gameState.enemiesDestroyed}
+        Enemies Escaped: ${gameState.enemiesEscaped}
+        
+        Would you like to restart from Level 1?
+    `;
+    
+    setTimeout(() => {
+        if (confirm(message)) {
+            restartGame();
+        }
+    }, 100);
+}
+
+/**
+ * Restarts the game from level 1 with fresh state
+ */
+function restartGame() {
+    gameState.resetGame();
+    
+    // Generate new path
+    generatePath(gameState.grid).then(path => {
+        gameState.path = path;
+        
+        // Reset enemy count to level 1
+        enemiesRemaining = 5;
+        
+        // Re-enable tower palette
+        document.querySelectorAll('.tower-option').forEach(option => {
+            option.draggable = true;
+            option.style.cursor = 'grab';
+            option.style.opacity = '1';
+        });
+        
+        // Reset start button
+        const startButton = document.getElementById('startCombat');
+        startButton.disabled = false;
+        startButton.textContent = 'Start Level 1';
+    });
+} </code></pre>
+        </article>
+    </section>
+
+
+
+
+</div>
+
+<nav>
+    <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-game.html">game</a></li><li><a href="module-gameState.html">gameState</a></li><li><a href="module-mechanics.html">mechanics</a></li><li><a href="module-path.html">path</a></li><li><a href="module-renderer.html">renderer</a></li><li><a href="module-uiHandlers.html">uiHandlers</a></li></ul>
+</nav>
+
+<br class="clear">
+
+<footer>
+    Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.3</a> on Mon Feb 17 2025 09:19:19 GMT-0500 (Eastern Standard Time)
+</footer>
+
+<script> prettyPrint(); </script>
+<script src="scripts/linenumber.js"> </script>
+</body>
+</html>