/** * Boss.js * Colossal Boss enemy for level 6 * Large immobile boss with turrets and special attacks */ export class Boss { /** * Generate the colossal boss for level 6 * @param {Object} level - Level data * @param {number} levelWidth - Width of the level * @param {number} canvasHeight - Canvas height * @returns {Object} - Boss data with turrets */ static generate(level, levelWidth, canvasHeight) { console.log(`👹 Generating Colossal Boss for level 6!`); // Boss positioned in center-right of level to block the path const bossX = levelWidth * 0.6; // 60% through the level const bossY = canvasHeight - 250; // Standing on ground const bossWidth = 150; const bossHeight = 200; const boss = { x: bossX, y: bossY, width: bossWidth, height: bossHeight, health: 5, // Takes 5 hits maxHealth: 5, color: '#2F4F4F', // Dark slate gray type: 'colossus', active: true, // Collision boxes (knees for damage) leftKnee: { x: bossX + 20, y: bossY + bossHeight - 60, width: 40, height: 40 }, rightKnee: { x: bossX + bossWidth - 60, y: bossY + bossHeight - 60, width: 40, height: 40 }, // Boss behavior lastTurretShot: Date.now(), turretCooldown: 2000, // Turrets fire every 2 seconds lastMinionLaunch: Date.now(), minionCooldown: 4000, // Launch minions every 4 seconds // Visual eyeColor: '#FF0000', // Red glowing eyes isDamaged: false, damageFlashTimer: 0, enraged: false // Becomes enraged at low health }; // Generate turrets on the boss (2 turrets) const turrets = [ { x: bossX + 30, y: bossY + 50, width: 25, height: 25, color: '#8B4513', type: 'turret', lastShot: Date.now(), shootCooldown: 2500 // Individual cooldown }, { x: bossX + bossWidth - 55, y: bossY + 50, width: 25, height: 25, color: '#8B4513', type: 'turret', lastShot: Date.now(), shootCooldown: 3000 // Slightly different timing } ]; console.log(`👹 Colossal Boss spawned at x=${bossX.toFixed(0)}, health=${boss.health}`); console.log(`🔫 ${turrets.length} turrets mounted on boss`); return { boss, turrets }; } /** * Update boss behavior * @param {Object} boss - Boss object * @param {Array} turrets - Boss turrets * @param {Object} mario - Mario object * @param {Array} projectiles - Projectiles array * @param {Array} flyingEyes - Flying eyes array (for spawning minions) * @param {Function} playSound - Sound callback */ static update(boss, turrets, mario, projectiles, flyingEyes, playSound) { if (!boss || !boss.active) return; const currentTime = Date.now(); // Update boss state if (boss.health < boss.maxHealth / 2) { boss.enraged = true; } // Damage flash animation if (boss.isDamaged) { boss.damageFlashTimer--; if (boss.damageFlashTimer <= 0) { boss.isDamaged = false; } } // Update turrets - they shoot projectiles at Mario turrets.forEach(turret => { if (currentTime - turret.lastShot > turret.shootCooldown) { // Calculate trajectory to Mario const dx = mario.x - turret.x; const dy = mario.y - turret.y; const distance = Math.sqrt(dx * dx + dy * dy); const speed = boss.enraged ? 6 : 4; // Faster when enraged const velocityX = (dx / distance) * speed; const velocityY = (dy / distance) * speed; projectiles.push({ x: turret.x + turret.width / 2, y: turret.y + turret.height / 2, velocityX: velocityX, velocityY: velocityY, radius: 10, color: boss.enraged ? '#FF0000' : '#FF8C00', // Red when enraged, orange normally type: 'boss_projectile', life: 300 }); turret.lastShot = currentTime; if (playSound) playSound('enemy_defeat'); console.log(`🔫 Boss turret fired at Mario!`); } }); // Spawn flying eye minions periodically if (boss.enraged && currentTime - boss.lastMinionLaunch > boss.minionCooldown) { // Spawn a flying eye minion const minionX = boss.x + boss.width / 2; const minionY = boss.y + 50; flyingEyes.push({ x: minionX, y: minionY, width: 25, height: 25, velocityX: (Math.random() - 0.5) * 2, velocityY: -3, // Fly upward initially color: '#8B0000', // Dark red for minions pupilColor: '#000000', type: 'flying_eye_minion', health: 1, maxHealth: 1, chaseDistance: 300, chaseSpeed: 3, idleSpeed: 1, lastDirectionChange: Date.now(), directionChangeInterval: 2000, isChasing: false, dashCooldown: 0, dashDuration: 0, isDashing: false, dashSpeed: 6, lastDashTime: Date.now(), dashInterval: 4000, blinkTimer: 0, isBlinking: false }); boss.lastMinionLaunch = currentTime; if (playSound) playSound('powerup'); console.log(`👁️ Boss spawned a flying eye minion!`); } } /** * Damage the boss * @param {Object} boss - Boss object * @param {Function} playSound - Sound callback * @returns {boolean} - True if boss was defeated */ static damage(boss, playSound) { if (!boss || !boss.active) return false; boss.health--; boss.isDamaged = true; boss.damageFlashTimer = 15; // Flash for 15 frames if (playSound) playSound('enemy_defeat'); console.log(`👹 Boss damaged! Health: ${boss.health}/${boss.maxHealth}`); if (boss.health <= 0) { boss.active = false; console.log(`👹 BOSS DEFEATED!`); return true; } return false; } /** * Check collision between Mario and boss knees (weak points) * @param {Object} mario - Mario object * @param {Object} boss - Boss object * @returns {boolean} - True if Mario hit a knee from above */ static checkKneeCollision(mario, boss) { if (!boss || !boss.active) return false; // Check if Mario is jumping down onto knees const isFalling = mario.velocityY > 0; // Check left knee const hitLeftKnee = this._isCollidingRectRect(mario, boss.leftKnee) && isFalling; // Check right knee const hitRightKnee = this._isCollidingRectRect(mario, boss.rightKnee) && isFalling; return hitLeftKnee || hitRightKnee; } /** * Check collision between Mario and boss body (damage to Mario) * @param {Object} mario - Mario object * @param {Object} boss - Boss object * @returns {boolean} - True if Mario touched boss body */ static checkBodyCollision(mario, boss) { if (!boss || !boss.active) return false; return this._isCollidingRectRect(mario, boss); } /** * Rectangle-Rectangle collision detection */ static _isCollidingRectRect(rect1, rect2) { return rect1.x < rect2.x + rect2.width && rect1.x + rect1.width > rect2.x && rect1.y < rect2.y + rect2.height && rect1.y + rect1.height > rect2.y; } } export default Boss;