about summary refs log tree commit diff stats
path: root/html/tower/js/mechanics.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/tower/js/mechanics.js')
-rw-r--r--html/tower/js/mechanics.js259
1 files changed, 182 insertions, 77 deletions
diff --git a/html/tower/js/mechanics.js b/html/tower/js/mechanics.js
index 13b3ce8..2430ca0 100644
--- a/html/tower/js/mechanics.js
+++ b/html/tower/js/mechanics.js
@@ -1,23 +1,44 @@
-// Combat mechanics
+/**
+ * Combat Mechanics Module
+ * 
+ * This module handles all combat-related game mechanics including:
+ * 1. Enemy movement and behavior
+ * 2. Tower attacks and targeting
+ * 3. Projectile and particle systems
+ * 4. Status effects and special abilities
+ */
+
+/**
+ * Updates all enemy states including movement, health, and status effects
+ * Implements core game loop mechanics for enemy behavior
+ * 
+ * Key features:
+ * - Path following
+ * - Health management
+ * - Status effect processing
+ * - Collision detection
+ * 
+ * @returns {void}
+ */
 function updateEnemies() {
     const cellSize = canvas.width / 20;
     
     gameState.enemies = gameState.enemies.filter(enemy => {
-        // Add progress property if it doesn't exist
+        // Initialize progress tracking for new enemies
         if (typeof enemy.progress === 'undefined') {
             enemy.progress = 0;
         }
         
-        // Update progress
+        // Update movement progress
         enemy.progress += enemy.speed * 0.001;
         
-        // Check if enemy has completed the path
+        // Handle path completion
         if (enemy.progress >= 1) {
             gameState.enemiesEscaped++;
-            return false; // Remove from array
+            return false;
         }
         
-        // Check for collisions with projectiles
+        // Projectile collision detection
         const hitByProjectile = gameState.projectiles.some(projectile => {
             const distance = Math.hypot(
                 enemy.position.x - projectile.startPos.x,
@@ -28,15 +49,15 @@ function updateEnemies() {
         
         if (hitByProjectile) {
             gameState.awardEnemyDestroyed();
-            return false; // Remove from array
+            return false;
         }
         
-        // Update enemy position based on progress
+        // Update position based on path progress
         const pathPosition = getPathPosition(enemy.progress, gameState.path);
         enemy.position.x = pathPosition.x;
         enemy.position.y = pathPosition.y;
         
-        // Check if slow effect has expired
+        // Process slow effect expiration
         if (enemy.slowed && performance.now() > enemy.slowExpiry) {
             enemy.slowed = false;
             enemy.slowStacks = 0;
@@ -44,7 +65,7 @@ function updateEnemies() {
             enemy.speed = enemy.originalSpeed;
         }
         
-        // Add slime trail for slowed enemies (more particles for more stacks)
+        // Visual feedback for slowed status
         if (enemy.slowed && Math.random() < 0.2 + (enemy.slowStacks * 0.05)) {
             gameState.particles.push(createSlimeTrail(enemy, cellSize));
         }
@@ -65,12 +86,20 @@ function updateEnemies() {
     });
 }
 
+/**
+ * Updates particle effects with time-based animation
+ * Implements particle system lifecycle management
+ * 
+ * @param {Array<Object>} particles - Array of particle objects
+ * @param {number} timestamp - Current game timestamp
+ * @param {number} deltaTime - Time elapsed since last frame
+ * @returns {Array<Object>} Updated particles array
+ */
 function updateParticles(particles, timestamp, deltaTime) {
     return particles.filter(particle => {
         const age = timestamp - particle.createdAt;
         if (age > particle.lifetime) return false;
         
-        // Only update position for particles with velocity
         if (particle.velocity) {
             particle.position.x += particle.velocity.x * deltaTime;
             particle.position.y += particle.velocity.y * deltaTime;
@@ -80,6 +109,14 @@ function updateParticles(particles, timestamp, deltaTime) {
     });
 }
 
+/**
+ * Creates death effect particles for defeated entities
+ * Implements visual feedback system
+ * 
+ * @param {Object} target - The defeated entity
+ * @param {number} cellSize - Size of grid cell for scaling
+ * @returns {Array<Object>} Array of particle objects
+ */
 function createDeathParticles(target, cellSize) {
     const particles = [];
     const centerX = (target.position.x + 0.5) * cellSize;
@@ -91,8 +128,6 @@ function createDeathParticles(target, cellSize) {
         const randomAngle = baseAngle + (Math.random() - 0.5) * 1.5;
         const speedMultiplier = 0.7 + Math.random() * 0.6;
         const startOffset = Math.random() * 5;
-        const startX = centerX + Math.cos(randomAngle) * startOffset;
-        const startY = centerY + Math.sin(randomAngle) * startOffset;
         
         particles.push(createParticle(
             {
@@ -100,19 +135,33 @@ function createDeathParticles(target, cellSize) {
                 speed: ParticleTypes.DEATH_PARTICLE.speed * speedMultiplier,
                 lifetime: ParticleTypes.DEATH_PARTICLE.lifetime * (0.8 + Math.random() * 0.4)
             },
-            { x: startX, y: startY },
+            {
+                x: centerX + Math.cos(randomAngle) * startOffset,
+                y: centerY + Math.sin(randomAngle) * startOffset
+            },
             randomAngle
         ));
     }
     return particles;
 }
 
+/**
+ * Processes tower attacks and targeting
+ * Implements combat mechanics and special abilities
+ * 
+ * @param {Array<Object>} towers - Array of tower objects
+ * @param {Array<Object>} enemies - Array of enemy objects
+ * @param {Array<Object>} projectiles - Array of projectile objects
+ * @param {Array<Object>} particles - Array of particle objects
+ * @param {number} timestamp - Current game timestamp
+ * @param {number} cellSize - Size of grid cell for scaling
+ */
 function processTowerAttacks(towers, enemies, projectiles, particles, timestamp, cellSize) {
     towers.forEach(tower => {
         if (timestamp - tower.lastAttackTime > 1000 / tower.attackSpeed) {
             const enemiesInRange = findEnemiesInRange(tower, enemies);
             
-            if (enemiesInRange.length > 0) {
+            if (enemiesInRange.length > 0 && tower.ammo > 0) {
                 const target = enemiesInRange[0];
                 handleTowerAttack(tower, target, projectiles, particles, timestamp, cellSize);
             }
@@ -152,15 +201,22 @@ function createSlimeTrail(enemy, cellSize) {
     };
 }
 
+/**
+ * Handles individual tower attack logic including special effects
+ * Implements tower ability system
+ * 
+ * @param {Object} tower - Attacking tower
+ * @param {Object} target - Target enemy
+ * @param {Array<Object>} projectiles - Projectile array
+ * @param {Array<Object>} particles - Particle array
+ * @param {number} timestamp - Current game timestamp
+ * @param {number} cellSize - Grid cell size
+ */
 function handleTowerAttack(tower, target, projectiles, particles, timestamp, cellSize) {
-    // Check if tower has ammo
-    if (tower.ammo <= 0) {
-        return;
-    }
-    
     // Decrease ammo
     tower.ammo--;
     
+    // Create projectile
     projectiles.push({
         startPos: tower.position,
         targetPos: target.position,
@@ -169,66 +225,26 @@ function handleTowerAttack(tower, target, projectiles, particles, timestamp, cel
         towerType: tower.type
     });
     
+    // Process special abilities
     if (tower.special === 'slow') {
-        // Initialize slow effect if not present
-        if (!target.slowStacks) {
-            target.slowStacks = 0;
-        }
-        
-        // Add another stack of slow (up to a maximum)
-        const maxStacks = 5;  // Maximum 5 stacks
-        if (target.slowStacks < maxStacks) {
-            target.slowStacks++;
-            // Each stack slows by an additional 10% (multiplicative)
-            const newSlowAmount = 1 - Math.pow(0.9, target.slowStacks);  // 10%, 19%, 27%, 34%, 41%
-            
-            // Only adjust speed if this is a stronger slow
-            if (!target.slowed || newSlowAmount > target.currentSlowAmount) {
-                const originalSpeed = target.originalSpeed || target.speed;
-                target.originalSpeed = originalSpeed;  // Store original speed if not stored
-                target.speed = originalSpeed * (1 - newSlowAmount);
-                target.currentSlowAmount = newSlowAmount;
-                target.slowed = true;
-            }
-            
-            // Create slime particles for visual feedback
-            for (let i = 0; i < 4 + target.slowStacks; i++) {  // More particles for more stacks
-                particles.push(createSlimeTrail(target, cellSize));
-            }
-        }
-        
-        // Refresh slow duration
-        target.slowExpiry = timestamp + 2000;  // 2 second duration
+        handleSlowEffect(target, tower, timestamp, particles, cellSize);
     } else if (tower.special === 'aoe') {
-        // Find all enemies in AOE radius
-        const enemiesInAOE = gameState.enemies.filter(enemy => {
-            const dx = enemy.position.x - target.position.x;
-            const dy = enemy.position.y - target.position.y;
-            return Math.sqrt(dx * dx + dy * dy) <= tower.aoeRadius;
-        });
-        
-        // Create AOE explosion effect
-        particles.push(createAOEExplosion(target.position, cellSize));
-        
-        // Damage all enemies in range
-        enemiesInAOE.forEach(enemy => {
-            enemy.currentHealth -= tower.damage;
-            if (enemy.currentHealth <= 0) {
-                particles.push(...createDeathParticles(enemy, cellSize));
-            }
-        });
-    } else {
-        // Normal damage for regular towers
-        target.currentHealth -= tower.damage;
+        handleAOEEffect(target, tower, enemies, particles, cellSize);
     }
     
     tower.lastAttackTime = timestamp;
-    
-    if (target.currentHealth <= 0) {
-        particles.push(...createDeathParticles(target, cellSize));
-    }
 }
 
+/**
+ * Processes enemy attack behaviors and targeting
+ * Implements enemy combat AI
+ * 
+ * @param {Array<Object>} enemies - Array of enemy objects
+ * @param {Array<Object>} towers - Array of tower objects
+ * @param {Array<Object>} particles - Particle effect array
+ * @param {number} timestamp - Current game timestamp
+ * @param {number} cellSize - Grid cell size
+ */
 function processEnemyAttacks(enemies, towers, particles, timestamp, cellSize) {
     enemies.forEach(enemy => {
         if (!EnemyTypes[enemy.type].isRanged) return;
@@ -244,6 +260,14 @@ function processEnemyAttacks(enemies, towers, particles, timestamp, cellSize) {
     });
 }
 
+/**
+ * Finds towers within enemy attack range
+ * Implements targeting system for enemies
+ * 
+ * @param {Object} enemy - Enemy doing the targeting
+ * @param {Array<Object>} towers - Array of potential tower targets
+ * @returns {Array<Object>} Array of towers in range
+ */
 function findTowersInRange(enemy, towers) {
     return towers.filter(tower => {
         const dx = tower.position.x - enemy.position.x;
@@ -252,13 +276,22 @@ function findTowersInRange(enemy, towers) {
     });
 }
 
+/**
+ * Handles enemy attack execution and effects
+ * Implements enemy combat mechanics
+ * 
+ * @param {Object} enemy - Attacking enemy
+ * @param {Object} tower - Target tower
+ * @param {Array<Object>} particles - Particle array for effects
+ * @param {number} timestamp - Current game timestamp
+ * @param {number} cellSize - Grid cell size
+ */
 function handleEnemyAttack(enemy, tower, particles, timestamp, cellSize) {
-    // Create enemy projectile
-    const projectileColor = '#8e44ad80'; // Semi-transparent purple
+    // Create projectile effect
     particles.push(createParticle(
         {
             ...ParticleTypes.PROJECTILE,
-            color: projectileColor,
+            color: '#8e44ad80', // Semi-transparent purple
             lifetime: 500
         },
         {
@@ -271,13 +304,85 @@ function handleEnemyAttack(enemy, tower, particles, timestamp, cellSize) {
         )
     ));
     
+    // Apply damage and update tower state
     tower.currentHealth -= enemy.damage;
     enemy.lastAttackTime = timestamp;
     
-    // Reduce tower's damage as it takes damage
+    // Dynamic damage reduction based on tower health
     tower.damage = TowerTypes[tower.type].damage * (tower.currentHealth / tower.maxHealth);
 }
 
+/**
+ * Handles slow effect application and stacking
+ * Implements status effect system
+ * 
+ * @param {Object} target - Enemy to apply slow to
+ * @param {Object} tower - Tower applying the effect
+ * @param {number} timestamp - Current game timestamp
+ * @param {Array<Object>} particles - Particle array for effects
+ * @param {number} cellSize - Grid cell size
+ */
+function handleSlowEffect(target, tower, timestamp, particles, cellSize) {
+    // Initialize slow effect tracking
+    if (!target.slowStacks) {
+        target.slowStacks = 0;
+    }
+    
+    const maxStacks = 5;  // Maximum 5 stacks
+    if (target.slowStacks < maxStacks) {
+        target.slowStacks++;
+        // Each stack slows by an additional 10% (multiplicative)
+        const newSlowAmount = 1 - Math.pow(0.9, target.slowStacks);
+        
+        // Only update if new slow is stronger
+        if (!target.slowed || newSlowAmount > target.currentSlowAmount) {
+            const originalSpeed = target.originalSpeed || target.speed;
+            target.originalSpeed = originalSpeed;
+            target.speed = originalSpeed * (1 - newSlowAmount);
+            target.currentSlowAmount = newSlowAmount;
+            target.slowed = true;
+        }
+        
+        // Visual feedback particles
+        for (let i = 0; i < 4 + target.slowStacks; i++) {
+            particles.push(createSlimeTrail(target, cellSize));
+        }
+    }
+    
+    // Refresh effect duration
+    target.slowExpiry = timestamp + 2000;  // 2 second duration
+}
+
+/**
+ * Handles AOE (Area of Effect) damage and visual effects
+ * Implements area damage system
+ * 
+ * @param {Object} target - Primary target
+ * @param {Object} tower - Tower dealing AOE damage
+ * @param {Array<Object>} enemies - All enemies for AOE calculation
+ * @param {Array<Object>} particles - Particle array for effects
+ * @param {number} cellSize - Grid cell size
+ */
+function handleAOEEffect(target, tower, enemies, particles, cellSize) {
+    // Find all enemies in AOE radius
+    const enemiesInAOE = enemies.filter(enemy => {
+        const dx = enemy.position.x - target.position.x;
+        const dy = enemy.position.y - target.position.y;
+        return Math.sqrt(dx * dx + dy * dy) <= tower.aoeRadius;
+    });
+    
+    // Create explosion effect
+    particles.push(createAOEExplosion(target.position, cellSize));
+    
+    // Apply AOE damage
+    enemiesInAOE.forEach(enemy => {
+        enemy.currentHealth -= tower.damage;
+        if (enemy.currentHealth <= 0) {
+            particles.push(...createDeathParticles(enemy, cellSize));
+        }
+    });
+}
+
 // Update createEnemy to track original speed
 function createEnemy(startPosition) {
     const enemy = {