/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BossEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('jefe', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); self.speed = -1; // Very slow self.lastX = self.x; // Initialize in constructor self.lastY = self.y; // Initialize in constructor self.shootCooldown = 0; self.health = 10; // Takes 10 hits to destroy self.verticalSpeed = 2; self.verticalDirection = 1; self.isFirstFrame = true; // Initialization flag self.update = function () { // Skip first frame logic to ensure proper initialization if (self.isFirstFrame) { self.isFirstFrame = false; return; // Skip first frame to avoid premature trigger } // Move enemy left self.x += self.speed; // Move vertically up and down self.y += self.verticalSpeed * self.verticalDirection; // Calculate bounds based on scaled boss height to keep it fully visible var margin = 50; // Safety margin var scaledHeight = 126 * 2; // Original height * scale factor var topBound = scaledHeight / 2 + margin; var bottomBound = 2732 - scaledHeight / 2 - margin; if (self.y <= topBound || self.y >= bottomBound) { self.verticalDirection *= -1; } // Update shooting cooldown if (self.shootCooldown > 0) { self.shootCooldown--; } // Update last values for next frame self.lastX = self.x; self.lastY = self.y; }; self.canShoot = function () { return self.shootCooldown <= 0; }; self.shoot = function () { if (self.canShoot()) { self.shootCooldown = 60; // Slower shooting return true; } return false; }; self.takeDamage = function () { self.health--; // Visual feedback for taking damage enemyGraphics.alpha = Math.max(0.3, Math.min(1.0, 0.3 + self.health / 10 * 0.7)); return self.health <= 0; }; self.cleanup = function () { // Clear any references and reset properties self.lastX = undefined; self.lastY = undefined; self.shootCooldown = 0; self.speed = 0; self.health = 0; self.verticalSpeed = 0; self.verticalDirection = 1; self.isFirstFrame = true; // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.lastX = self.x; // Initialize in constructor self.lastY = self.y; // Initialize in constructor self.directionX = 1; // Default horizontal direction self.directionY = 0; // Default no vertical movement self.update = function () { // Null checks for safety if (self.lastX === null || self.lastX === undefined) self.lastX = self.x; if (self.lastY === null || self.lastY === undefined) self.lastY = self.y; // Set bullet rotation based on direction var angle = Math.atan2(self.directionY, self.directionX); bulletGraphics.rotation = angle; self.x += self.speed * self.directionX; self.y += self.speed * self.directionY; self.lastX = self.x; self.lastY = self.y; }; self.cleanup = function () { // Clear any references and reset properties self.lastX = undefined; self.lastY = undefined; self.speed = 0; self.directionX = 0; self.directionY = 0; // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } }; return self; }); var CharacterSelectionScreen = Container.expand(function () { var self = Container.call(this); // Create title image var titleImage = self.attachAsset('titulopersonajes', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 400, scaleX: 4, scaleY: 4 }); // Create character images var player1Image = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 600, y: 1000, scaleX: 1.5, scaleY: 1.5 }); var player2Image = self.attachAsset('jugador2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 200, y: 1000, scaleX: 1.5, scaleY: 1.5 }); var player3Image = self.attachAsset('jugador3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 200, y: 1000, scaleX: 1.5, scaleY: 1.5 }); var player4Image = self.attachAsset('jugador4', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 600, y: 1000, scaleX: 1.5, scaleY: 1.5 }); // Create play button image var playImage = self.attachAsset('jugar', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1400, scaleX: 2, scaleY: 2 }); // Store references for event handling self.player1Image = player1Image; self.player2Image = player2Image; self.player3Image = player3Image; self.player4Image = player4Image; self.playButton = playImage; self.selectedCharacter = 'player'; // Default selection return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -3; self.lastX = self.x; // Initialize in constructor self.shootCooldown = 0; self.isFirstFrame = true; // Initialization flag self.update = function () { // Skip first frame logic to ensure proper initialization if (self.isFirstFrame) { self.isFirstFrame = false; return; // Skip first frame to avoid premature trigger } // Move enemy left self.x += self.speed; // Update shooting cooldown if (self.shootCooldown > 0) { self.shootCooldown--; } // Update lastX for next frame self.lastX = self.x; }; self.canShoot = function () { return self.shootCooldown <= 0; }; self.shoot = function () { if (self.canShoot()) { self.shootCooldown = 180; // 3 second cooldown return true; } return false; }; self.cleanup = function () { // Clear any references and reset properties self.lastX = undefined; self.shootCooldown = 0; self.speed = 0; self.isFirstFrame = true; // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.lastX = self.x; // Initialize in constructor self.lastY = self.y; // Initialize in constructor self.directionX = -1; // Default moving left self.directionY = 0; // Default no vertical movement self.update = function () { // Null checks for safety if (self.lastX === null || self.lastX === undefined) self.lastX = self.x; if (self.lastY === null || self.lastY === undefined) self.lastY = self.y; // Set bullet rotation based on direction var angle = Math.atan2(self.directionY, self.directionX); bulletGraphics.rotation = angle; self.x += self.speed * self.directionX; self.y += self.speed * self.directionY; self.lastX = self.x; self.lastY = self.y; }; self.cleanup = function () { // Clear any references and reset properties self.lastX = undefined; self.lastY = undefined; self.speed = 0; self.directionX = 0; self.directionY = 0; // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } }; return self; }); var FastEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemigorapido', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -6; // Faster than regular enemy self.lastX = self.x; // Initialize in constructor self.shootCooldown = 0; self.isFirstFrame = true; // Initialization flag self.update = function () { // Skip first frame logic to ensure proper initialization if (self.isFirstFrame) { self.isFirstFrame = false; return; // Skip first frame to avoid premature trigger } // Move enemy left self.x += self.speed; // Update shooting cooldown if (self.shootCooldown > 0) { self.shootCooldown--; } // Update lastX for next frame self.lastX = self.x; }; self.canShoot = function () { return self.shootCooldown <= 0; }; self.shoot = function () { if (self.canShoot()) { self.shootCooldown = 120; // 2 second cooldown return true; } return false; }; self.cleanup = function () { // Clear any references and reset properties self.lastX = undefined; self.shootCooldown = 0; self.speed = 0; self.isFirstFrame = true; // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } }; return self; }); var HealthPowerup = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('healthPowerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; self.lastX = self.x; self.lastY = self.y; self.bobOffset = 0; self.isFirstFrame = true; self.update = function () { if (self.isFirstFrame) { self.isFirstFrame = false; return; } // Move left self.x += self.speed; // Bob up and down for visual appeal self.bobOffset += 0.1; powerupGraphics.y = Math.sin(self.bobOffset) * 10; // Gentle rotation powerupGraphics.rotation += 0.02; self.lastX = self.x; self.lastY = self.y; }; self.cleanup = function () { self.lastX = undefined; self.lastY = undefined; self.speed = 0; self.isFirstFrame = true; if (self.parent) { self.parent.removeChild(self); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset(selectedPlayerAsset, { anchorX: 0.5, anchorY: 0.5 }); self.shootCooldown = 0; self.update = function () { if (self.shootCooldown > 0) { self.shootCooldown--; } // Update shield visual effect if (playerShieldTime > 0) { // Pulsing blue tint when shielded var pulse = Math.sin(LK.ticks * 0.3) * 0.3 + 0.7; playerGraphics.tint = 0x4488ff; playerGraphics.alpha = pulse; } else { // Normal appearance playerGraphics.tint = 0xffffff; playerGraphics.alpha = 1.0; } }; self.canShoot = function () { return self.shootCooldown <= 0; }; self.shoot = function () { if (self.canShoot()) { // Faster shooting with weapon upgrades var cooldown = Math.max(5, 10 - (playerWeaponLevel - 1) * 2); self.shootCooldown = cooldown; return playerWeaponLevel; // Return weapon level for multi-shot } return 0; }; return self; }); var ShieldEnemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemigoescudo', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; // Slower than regular enemy self.lastX = self.x; // Initialize in constructor self.shootCooldown = 0; self.health = 3; // Takes 3 hits to destroy self.isFirstFrame = true; // Initialization flag self.update = function () { // Skip first frame logic to ensure proper initialization if (self.isFirstFrame) { self.isFirstFrame = false; return; // Skip first frame to avoid premature trigger } // Move enemy left self.x += self.speed; // Update shooting cooldown if (self.shootCooldown > 0) { self.shootCooldown--; } // Update lastX for next frame self.lastX = self.x; }; self.canShoot = function () { return self.shootCooldown <= 0; }; self.shoot = function () { if (self.canShoot()) { self.shootCooldown = 150; // Much slower shooting return true; } return false; }; self.takeDamage = function () { self.health--; // Visual feedback for taking damage enemyGraphics.alpha = Math.max(0.5, Math.min(1.0, 0.5 + self.health / 3 * 0.5)); return self.health <= 0; }; self.cleanup = function () { // Clear any references and reset properties self.lastX = undefined; self.shootCooldown = 0; self.speed = 0; self.health = 0; self.isFirstFrame = true; // Remove from parent if still attached if (self.parent) { self.parent.removeChild(self); } }; return self; }); var ShieldPowerup = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('shieldPowerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; self.lastX = self.x; self.lastY = self.y; self.bobOffset = 0; self.isFirstFrame = true; self.update = function () { if (self.isFirstFrame) { self.isFirstFrame = false; return; } // Move left self.x += self.speed; // Bob up and down for visual appeal self.bobOffset += 0.15; powerupGraphics.y = Math.sin(self.bobOffset) * 15; // Pulsing effect var pulse = Math.sin(self.bobOffset * 2) * 0.2 + 1.0; powerupGraphics.scaleX = pulse; powerupGraphics.scaleY = pulse; self.lastX = self.x; self.lastY = self.y; }; self.cleanup = function () { self.lastX = undefined; self.lastY = undefined; self.speed = 0; self.isFirstFrame = true; if (self.parent) { self.parent.removeChild(self); } }; return self; }); var StartScreen = Container.expand(function () { var self = Container.call(this); // Create title image var titleImage = self.attachAsset('titulo', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 450, scaleX: 8, scaleY: 8 }); // Create play button image var playImage = self.attachAsset('jugar', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 50, scaleX: 2, scaleY: 2 }); // Create character selection button image var characterImage = self.attachAsset('seleccionpersonaje', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 450, scaleX: 2, scaleY: 2 }); // Store button references for event handling self.playButton = playImage; self.characterButton = characterImage; return self; }); var WeaponPowerup = Container.expand(function () { var self = Container.call(this); var powerupGraphics = self.attachAsset('weaponPowerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -2; self.lastX = self.x; self.lastY = self.y; self.bobOffset = 0; self.isFirstFrame = true; self.update = function () { if (self.isFirstFrame) { self.isFirstFrame = false; return; } // Move left self.x += self.speed; // Bob up and down for visual appeal self.bobOffset += 0.12; powerupGraphics.y = Math.sin(self.bobOffset) * 12; // Spinning effect powerupGraphics.rotation += 0.05; self.lastX = self.x; self.lastY = self.y; }; self.cleanup = function () { self.lastX = undefined; self.lastY = undefined; self.speed = 0; self.isFirstFrame = true; if (self.parent) { self.parent.removeChild(self); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ // Game state variables var gameStarted = false; var startScreen = null; var characterSelectionScreen = null; var selectedPlayerAsset = 'player'; // Track which character asset to use var player = null; var enemies = []; var fastEnemies = []; var shieldEnemies = []; var bossEnemies = []; var bullets = []; var enemyBullets = []; var enemySpawnTimer = 0; var enemySpawnRate = 120; // Start spawning every 2 seconds (120 frames) var difficultyTimer = 0; var playerLives = 3; var playerShieldTime = 0; // Shield duration in frames var playerWeaponLevel = 1; // Weapon upgrade level (1-3) var playerWeaponTime = 0; // Weapon upgrade duration in frames // Power-up arrays var healthPowerups = []; var shieldPowerups = []; var weaponPowerups = []; var powerupSpawnTimer = 0; var powerupSpawnRate = 180; // Spawn every 3 seconds var enemiesEscaped = 0; // Track how many enemies have escaped var bossTimer = 0; // Track time for boss battle cycles (2 minutes = 7200 frames) var bossPhase = false; // Track if we're in boss phase var bossesSpawned = 0; // Track how many bosses have been spawned in current phase // Arrays to track objects marked for removal (using object references for safer iteration) var bulletsToRemove = []; var healthPowerupsToRemove = []; var shieldPowerupsToRemove = []; var weaponPowerupsToRemove = []; var enemyBulletsToRemove = []; var enemiesToRemove = []; var fastEnemiesToRemove = []; var shieldEnemiesToRemove = []; var bossEnemiesToRemove = []; var isDragging = false; var scoreTxt = null; var livesTxt = null; var livesNumberTxt = null; // Add background image var backgroundImage = game.attachAsset('fondo', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Initialize start screen startScreen = new StartScreen(); game.addChild(startScreen); function showCharacterSelection() { // Remove start screen if (startScreen && startScreen.parent) { startScreen.destroy(); startScreen = null; } // Show character selection screen characterSelectionScreen = new CharacterSelectionScreen(); game.addChild(characterSelectionScreen); } function startGame() { gameStarted = true; // Remove start screen if (startScreen && startScreen.parent) { startScreen.destroy(); startScreen = null; } // Remove character selection screen if (characterSelectionScreen && characterSelectionScreen.parent) { characterSelectionScreen.destroy(); characterSelectionScreen = null; } // Initialize player with selected character asset player = new Player(); player.x = 300; player.y = 2732 - 150; // Near bottom with some margin game.addChild(player); // Create score display scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.setText(LK.getScore()); // Create lives display with image livesTxt = LK.getAsset('vidas', { anchorX: 0, anchorY: 0, x: 120, y: 20 }); LK.gui.topLeft.addChild(livesTxt); // Create lives number display livesNumberTxt = new Text2(playerLives.toString(), { size: 80, fill: 0xFFFFFF }); livesNumberTxt.anchor.set(0, 0.5); livesNumberTxt.x = 330; // Position to the right of vidas image livesNumberTxt.y = 77; // Center vertically with vidas image LK.gui.topLeft.addChild(livesNumberTxt); } // Touch/mouse down - handle start screen or game play game.down = function (x, y, obj) { if (!gameStarted) { // Handle start screen if (startScreen) { // Check if play button was clicked if (startScreen.playButton) { var deltaX = x - startScreen.playButton.x; var deltaY = y - startScreen.playButton.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 150) { // Button click radius startGame(); return; } } // Check if character selection button was clicked if (startScreen.characterButton) { var deltaX = x - startScreen.characterButton.x; var deltaY = y - startScreen.characterButton.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 200) { // Button click radius showCharacterSelection(); return; } } return; } // Handle character selection screen if (characterSelectionScreen) { // Check character image clicks if (characterSelectionScreen.player1Image) { var deltaX = x - characterSelectionScreen.player1Image.x; var deltaY = y - characterSelectionScreen.player1Image.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 150) { selectedPlayerAsset = 'player'; characterSelectionScreen.selectedCharacter = 'player'; return; } } if (characterSelectionScreen.player2Image) { var deltaX = x - characterSelectionScreen.player2Image.x; var deltaY = y - characterSelectionScreen.player2Image.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 150) { selectedPlayerAsset = 'jugador2'; characterSelectionScreen.selectedCharacter = 'jugador2'; return; } } if (characterSelectionScreen.player3Image) { var deltaX = x - characterSelectionScreen.player3Image.x; var deltaY = y - characterSelectionScreen.player3Image.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 150) { selectedPlayerAsset = 'jugador3'; characterSelectionScreen.selectedCharacter = 'jugador3'; return; } } if (characterSelectionScreen.player4Image) { var deltaX = x - characterSelectionScreen.player4Image.x; var deltaY = y - characterSelectionScreen.player4Image.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 150) { selectedPlayerAsset = 'jugador4'; characterSelectionScreen.selectedCharacter = 'jugador4'; return; } } // Check if play button was clicked if (characterSelectionScreen.playButton) { var deltaX = x - characterSelectionScreen.playButton.x; var deltaY = y - characterSelectionScreen.playButton.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 150) { // Button click radius startGame(); return; } } return; } } isDragging = true; // Move player to touch position (constrain to vertical movement) player.y = Math.max(100, Math.min(2732 - 100, y)); var weaponLevel = player.shoot(); if (weaponLevel > 0) { // Calculate direction vector from player to tap position var deltaX = x - player.x; var deltaY = y - player.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var directionX = 1; var directionY = 0; if (distance > 1.0) { directionX = deltaX / distance; directionY = deltaY / distance; } // Create bullets based on weapon level for (var w = 0; w < weaponLevel; w++) { var bullet = new Bullet(); bullet.x = player.x + 40; // Spawn slightly ahead of player bullet.y = player.y - 40 + (w - Math.floor(weaponLevel / 2)) * 20; // Spread bullets vertically bullet.lastX = bullet.x; bullet.lastY = bullet.y; // Slight angle variation for multiple bullets var angleOffset = (w - Math.floor(weaponLevel / 2)) * 0.2; var cos = Math.cos(angleOffset); var sin = Math.sin(angleOffset); bullet.directionX = directionX * cos - directionY * sin; bullet.directionY = directionX * sin + directionY * cos; bullets.push(bullet); game.addChild(bullet); } LK.getSound('shoot').play(); } }; // Touch/mouse move - move player vertically while dragging game.move = function (x, y, obj) { if (!gameStarted || characterSelectionScreen) return; if (isDragging) { // Move player to touch position (constrain to vertical movement) player.y = Math.max(100, Math.min(2732 - 100, y)); } }; // Touch/mouse up - stop dragging game.up = function (x, y, obj) { if (!gameStarted || characterSelectionScreen) return; isDragging = false; }; game.update = function () { // Only update game logic if game has started and not in character selection if (!gameStarted || characterSelectionScreen) return; // Update enemy spawn timer and difficulty enemySpawnTimer++; difficultyTimer++; powerupSpawnTimer++; // Update power-up timers if (playerShieldTime > 0) { playerShieldTime--; } if (playerWeaponTime > 0) { playerWeaponTime--; if (playerWeaponTime <= 0) { playerWeaponLevel = 1; // Reset to normal weapon } } // Increase difficulty every 10 seconds (600 frames) if (difficultyTimer % 600 === 0 && enemySpawnRate > 30) { enemySpawnRate -= 10; // Spawn enemies more frequently } // Update boss timer (always increment) bossTimer++; // Check for boss battle cycle (every 1 minute = 3600 frames) if (bossTimer >= 3600) { bossPhase = true; bossesSpawned = 0; } // Spawn enemies - normal enemies spawn during non-boss phase OR after bosses are defeated if (!bossPhase || bossPhase && bossesSpawned >= 2 && bossEnemies.length === 0) { // Normal enemy spawning during non-boss phase or after boss defeat if (enemySpawnTimer >= enemySpawnRate) { var spawnType = Math.random(); // Define three spawn positions: top-right, center-right, bottom-right var spawnPositions = [400, // Top-right corner (with margin, lowered) 1566, // Center-right (middle of screen, lowered) 2432 // Bottom-right corner (with margin) ]; var spawnY = spawnPositions[Math.floor(Math.random() * 3)]; if (spawnType < 0.6) { // 60% chance for regular enemy var enemy = new Enemy(); enemy.x = 2048 + 30; enemy.y = spawnY; enemy.lastX = enemy.x; enemies.push(enemy); game.addChild(enemy); } else if (spawnType < 0.8) { // 20% chance for fast enemy var fastEnemy = new FastEnemy(); fastEnemy.x = 2048 + 30; fastEnemy.y = spawnY; fastEnemy.lastX = fastEnemy.x; fastEnemies.push(fastEnemy); game.addChild(fastEnemy); } else { // 20% chance for shield enemy var shieldEnemy = new ShieldEnemy(); shieldEnemy.x = 2048 + 30; shieldEnemy.y = spawnY; shieldEnemy.lastX = shieldEnemy.x; shieldEnemies.push(shieldEnemy); game.addChild(shieldEnemy); } enemySpawnTimer = 0; } } else if (bossPhase && bossesSpawned < 2) { // Boss phase - spawn 2 bosses then wait for them to be defeated if (enemySpawnTimer >= 60) { // Small delay between boss spawns // Define three spawn positions for bosses: top-right, center-right, bottom-right var bossSpawnPositions = [400, // Top-right corner (with margin, lowered) 1566, // Center-right (middle of screen, lowered) 2432 // Bottom-right corner (with margin) ]; var spawnY1 = bossSpawnPositions[Math.floor(Math.random() * 3)]; // First boss position var spawnY2 = bossSpawnPositions[Math.floor(Math.random() * 3)]; // Second boss position // Ensure bosses don't spawn at the same position while (spawnY2 === spawnY1) { spawnY2 = bossSpawnPositions[Math.floor(Math.random() * 3)]; } // Spawn first boss var bossEnemy1 = new BossEnemy(); bossEnemy1.x = 2048 + 30; bossEnemy1.y = spawnY1; bossEnemy1.lastX = bossEnemy1.x; bossEnemies.push(bossEnemy1); game.addChild(bossEnemy1); // Spawn second boss var bossEnemy2 = new BossEnemy(); bossEnemy2.x = 2048 + 30; bossEnemy2.y = spawnY2; bossEnemy2.lastX = bossEnemy2.x; bossEnemies.push(bossEnemy2); game.addChild(bossEnemy2); bossesSpawned = 2; enemySpawnTimer = 0; } } // Check if boss phase should end (when timer reaches next cycle) if (bossPhase && bossTimer >= 3600) { bossPhase = false; bossTimer = 0; // Reset timer for next boss cycle } // Spawn power-ups if (powerupSpawnTimer >= powerupSpawnRate) { var powerupType = Math.random(); var spawnY = Math.random() * (2732 - 200) + 100; if (powerupType < 0.4) { // 40% chance for health power-up var healthPowerup = new HealthPowerup(); healthPowerup.x = 2048 + 30; healthPowerup.y = spawnY; healthPowerup.lastX = healthPowerup.x; healthPowerup.lastY = healthPowerup.y; healthPowerups.push(healthPowerup); game.addChild(healthPowerup); } else if (powerupType < 0.7) { // 30% chance for shield power-up var shieldPowerup = new ShieldPowerup(); shieldPowerup.x = 2048 + 30; shieldPowerup.y = spawnY; shieldPowerup.lastX = shieldPowerup.x; shieldPowerup.lastY = shieldPowerup.y; shieldPowerups.push(shieldPowerup); game.addChild(shieldPowerup); } else { // 30% chance for weapon power-up var weaponPowerup = new WeaponPowerup(); weaponPowerup.x = 2048 + 30; weaponPowerup.y = spawnY; weaponPowerup.lastX = weaponPowerup.x; weaponPowerup.lastY = weaponPowerup.y; weaponPowerups.push(weaponPowerup); game.addChild(weaponPowerup); } powerupSpawnTimer = 0; } // Update and check bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; // Mark bullets that go off screen for removal if (bullet.x > 2048 + 50 || bullet.y < -50) { bulletsToRemove.push(bullet); continue; } // Skip collision detection for bullets outside meaningful collision zones if (bullet.x < -50 || bullet.x > 2100 || bullet.y < -50 || bullet.y > 2800) { continue; } // Check bullet vs enemy collisions var bulletHit = false; // Check regular enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; // Skip enemies outside collision zone (bounding box check) if (enemy.x < -250 || enemy.x > 2100 || enemy.y < -50 || enemy.y > 2800) { continue; } // Check bullet collision with regular enemy using distance-based detection var deltaX = bullet.x - enemy.x; var deltaY = bullet.y - enemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 80) { // 80px collision radius for regular enemies // Bullet hit enemy LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); // Check for victory condition if (LK.getScore() >= 10000) { LK.showYouWin(); return; } // Visual feedback tween(enemy, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (enemy.parent) { enemy.destroy(); } } }); enemiesToRemove.push(enemy); bulletsToRemove.push(bullet); bulletHit = true; LK.getSound('hit').play(); break; } } // Check fast enemies for (var j = fastEnemies.length - 1; j >= 0; j--) { var fastEnemy = fastEnemies[j]; // Skip fast enemies outside collision zone (bounding box check) if (fastEnemy.x < -250 || fastEnemy.x > 2100 || fastEnemy.y < -50 || fastEnemy.y > 2800) { continue; } // Check bullet collision with fast enemy using distance-based detection var deltaX = bullet.x - fastEnemy.x; var deltaY = bullet.y - fastEnemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 85) { // 85px collision radius for fast enemies LK.setScore(LK.getScore() + 15); scoreTxt.setText(LK.getScore()); if (LK.getScore() >= 10000) { LK.showYouWin(); return; } tween(fastEnemy, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (fastEnemy.parent) { fastEnemy.destroy(); } } }); fastEnemiesToRemove.push(fastEnemy); bulletsToRemove.push(bullet); bulletHit = true; LK.getSound('hit').play(); break; } } // Check shield enemies for (var j = shieldEnemies.length - 1; j >= 0; j--) { var shieldEnemy = shieldEnemies[j]; // Skip shield enemies outside collision zone (bounding box check) if (shieldEnemy.x < -250 || shieldEnemy.x > 2100 || shieldEnemy.y < -50 || shieldEnemy.y > 2800) { continue; } // Check bullet collision with shield enemy using distance-based detection var deltaX = bullet.x - shieldEnemy.x; var deltaY = bullet.y - shieldEnemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 90) { // 90px collision radius for shield enemies (larger due to shield) if (shieldEnemy.takeDamage()) { LK.setScore(LK.getScore() + 30); scoreTxt.setText(LK.getScore()); if (LK.getScore() >= 10000) { LK.showYouWin(); return; } tween(shieldEnemy, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { if (shieldEnemy.parent) { shieldEnemy.destroy(); } } }); shieldEnemiesToRemove.push(shieldEnemy); } else { LK.setScore(LK.getScore() + 5); scoreTxt.setText(LK.getScore()); } bulletsToRemove.push(bullet); bulletHit = true; LK.getSound('hit').play(); break; } } // Check boss enemies for (var j = bossEnemies.length - 1; j >= 0; j--) { var bossEnemy = bossEnemies[j]; // Skip boss enemies outside collision zone (bounding box check) if (bossEnemy.x < -250 || bossEnemy.x > 2100 || bossEnemy.y < -50 || bossEnemy.y > 2800) { continue; } // Check bullet collision with boss enemy using distance-based detection var deltaX = bullet.x - bossEnemy.x; var deltaY = bullet.y - bossEnemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 80) { // 80px collision radius for boss enemies (much larger due to 2x scale and boss status) if (bossEnemy.takeDamage()) { LK.setScore(LK.getScore() + 100); scoreTxt.setText(LK.getScore()); if (LK.getScore() >= 10000) { LK.showYouWin(); return; } tween(bossEnemy, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (bossEnemy.parent) { bossEnemy.destroy(); } } }); bossEnemiesToRemove.push(bossEnemy); } else { LK.setScore(LK.getScore() + 10); scoreTxt.setText(LK.getScore()); } bulletsToRemove.push(bullet); bulletHit = true; LK.getSound('hit').play(); break; } } if (bulletHit) continue; } // Update and check enemy bullets for (var m = enemyBullets.length - 1; m >= 0; m--) { var enemyBullet = enemyBullets[m]; // Mark enemy bullets that go off screen for removal if (enemyBullet.x < -50 || enemyBullet.y < -50 || enemyBullet.y > 2732 + 50) { enemyBulletsToRemove.push(enemyBullet); continue; } // Skip collision detection for enemy bullets outside meaningful collision zones if (enemyBullet.x < -50 || enemyBullet.x > 2100 || enemyBullet.y < -50 || enemyBullet.y > 2800) { continue; } // Check if enemy bullet hits player center (more precise collision) var deltaX = enemyBullet.x - player.x; var deltaY = enemyBullet.y - player.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 45) { // Only hits if within 45 pixels of player center (consistent bullet collision radius) if (playerShieldTime <= 0) { // Only take damage if not shielded playerLives--; // Update lives number display if (livesNumberTxt) { livesNumberTxt.setText(playerLives.toString()); } LK.effects.flashScreen(0xFF0000, 500); if (playerLives <= 0) { LK.showGameOver(); return; } } enemyBulletsToRemove.push(enemyBullet); } } // Update and check regular enemies for (var k = enemies.length - 1; k >= 0; k--) { var enemy = enemies[k]; // Mark enemies that go off screen for removal if (enemy.x < -200) { // Regular enemy width is 200px enemiesEscaped++; if (enemiesEscaped >= 5) { LK.showGameOver(); return; } enemiesToRemove.push(enemy); continue; } // Skip processing for enemies outside meaningful interaction zones if (enemy.x < -250 || enemy.x > 2100 || enemy.y < -50 || enemy.y > 2800) { continue; } // Calculate distance once for collision detection var deltaX = player.x - enemy.x; var deltaY = player.y - enemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Enemy shooting logic if (enemy.shoot()) { var enemyBullet = new EnemyBullet(); var bulletOffset = 10; // Additional offset beyond enemy edge enemyBullet.x = enemy.x - (enemy.width / 2 + bulletOffset); // Spawn relative to enemy width enemyBullet.y = enemy.y; enemyBullet.lastX = enemyBullet.x; enemyBullet.lastY = enemyBullet.y; // Shoot straight left - no player tracking enemyBullet.directionX = -1; enemyBullet.directionY = 0; enemyBullets.push(enemyBullet); game.addChild(enemyBullet); } // Check if enemy collides with player center using same distance calculation if (distance < 60) { // Only hits if within 60 pixels of player center (larger radius for direct contact) if (playerShieldTime <= 0) { // Only take damage if not shielded playerLives--; // Update lives number display if (livesNumberTxt) { livesNumberTxt.setText(playerLives.toString()); } LK.effects.flashScreen(0xFF0000, 500); if (playerLives <= 0) { LK.showGameOver(); return; } } enemiesToRemove.push(enemy); } } // Update and check fast enemies for (var k = fastEnemies.length - 1; k >= 0; k--) { var fastEnemy = fastEnemies[k]; // Mark fast enemies that go off screen for removal if (fastEnemy.x < -200) { // Fast enemy width is 200px enemiesEscaped++; if (enemiesEscaped >= 5) { LK.showGameOver(); return; } fastEnemiesToRemove.push(fastEnemy); continue; } // Skip processing for fast enemies outside meaningful interaction zones if (fastEnemy.x < -250 || fastEnemy.x > 2100 || fastEnemy.y < -50 || fastEnemy.y > 2800) { continue; } // Calculate distance once for collision detection var deltaX = player.x - fastEnemy.x; var deltaY = player.y - fastEnemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (fastEnemy.shoot()) { var enemyBullet = new EnemyBullet(); var bulletOffset = 10; // Additional offset beyond enemy edge enemyBullet.x = fastEnemy.x - (fastEnemy.width / 2 + bulletOffset); // Spawn relative to enemy width enemyBullet.y = fastEnemy.y; enemyBullet.lastX = enemyBullet.x; enemyBullet.lastY = enemyBullet.y; // Shoot straight left - no player tracking enemyBullet.directionX = -1; enemyBullet.directionY = 0; enemyBullets.push(enemyBullet); game.addChild(enemyBullet); } // Check if fast enemy collides with player center using same distance calculation if (distance < 65) { // Only hits if within 65 pixels of player center (slightly larger due to fast movement) if (playerShieldTime <= 0) { // Only take damage if not shielded playerLives--; // Update lives number display if (livesNumberTxt) { livesNumberTxt.setText(playerLives.toString()); } LK.effects.flashScreen(0xFF0000, 500); if (playerLives <= 0) { LK.showGameOver(); return; } } fastEnemiesToRemove.push(fastEnemy); } } // Update and check shield enemies for (var k = shieldEnemies.length - 1; k >= 0; k--) { var shieldEnemy = shieldEnemies[k]; // Mark shield enemies that go off screen for removal if (shieldEnemy.x < -200) { // Shield enemy width is 200px enemiesEscaped++; if (enemiesEscaped >= 5) { LK.showGameOver(); return; } shieldEnemiesToRemove.push(shieldEnemy); continue; } // Skip processing for shield enemies outside meaningful interaction zones if (shieldEnemy.x < -250 || shieldEnemy.x > 2100 || shieldEnemy.y < -50 || shieldEnemy.y > 2800) { continue; } // Calculate distance once for collision detection var deltaX = player.x - shieldEnemy.x; var deltaY = player.y - shieldEnemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (shieldEnemy.shoot()) { var enemyBullet = new EnemyBullet(); var bulletOffset = 10; // Additional offset beyond enemy edge enemyBullet.x = shieldEnemy.x - (shieldEnemy.width / 2 + bulletOffset); // Spawn relative to enemy width enemyBullet.y = shieldEnemy.y; enemyBullet.lastX = enemyBullet.x; enemyBullet.lastY = enemyBullet.y; // Shoot straight left - no player tracking enemyBullet.directionX = -1; enemyBullet.directionY = 0; enemyBullets.push(enemyBullet); game.addChild(enemyBullet); } // Check if shield enemy collides with player center using same distance calculation if (distance < 70) { // Only hits if within 70 pixels of player center (larger due to shield size) if (playerShieldTime <= 0) { // Only take damage if not shielded playerLives--; // Update lives number display if (livesNumberTxt) { livesNumberTxt.setText(playerLives.toString()); } LK.effects.flashScreen(0xFF0000, 500); if (playerLives <= 0) { LK.showGameOver(); return; } } shieldEnemiesToRemove.push(shieldEnemy); } } // Update and check health power-ups for (var p = healthPowerups.length - 1; p >= 0; p--) { var healthPowerup = healthPowerups[p]; // Remove if off screen if (healthPowerup.x < -100) { healthPowerupsToRemove.push(healthPowerup); continue; } // Check collision with player var deltaX = healthPowerup.x - player.x; var deltaY = healthPowerup.y - player.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 60) { // Player collected health power-up if (playerLives < 5) { // Max 5 lives playerLives++; // Update lives number display if (livesNumberTxt) { livesNumberTxt.setText(playerLives.toString()); } LK.getSound('powerup').play(); LK.effects.flashScreen(0x00ff00, 300); } healthPowerupsToRemove.push(healthPowerup); } } // Update and check shield power-ups for (var p = shieldPowerups.length - 1; p >= 0; p--) { var shieldPowerup = shieldPowerups[p]; // Remove if off screen if (shieldPowerup.x < -100) { shieldPowerupsToRemove.push(shieldPowerup); continue; } // Check collision with player var deltaX = shieldPowerup.x - player.x; var deltaY = shieldPowerup.y - player.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 60) { // Player collected shield power-up playerShieldTime = 600; // 10 seconds of invincibility LK.getSound('powerup').play(); LK.effects.flashScreen(0x0088ff, 300); shieldPowerupsToRemove.push(shieldPowerup); } } // Update and check weapon power-ups for (var p = weaponPowerups.length - 1; p >= 0; p--) { var weaponPowerup = weaponPowerups[p]; // Remove if off screen if (weaponPowerup.x < -100) { weaponPowerupsToRemove.push(weaponPowerup); continue; } // Check collision with player var deltaX = weaponPowerup.x - player.x; var deltaY = weaponPowerup.y - player.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance < 60) { // Player collected weapon power-up playerWeaponLevel = Math.min(3, playerWeaponLevel + 1); // Max level 3 playerWeaponTime = 1800; // 30 seconds of upgrade LK.getSound('powerup').play(); LK.effects.flashScreen(0xffaa00, 300); weaponPowerupsToRemove.push(weaponPowerup); } } // Update and check boss enemies for (var k = bossEnemies.length - 1; k >= 0; k--) { var bossEnemy = bossEnemies[k]; // Mark boss enemies that go off screen for removal if (bossEnemy.x < -200) { // Boss enemy width is 100px * 2 scale = 200px enemiesEscaped++; if (enemiesEscaped >= 5) { LK.showGameOver(); return; } bossEnemiesToRemove.push(bossEnemy); continue; } // Skip processing for boss enemies outside meaningful interaction zones if (bossEnemy.x < -250 || bossEnemy.x > 2100 || bossEnemy.y < -50 || bossEnemy.y > 2800) { continue; } // Calculate distance once for collision detection var deltaX = player.x - bossEnemy.x; var deltaY = player.y - bossEnemy.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (bossEnemy.shoot()) { var enemyBullet = new EnemyBullet(); var bulletOffset = 10; // Additional offset beyond enemy edge enemyBullet.x = bossEnemy.x - (bossEnemy.width / 2 + bulletOffset); // Spawn relative to enemy width (scaled) enemyBullet.y = bossEnemy.y; enemyBullet.lastX = enemyBullet.x; enemyBullet.lastY = enemyBullet.y; // Shoot straight left - no player tracking enemyBullet.directionX = -1; enemyBullet.directionY = 0; enemyBullets.push(enemyBullet); game.addChild(enemyBullet); } // Check if boss enemy collides with player center using same distance calculation if (distance < 90) { // Only hits if within 90 pixels of player center (much larger due to 2x scale and boss size) if (playerShieldTime <= 0) { // Only take damage if not shielded playerLives--; // Update lives number display if (livesNumberTxt) { livesNumberTxt.setText(playerLives.toString()); } LK.effects.flashScreen(0xFF0000, 500); if (playerLives <= 0) { LK.showGameOver(); return; } } bossEnemiesToRemove.push(bossEnemy); } } // Deferred cleanup - process all marked objects for removal using safer object-based iteration // Process bullets marked for removal for (var i = bulletsToRemove.length - 1; i >= 0; i--) { var bulletToRemove = bulletsToRemove[i]; if (bulletToRemove && bulletToRemove.parent) { var index = bullets.indexOf(bulletToRemove); if (index !== -1 && index < bullets.length) { bullets[index].cleanup(); bullets[index].destroy(); bullets.splice(index, 1); } } } bulletsToRemove = []; // Process enemy bullets marked for removal for (var i = enemyBulletsToRemove.length - 1; i >= 0; i--) { var enemyBulletToRemove = enemyBulletsToRemove[i]; if (enemyBulletToRemove && enemyBulletToRemove.parent) { var index = enemyBullets.indexOf(enemyBulletToRemove); if (index !== -1 && index < enemyBullets.length) { enemyBullets[index].cleanup(); enemyBullets[index].destroy(); enemyBullets.splice(index, 1); } } } enemyBulletsToRemove = []; // Process regular enemies marked for removal for (var i = enemiesToRemove.length - 1; i >= 0; i--) { var enemyToRemove = enemiesToRemove[i]; if (enemyToRemove && enemyToRemove.parent) { var index = enemies.indexOf(enemyToRemove); if (index !== -1 && index < enemies.length) { enemies[index].cleanup(); enemies[index].destroy(); enemies.splice(index, 1); } } } enemiesToRemove = []; // Process fast enemies marked for removal for (var i = fastEnemiesToRemove.length - 1; i >= 0; i--) { var fastEnemyToRemove = fastEnemiesToRemove[i]; if (fastEnemyToRemove && fastEnemyToRemove.parent) { var index = fastEnemies.indexOf(fastEnemyToRemove); if (index !== -1 && index < fastEnemies.length) { fastEnemies[index].cleanup(); fastEnemies[index].destroy(); fastEnemies.splice(index, 1); } } } fastEnemiesToRemove = []; // Process shield enemies marked for removal for (var i = shieldEnemiesToRemove.length - 1; i >= 0; i--) { var shieldEnemyToRemove = shieldEnemiesToRemove[i]; if (shieldEnemyToRemove && shieldEnemyToRemove.parent) { var index = shieldEnemies.indexOf(shieldEnemyToRemove); if (index !== -1 && index < shieldEnemies.length) { shieldEnemies[index].cleanup(); shieldEnemies[index].destroy(); shieldEnemies.splice(index, 1); } } } shieldEnemiesToRemove = []; // Process boss enemies marked for removal for (var i = bossEnemiesToRemove.length - 1; i >= 0; i--) { var bossEnemyToRemove = bossEnemiesToRemove[i]; if (bossEnemyToRemove && bossEnemyToRemove.parent) { var index = bossEnemies.indexOf(bossEnemyToRemove); if (index !== -1 && index < bossEnemies.length) { bossEnemies[index].cleanup(); bossEnemies[index].destroy(); bossEnemies.splice(index, 1); } } } bossEnemiesToRemove = []; // Process health power-ups marked for removal for (var i = healthPowerupsToRemove.length - 1; i >= 0; i--) { var healthPowerupToRemove = healthPowerupsToRemove[i]; if (healthPowerupToRemove && healthPowerupToRemove.parent) { var index = healthPowerups.indexOf(healthPowerupToRemove); if (index !== -1 && index < healthPowerups.length) { healthPowerups[index].cleanup(); healthPowerups[index].destroy(); healthPowerups.splice(index, 1); } } } healthPowerupsToRemove = []; // Process shield power-ups marked for removal for (var i = shieldPowerupsToRemove.length - 1; i >= 0; i--) { var shieldPowerupToRemove = shieldPowerupsToRemove[i]; if (shieldPowerupToRemove && shieldPowerupToRemove.parent) { var index = shieldPowerups.indexOf(shieldPowerupToRemove); if (index !== -1 && index < shieldPowerups.length) { shieldPowerups[index].cleanup(); shieldPowerups[index].destroy(); shieldPowerups.splice(index, 1); } } } shieldPowerupsToRemove = []; // Process weapon power-ups marked for removal for (var i = weaponPowerupsToRemove.length - 1; i >= 0; i--) { var weaponPowerupToRemove = weaponPowerupsToRemove[i]; if (weaponPowerupToRemove && weaponPowerupToRemove.parent) { var index = weaponPowerups.indexOf(weaponPowerupToRemove); if (index !== -1 && index < weaponPowerups.length) { weaponPowerups[index].cleanup(); weaponPowerups[index].destroy(); weaponPowerups.splice(index, 1); } } } weaponPowerupsToRemove = []; // Monitor array sizes and implement limits to prevent memory leaks var maxBullets = 100; var maxEnemyBullets = 200; var maxEnemies = 50; // Limit bullets array size with safety checks if (bullets.length > maxBullets) { for (var i = bullets.length - 1; i >= maxBullets; i--) { if (i < bullets.length && bullets[i]) { bullets[i].cleanup(); bullets[i].destroy(); bullets.splice(i, 1); } } } // Limit enemy bullets array size with safety checks if (enemyBullets.length > maxEnemyBullets) { for (var i = enemyBullets.length - 1; i >= maxEnemyBullets; i--) { if (i < enemyBullets.length && enemyBullets[i]) { enemyBullets[i].cleanup(); enemyBullets[i].destroy(); enemyBullets.splice(i, 1); } } } // Limit enemies array size with safety checks if (enemies.length > maxEnemies) { for (var i = enemies.length - 1; i >= maxEnemies; i--) { if (i < enemies.length && enemies[i]) { enemies[i].cleanup(); enemies[i].destroy(); enemies.splice(i, 1); } } } // Limit fast enemies array size with safety checks if (fastEnemies.length > maxEnemies) { for (var i = fastEnemies.length - 1; i >= maxEnemies; i--) { if (i < fastEnemies.length && fastEnemies[i]) { fastEnemies[i].cleanup(); fastEnemies[i].destroy(); fastEnemies.splice(i, 1); } } } // Limit shield enemies array size with safety checks if (shieldEnemies.length > maxEnemies) { for (var i = shieldEnemies.length - 1; i >= maxEnemies; i--) { if (i < shieldEnemies.length && shieldEnemies[i]) { shieldEnemies[i].cleanup(); shieldEnemies[i].destroy(); shieldEnemies.splice(i, 1); } } } // Limit boss enemies array size with safety checks if (bossEnemies.length > maxEnemies) { for (var i = bossEnemies.length - 1; i >= maxEnemies; i--) { if (i < bossEnemies.length && bossEnemies[i]) { bossEnemies[i].cleanup(); bossEnemies[i].destroy(); bossEnemies.splice(i, 1); } } } }; // Play background music LK.playMusic('bgmusic');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BossEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('jefe', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.speed = -1; // Very slow
self.lastX = self.x; // Initialize in constructor
self.lastY = self.y; // Initialize in constructor
self.shootCooldown = 0;
self.health = 10; // Takes 10 hits to destroy
self.verticalSpeed = 2;
self.verticalDirection = 1;
self.isFirstFrame = true; // Initialization flag
self.update = function () {
// Skip first frame logic to ensure proper initialization
if (self.isFirstFrame) {
self.isFirstFrame = false;
return; // Skip first frame to avoid premature trigger
}
// Move enemy left
self.x += self.speed;
// Move vertically up and down
self.y += self.verticalSpeed * self.verticalDirection;
// Calculate bounds based on scaled boss height to keep it fully visible
var margin = 50; // Safety margin
var scaledHeight = 126 * 2; // Original height * scale factor
var topBound = scaledHeight / 2 + margin;
var bottomBound = 2732 - scaledHeight / 2 - margin;
if (self.y <= topBound || self.y >= bottomBound) {
self.verticalDirection *= -1;
}
// Update shooting cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update last values for next frame
self.lastX = self.x;
self.lastY = self.y;
};
self.canShoot = function () {
return self.shootCooldown <= 0;
};
self.shoot = function () {
if (self.canShoot()) {
self.shootCooldown = 60; // Slower shooting
return true;
}
return false;
};
self.takeDamage = function () {
self.health--;
// Visual feedback for taking damage
enemyGraphics.alpha = Math.max(0.3, Math.min(1.0, 0.3 + self.health / 10 * 0.7));
return self.health <= 0;
};
self.cleanup = function () {
// Clear any references and reset properties
self.lastX = undefined;
self.lastY = undefined;
self.shootCooldown = 0;
self.speed = 0;
self.health = 0;
self.verticalSpeed = 0;
self.verticalDirection = 1;
self.isFirstFrame = true;
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.lastX = self.x; // Initialize in constructor
self.lastY = self.y; // Initialize in constructor
self.directionX = 1; // Default horizontal direction
self.directionY = 0; // Default no vertical movement
self.update = function () {
// Null checks for safety
if (self.lastX === null || self.lastX === undefined) self.lastX = self.x;
if (self.lastY === null || self.lastY === undefined) self.lastY = self.y;
// Set bullet rotation based on direction
var angle = Math.atan2(self.directionY, self.directionX);
bulletGraphics.rotation = angle;
self.x += self.speed * self.directionX;
self.y += self.speed * self.directionY;
self.lastX = self.x;
self.lastY = self.y;
};
self.cleanup = function () {
// Clear any references and reset properties
self.lastX = undefined;
self.lastY = undefined;
self.speed = 0;
self.directionX = 0;
self.directionY = 0;
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var CharacterSelectionScreen = Container.expand(function () {
var self = Container.call(this);
// Create title image
var titleImage = self.attachAsset('titulopersonajes', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 400,
scaleX: 4,
scaleY: 4
});
// Create character images
var player1Image = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 600,
y: 1000,
scaleX: 1.5,
scaleY: 1.5
});
var player2Image = self.attachAsset('jugador2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 200,
y: 1000,
scaleX: 1.5,
scaleY: 1.5
});
var player3Image = self.attachAsset('jugador3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 200,
y: 1000,
scaleX: 1.5,
scaleY: 1.5
});
var player4Image = self.attachAsset('jugador4', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 600,
y: 1000,
scaleX: 1.5,
scaleY: 1.5
});
// Create play button image
var playImage = self.attachAsset('jugar', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 1400,
scaleX: 2,
scaleY: 2
});
// Store references for event handling
self.player1Image = player1Image;
self.player2Image = player2Image;
self.player3Image = player3Image;
self.player4Image = player4Image;
self.playButton = playImage;
self.selectedCharacter = 'player'; // Default selection
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -3;
self.lastX = self.x; // Initialize in constructor
self.shootCooldown = 0;
self.isFirstFrame = true; // Initialization flag
self.update = function () {
// Skip first frame logic to ensure proper initialization
if (self.isFirstFrame) {
self.isFirstFrame = false;
return; // Skip first frame to avoid premature trigger
}
// Move enemy left
self.x += self.speed;
// Update shooting cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update lastX for next frame
self.lastX = self.x;
};
self.canShoot = function () {
return self.shootCooldown <= 0;
};
self.shoot = function () {
if (self.canShoot()) {
self.shootCooldown = 180; // 3 second cooldown
return true;
}
return false;
};
self.cleanup = function () {
// Clear any references and reset properties
self.lastX = undefined;
self.shootCooldown = 0;
self.speed = 0;
self.isFirstFrame = true;
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.lastX = self.x; // Initialize in constructor
self.lastY = self.y; // Initialize in constructor
self.directionX = -1; // Default moving left
self.directionY = 0; // Default no vertical movement
self.update = function () {
// Null checks for safety
if (self.lastX === null || self.lastX === undefined) self.lastX = self.x;
if (self.lastY === null || self.lastY === undefined) self.lastY = self.y;
// Set bullet rotation based on direction
var angle = Math.atan2(self.directionY, self.directionX);
bulletGraphics.rotation = angle;
self.x += self.speed * self.directionX;
self.y += self.speed * self.directionY;
self.lastX = self.x;
self.lastY = self.y;
};
self.cleanup = function () {
// Clear any references and reset properties
self.lastX = undefined;
self.lastY = undefined;
self.speed = 0;
self.directionX = 0;
self.directionY = 0;
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var FastEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemigorapido', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -6; // Faster than regular enemy
self.lastX = self.x; // Initialize in constructor
self.shootCooldown = 0;
self.isFirstFrame = true; // Initialization flag
self.update = function () {
// Skip first frame logic to ensure proper initialization
if (self.isFirstFrame) {
self.isFirstFrame = false;
return; // Skip first frame to avoid premature trigger
}
// Move enemy left
self.x += self.speed;
// Update shooting cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update lastX for next frame
self.lastX = self.x;
};
self.canShoot = function () {
return self.shootCooldown <= 0;
};
self.shoot = function () {
if (self.canShoot()) {
self.shootCooldown = 120; // 2 second cooldown
return true;
}
return false;
};
self.cleanup = function () {
// Clear any references and reset properties
self.lastX = undefined;
self.shootCooldown = 0;
self.speed = 0;
self.isFirstFrame = true;
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var HealthPowerup = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('healthPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.lastX = self.x;
self.lastY = self.y;
self.bobOffset = 0;
self.isFirstFrame = true;
self.update = function () {
if (self.isFirstFrame) {
self.isFirstFrame = false;
return;
}
// Move left
self.x += self.speed;
// Bob up and down for visual appeal
self.bobOffset += 0.1;
powerupGraphics.y = Math.sin(self.bobOffset) * 10;
// Gentle rotation
powerupGraphics.rotation += 0.02;
self.lastX = self.x;
self.lastY = self.y;
};
self.cleanup = function () {
self.lastX = undefined;
self.lastY = undefined;
self.speed = 0;
self.isFirstFrame = true;
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset(selectedPlayerAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.shootCooldown = 0;
self.update = function () {
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update shield visual effect
if (playerShieldTime > 0) {
// Pulsing blue tint when shielded
var pulse = Math.sin(LK.ticks * 0.3) * 0.3 + 0.7;
playerGraphics.tint = 0x4488ff;
playerGraphics.alpha = pulse;
} else {
// Normal appearance
playerGraphics.tint = 0xffffff;
playerGraphics.alpha = 1.0;
}
};
self.canShoot = function () {
return self.shootCooldown <= 0;
};
self.shoot = function () {
if (self.canShoot()) {
// Faster shooting with weapon upgrades
var cooldown = Math.max(5, 10 - (playerWeaponLevel - 1) * 2);
self.shootCooldown = cooldown;
return playerWeaponLevel; // Return weapon level for multi-shot
}
return 0;
};
return self;
});
var ShieldEnemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemigoescudo', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2; // Slower than regular enemy
self.lastX = self.x; // Initialize in constructor
self.shootCooldown = 0;
self.health = 3; // Takes 3 hits to destroy
self.isFirstFrame = true; // Initialization flag
self.update = function () {
// Skip first frame logic to ensure proper initialization
if (self.isFirstFrame) {
self.isFirstFrame = false;
return; // Skip first frame to avoid premature trigger
}
// Move enemy left
self.x += self.speed;
// Update shooting cooldown
if (self.shootCooldown > 0) {
self.shootCooldown--;
}
// Update lastX for next frame
self.lastX = self.x;
};
self.canShoot = function () {
return self.shootCooldown <= 0;
};
self.shoot = function () {
if (self.canShoot()) {
self.shootCooldown = 150; // Much slower shooting
return true;
}
return false;
};
self.takeDamage = function () {
self.health--;
// Visual feedback for taking damage
enemyGraphics.alpha = Math.max(0.5, Math.min(1.0, 0.5 + self.health / 3 * 0.5));
return self.health <= 0;
};
self.cleanup = function () {
// Clear any references and reset properties
self.lastX = undefined;
self.shootCooldown = 0;
self.speed = 0;
self.health = 0;
self.isFirstFrame = true;
// Remove from parent if still attached
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var ShieldPowerup = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('shieldPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.lastX = self.x;
self.lastY = self.y;
self.bobOffset = 0;
self.isFirstFrame = true;
self.update = function () {
if (self.isFirstFrame) {
self.isFirstFrame = false;
return;
}
// Move left
self.x += self.speed;
// Bob up and down for visual appeal
self.bobOffset += 0.15;
powerupGraphics.y = Math.sin(self.bobOffset) * 15;
// Pulsing effect
var pulse = Math.sin(self.bobOffset * 2) * 0.2 + 1.0;
powerupGraphics.scaleX = pulse;
powerupGraphics.scaleY = pulse;
self.lastX = self.x;
self.lastY = self.y;
};
self.cleanup = function () {
self.lastX = undefined;
self.lastY = undefined;
self.speed = 0;
self.isFirstFrame = true;
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
var StartScreen = Container.expand(function () {
var self = Container.call(this);
// Create title image
var titleImage = self.attachAsset('titulo', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 - 450,
scaleX: 8,
scaleY: 8
});
// Create play button image
var playImage = self.attachAsset('jugar', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 + 50,
scaleX: 2,
scaleY: 2
});
// Create character selection button image
var characterImage = self.attachAsset('seleccionpersonaje', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 + 450,
scaleX: 2,
scaleY: 2
});
// Store button references for event handling
self.playButton = playImage;
self.characterButton = characterImage;
return self;
});
var WeaponPowerup = Container.expand(function () {
var self = Container.call(this);
var powerupGraphics = self.attachAsset('weaponPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -2;
self.lastX = self.x;
self.lastY = self.y;
self.bobOffset = 0;
self.isFirstFrame = true;
self.update = function () {
if (self.isFirstFrame) {
self.isFirstFrame = false;
return;
}
// Move left
self.x += self.speed;
// Bob up and down for visual appeal
self.bobOffset += 0.12;
powerupGraphics.y = Math.sin(self.bobOffset) * 12;
// Spinning effect
powerupGraphics.rotation += 0.05;
self.lastX = self.x;
self.lastY = self.y;
};
self.cleanup = function () {
self.lastX = undefined;
self.lastY = undefined;
self.speed = 0;
self.isFirstFrame = true;
if (self.parent) {
self.parent.removeChild(self);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a2e
});
/****
* Game Code
****/
// Game state variables
var gameStarted = false;
var startScreen = null;
var characterSelectionScreen = null;
var selectedPlayerAsset = 'player'; // Track which character asset to use
var player = null;
var enemies = [];
var fastEnemies = [];
var shieldEnemies = [];
var bossEnemies = [];
var bullets = [];
var enemyBullets = [];
var enemySpawnTimer = 0;
var enemySpawnRate = 120; // Start spawning every 2 seconds (120 frames)
var difficultyTimer = 0;
var playerLives = 3;
var playerShieldTime = 0; // Shield duration in frames
var playerWeaponLevel = 1; // Weapon upgrade level (1-3)
var playerWeaponTime = 0; // Weapon upgrade duration in frames
// Power-up arrays
var healthPowerups = [];
var shieldPowerups = [];
var weaponPowerups = [];
var powerupSpawnTimer = 0;
var powerupSpawnRate = 180; // Spawn every 3 seconds
var enemiesEscaped = 0; // Track how many enemies have escaped
var bossTimer = 0; // Track time for boss battle cycles (2 minutes = 7200 frames)
var bossPhase = false; // Track if we're in boss phase
var bossesSpawned = 0; // Track how many bosses have been spawned in current phase
// Arrays to track objects marked for removal (using object references for safer iteration)
var bulletsToRemove = [];
var healthPowerupsToRemove = [];
var shieldPowerupsToRemove = [];
var weaponPowerupsToRemove = [];
var enemyBulletsToRemove = [];
var enemiesToRemove = [];
var fastEnemiesToRemove = [];
var shieldEnemiesToRemove = [];
var bossEnemiesToRemove = [];
var isDragging = false;
var scoreTxt = null;
var livesTxt = null;
var livesNumberTxt = null;
// Add background image
var backgroundImage = game.attachAsset('fondo', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Initialize start screen
startScreen = new StartScreen();
game.addChild(startScreen);
function showCharacterSelection() {
// Remove start screen
if (startScreen && startScreen.parent) {
startScreen.destroy();
startScreen = null;
}
// Show character selection screen
characterSelectionScreen = new CharacterSelectionScreen();
game.addChild(characterSelectionScreen);
}
function startGame() {
gameStarted = true;
// Remove start screen
if (startScreen && startScreen.parent) {
startScreen.destroy();
startScreen = null;
}
// Remove character selection screen
if (characterSelectionScreen && characterSelectionScreen.parent) {
characterSelectionScreen.destroy();
characterSelectionScreen = null;
}
// Initialize player with selected character asset
player = new Player();
player.x = 300;
player.y = 2732 - 150; // Near bottom with some margin
game.addChild(player);
// Create score display
scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.setText(LK.getScore());
// Create lives display with image
livesTxt = LK.getAsset('vidas', {
anchorX: 0,
anchorY: 0,
x: 120,
y: 20
});
LK.gui.topLeft.addChild(livesTxt);
// Create lives number display
livesNumberTxt = new Text2(playerLives.toString(), {
size: 80,
fill: 0xFFFFFF
});
livesNumberTxt.anchor.set(0, 0.5);
livesNumberTxt.x = 330; // Position to the right of vidas image
livesNumberTxt.y = 77; // Center vertically with vidas image
LK.gui.topLeft.addChild(livesNumberTxt);
}
// Touch/mouse down - handle start screen or game play
game.down = function (x, y, obj) {
if (!gameStarted) {
// Handle start screen
if (startScreen) {
// Check if play button was clicked
if (startScreen.playButton) {
var deltaX = x - startScreen.playButton.x;
var deltaY = y - startScreen.playButton.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 150) {
// Button click radius
startGame();
return;
}
}
// Check if character selection button was clicked
if (startScreen.characterButton) {
var deltaX = x - startScreen.characterButton.x;
var deltaY = y - startScreen.characterButton.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 200) {
// Button click radius
showCharacterSelection();
return;
}
}
return;
}
// Handle character selection screen
if (characterSelectionScreen) {
// Check character image clicks
if (characterSelectionScreen.player1Image) {
var deltaX = x - characterSelectionScreen.player1Image.x;
var deltaY = y - characterSelectionScreen.player1Image.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 150) {
selectedPlayerAsset = 'player';
characterSelectionScreen.selectedCharacter = 'player';
return;
}
}
if (characterSelectionScreen.player2Image) {
var deltaX = x - characterSelectionScreen.player2Image.x;
var deltaY = y - characterSelectionScreen.player2Image.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 150) {
selectedPlayerAsset = 'jugador2';
characterSelectionScreen.selectedCharacter = 'jugador2';
return;
}
}
if (characterSelectionScreen.player3Image) {
var deltaX = x - characterSelectionScreen.player3Image.x;
var deltaY = y - characterSelectionScreen.player3Image.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 150) {
selectedPlayerAsset = 'jugador3';
characterSelectionScreen.selectedCharacter = 'jugador3';
return;
}
}
if (characterSelectionScreen.player4Image) {
var deltaX = x - characterSelectionScreen.player4Image.x;
var deltaY = y - characterSelectionScreen.player4Image.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 150) {
selectedPlayerAsset = 'jugador4';
characterSelectionScreen.selectedCharacter = 'jugador4';
return;
}
}
// Check if play button was clicked
if (characterSelectionScreen.playButton) {
var deltaX = x - characterSelectionScreen.playButton.x;
var deltaY = y - characterSelectionScreen.playButton.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 150) {
// Button click radius
startGame();
return;
}
}
return;
}
}
isDragging = true;
// Move player to touch position (constrain to vertical movement)
player.y = Math.max(100, Math.min(2732 - 100, y));
var weaponLevel = player.shoot();
if (weaponLevel > 0) {
// Calculate direction vector from player to tap position
var deltaX = x - player.x;
var deltaY = y - player.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var directionX = 1;
var directionY = 0;
if (distance > 1.0) {
directionX = deltaX / distance;
directionY = deltaY / distance;
}
// Create bullets based on weapon level
for (var w = 0; w < weaponLevel; w++) {
var bullet = new Bullet();
bullet.x = player.x + 40; // Spawn slightly ahead of player
bullet.y = player.y - 40 + (w - Math.floor(weaponLevel / 2)) * 20; // Spread bullets vertically
bullet.lastX = bullet.x;
bullet.lastY = bullet.y;
// Slight angle variation for multiple bullets
var angleOffset = (w - Math.floor(weaponLevel / 2)) * 0.2;
var cos = Math.cos(angleOffset);
var sin = Math.sin(angleOffset);
bullet.directionX = directionX * cos - directionY * sin;
bullet.directionY = directionX * sin + directionY * cos;
bullets.push(bullet);
game.addChild(bullet);
}
LK.getSound('shoot').play();
}
};
// Touch/mouse move - move player vertically while dragging
game.move = function (x, y, obj) {
if (!gameStarted || characterSelectionScreen) return;
if (isDragging) {
// Move player to touch position (constrain to vertical movement)
player.y = Math.max(100, Math.min(2732 - 100, y));
}
};
// Touch/mouse up - stop dragging
game.up = function (x, y, obj) {
if (!gameStarted || characterSelectionScreen) return;
isDragging = false;
};
game.update = function () {
// Only update game logic if game has started and not in character selection
if (!gameStarted || characterSelectionScreen) return;
// Update enemy spawn timer and difficulty
enemySpawnTimer++;
difficultyTimer++;
powerupSpawnTimer++;
// Update power-up timers
if (playerShieldTime > 0) {
playerShieldTime--;
}
if (playerWeaponTime > 0) {
playerWeaponTime--;
if (playerWeaponTime <= 0) {
playerWeaponLevel = 1; // Reset to normal weapon
}
}
// Increase difficulty every 10 seconds (600 frames)
if (difficultyTimer % 600 === 0 && enemySpawnRate > 30) {
enemySpawnRate -= 10; // Spawn enemies more frequently
}
// Update boss timer (always increment)
bossTimer++;
// Check for boss battle cycle (every 1 minute = 3600 frames)
if (bossTimer >= 3600) {
bossPhase = true;
bossesSpawned = 0;
}
// Spawn enemies - normal enemies spawn during non-boss phase OR after bosses are defeated
if (!bossPhase || bossPhase && bossesSpawned >= 2 && bossEnemies.length === 0) {
// Normal enemy spawning during non-boss phase or after boss defeat
if (enemySpawnTimer >= enemySpawnRate) {
var spawnType = Math.random();
// Define three spawn positions: top-right, center-right, bottom-right
var spawnPositions = [400,
// Top-right corner (with margin, lowered)
1566,
// Center-right (middle of screen, lowered)
2432 // Bottom-right corner (with margin)
];
var spawnY = spawnPositions[Math.floor(Math.random() * 3)];
if (spawnType < 0.6) {
// 60% chance for regular enemy
var enemy = new Enemy();
enemy.x = 2048 + 30;
enemy.y = spawnY;
enemy.lastX = enemy.x;
enemies.push(enemy);
game.addChild(enemy);
} else if (spawnType < 0.8) {
// 20% chance for fast enemy
var fastEnemy = new FastEnemy();
fastEnemy.x = 2048 + 30;
fastEnemy.y = spawnY;
fastEnemy.lastX = fastEnemy.x;
fastEnemies.push(fastEnemy);
game.addChild(fastEnemy);
} else {
// 20% chance for shield enemy
var shieldEnemy = new ShieldEnemy();
shieldEnemy.x = 2048 + 30;
shieldEnemy.y = spawnY;
shieldEnemy.lastX = shieldEnemy.x;
shieldEnemies.push(shieldEnemy);
game.addChild(shieldEnemy);
}
enemySpawnTimer = 0;
}
} else if (bossPhase && bossesSpawned < 2) {
// Boss phase - spawn 2 bosses then wait for them to be defeated
if (enemySpawnTimer >= 60) {
// Small delay between boss spawns
// Define three spawn positions for bosses: top-right, center-right, bottom-right
var bossSpawnPositions = [400,
// Top-right corner (with margin, lowered)
1566,
// Center-right (middle of screen, lowered)
2432 // Bottom-right corner (with margin)
];
var spawnY1 = bossSpawnPositions[Math.floor(Math.random() * 3)]; // First boss position
var spawnY2 = bossSpawnPositions[Math.floor(Math.random() * 3)]; // Second boss position
// Ensure bosses don't spawn at the same position
while (spawnY2 === spawnY1) {
spawnY2 = bossSpawnPositions[Math.floor(Math.random() * 3)];
}
// Spawn first boss
var bossEnemy1 = new BossEnemy();
bossEnemy1.x = 2048 + 30;
bossEnemy1.y = spawnY1;
bossEnemy1.lastX = bossEnemy1.x;
bossEnemies.push(bossEnemy1);
game.addChild(bossEnemy1);
// Spawn second boss
var bossEnemy2 = new BossEnemy();
bossEnemy2.x = 2048 + 30;
bossEnemy2.y = spawnY2;
bossEnemy2.lastX = bossEnemy2.x;
bossEnemies.push(bossEnemy2);
game.addChild(bossEnemy2);
bossesSpawned = 2;
enemySpawnTimer = 0;
}
}
// Check if boss phase should end (when timer reaches next cycle)
if (bossPhase && bossTimer >= 3600) {
bossPhase = false;
bossTimer = 0; // Reset timer for next boss cycle
}
// Spawn power-ups
if (powerupSpawnTimer >= powerupSpawnRate) {
var powerupType = Math.random();
var spawnY = Math.random() * (2732 - 200) + 100;
if (powerupType < 0.4) {
// 40% chance for health power-up
var healthPowerup = new HealthPowerup();
healthPowerup.x = 2048 + 30;
healthPowerup.y = spawnY;
healthPowerup.lastX = healthPowerup.x;
healthPowerup.lastY = healthPowerup.y;
healthPowerups.push(healthPowerup);
game.addChild(healthPowerup);
} else if (powerupType < 0.7) {
// 30% chance for shield power-up
var shieldPowerup = new ShieldPowerup();
shieldPowerup.x = 2048 + 30;
shieldPowerup.y = spawnY;
shieldPowerup.lastX = shieldPowerup.x;
shieldPowerup.lastY = shieldPowerup.y;
shieldPowerups.push(shieldPowerup);
game.addChild(shieldPowerup);
} else {
// 30% chance for weapon power-up
var weaponPowerup = new WeaponPowerup();
weaponPowerup.x = 2048 + 30;
weaponPowerup.y = spawnY;
weaponPowerup.lastX = weaponPowerup.x;
weaponPowerup.lastY = weaponPowerup.y;
weaponPowerups.push(weaponPowerup);
game.addChild(weaponPowerup);
}
powerupSpawnTimer = 0;
}
// Update and check bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
// Mark bullets that go off screen for removal
if (bullet.x > 2048 + 50 || bullet.y < -50) {
bulletsToRemove.push(bullet);
continue;
}
// Skip collision detection for bullets outside meaningful collision zones
if (bullet.x < -50 || bullet.x > 2100 || bullet.y < -50 || bullet.y > 2800) {
continue;
}
// Check bullet vs enemy collisions
var bulletHit = false;
// Check regular enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
// Skip enemies outside collision zone (bounding box check)
if (enemy.x < -250 || enemy.x > 2100 || enemy.y < -50 || enemy.y > 2800) {
continue;
}
// Check bullet collision with regular enemy using distance-based detection
var deltaX = bullet.x - enemy.x;
var deltaY = bullet.y - enemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 80) {
// 80px collision radius for regular enemies
// Bullet hit enemy
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
// Check for victory condition
if (LK.getScore() >= 10000) {
LK.showYouWin();
return;
}
// Visual feedback
tween(enemy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (enemy.parent) {
enemy.destroy();
}
}
});
enemiesToRemove.push(enemy);
bulletsToRemove.push(bullet);
bulletHit = true;
LK.getSound('hit').play();
break;
}
}
// Check fast enemies
for (var j = fastEnemies.length - 1; j >= 0; j--) {
var fastEnemy = fastEnemies[j];
// Skip fast enemies outside collision zone (bounding box check)
if (fastEnemy.x < -250 || fastEnemy.x > 2100 || fastEnemy.y < -50 || fastEnemy.y > 2800) {
continue;
}
// Check bullet collision with fast enemy using distance-based detection
var deltaX = bullet.x - fastEnemy.x;
var deltaY = bullet.y - fastEnemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 85) {
// 85px collision radius for fast enemies
LK.setScore(LK.getScore() + 15);
scoreTxt.setText(LK.getScore());
if (LK.getScore() >= 10000) {
LK.showYouWin();
return;
}
tween(fastEnemy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (fastEnemy.parent) {
fastEnemy.destroy();
}
}
});
fastEnemiesToRemove.push(fastEnemy);
bulletsToRemove.push(bullet);
bulletHit = true;
LK.getSound('hit').play();
break;
}
}
// Check shield enemies
for (var j = shieldEnemies.length - 1; j >= 0; j--) {
var shieldEnemy = shieldEnemies[j];
// Skip shield enemies outside collision zone (bounding box check)
if (shieldEnemy.x < -250 || shieldEnemy.x > 2100 || shieldEnemy.y < -50 || shieldEnemy.y > 2800) {
continue;
}
// Check bullet collision with shield enemy using distance-based detection
var deltaX = bullet.x - shieldEnemy.x;
var deltaY = bullet.y - shieldEnemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 90) {
// 90px collision radius for shield enemies (larger due to shield)
if (shieldEnemy.takeDamage()) {
LK.setScore(LK.getScore() + 30);
scoreTxt.setText(LK.getScore());
if (LK.getScore() >= 10000) {
LK.showYouWin();
return;
}
tween(shieldEnemy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (shieldEnemy.parent) {
shieldEnemy.destroy();
}
}
});
shieldEnemiesToRemove.push(shieldEnemy);
} else {
LK.setScore(LK.getScore() + 5);
scoreTxt.setText(LK.getScore());
}
bulletsToRemove.push(bullet);
bulletHit = true;
LK.getSound('hit').play();
break;
}
}
// Check boss enemies
for (var j = bossEnemies.length - 1; j >= 0; j--) {
var bossEnemy = bossEnemies[j];
// Skip boss enemies outside collision zone (bounding box check)
if (bossEnemy.x < -250 || bossEnemy.x > 2100 || bossEnemy.y < -50 || bossEnemy.y > 2800) {
continue;
}
// Check bullet collision with boss enemy using distance-based detection
var deltaX = bullet.x - bossEnemy.x;
var deltaY = bullet.y - bossEnemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 80) {
// 80px collision radius for boss enemies (much larger due to 2x scale and boss status)
if (bossEnemy.takeDamage()) {
LK.setScore(LK.getScore() + 100);
scoreTxt.setText(LK.getScore());
if (LK.getScore() >= 10000) {
LK.showYouWin();
return;
}
tween(bossEnemy, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
if (bossEnemy.parent) {
bossEnemy.destroy();
}
}
});
bossEnemiesToRemove.push(bossEnemy);
} else {
LK.setScore(LK.getScore() + 10);
scoreTxt.setText(LK.getScore());
}
bulletsToRemove.push(bullet);
bulletHit = true;
LK.getSound('hit').play();
break;
}
}
if (bulletHit) continue;
}
// Update and check enemy bullets
for (var m = enemyBullets.length - 1; m >= 0; m--) {
var enemyBullet = enemyBullets[m];
// Mark enemy bullets that go off screen for removal
if (enemyBullet.x < -50 || enemyBullet.y < -50 || enemyBullet.y > 2732 + 50) {
enemyBulletsToRemove.push(enemyBullet);
continue;
}
// Skip collision detection for enemy bullets outside meaningful collision zones
if (enemyBullet.x < -50 || enemyBullet.x > 2100 || enemyBullet.y < -50 || enemyBullet.y > 2800) {
continue;
}
// Check if enemy bullet hits player center (more precise collision)
var deltaX = enemyBullet.x - player.x;
var deltaY = enemyBullet.y - player.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 45) {
// Only hits if within 45 pixels of player center (consistent bullet collision radius)
if (playerShieldTime <= 0) {
// Only take damage if not shielded
playerLives--;
// Update lives number display
if (livesNumberTxt) {
livesNumberTxt.setText(playerLives.toString());
}
LK.effects.flashScreen(0xFF0000, 500);
if (playerLives <= 0) {
LK.showGameOver();
return;
}
}
enemyBulletsToRemove.push(enemyBullet);
}
}
// Update and check regular enemies
for (var k = enemies.length - 1; k >= 0; k--) {
var enemy = enemies[k];
// Mark enemies that go off screen for removal
if (enemy.x < -200) {
// Regular enemy width is 200px
enemiesEscaped++;
if (enemiesEscaped >= 5) {
LK.showGameOver();
return;
}
enemiesToRemove.push(enemy);
continue;
}
// Skip processing for enemies outside meaningful interaction zones
if (enemy.x < -250 || enemy.x > 2100 || enemy.y < -50 || enemy.y > 2800) {
continue;
}
// Calculate distance once for collision detection
var deltaX = player.x - enemy.x;
var deltaY = player.y - enemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Enemy shooting logic
if (enemy.shoot()) {
var enemyBullet = new EnemyBullet();
var bulletOffset = 10; // Additional offset beyond enemy edge
enemyBullet.x = enemy.x - (enemy.width / 2 + bulletOffset); // Spawn relative to enemy width
enemyBullet.y = enemy.y;
enemyBullet.lastX = enemyBullet.x;
enemyBullet.lastY = enemyBullet.y;
// Shoot straight left - no player tracking
enemyBullet.directionX = -1;
enemyBullet.directionY = 0;
enemyBullets.push(enemyBullet);
game.addChild(enemyBullet);
}
// Check if enemy collides with player center using same distance calculation
if (distance < 60) {
// Only hits if within 60 pixels of player center (larger radius for direct contact)
if (playerShieldTime <= 0) {
// Only take damage if not shielded
playerLives--;
// Update lives number display
if (livesNumberTxt) {
livesNumberTxt.setText(playerLives.toString());
}
LK.effects.flashScreen(0xFF0000, 500);
if (playerLives <= 0) {
LK.showGameOver();
return;
}
}
enemiesToRemove.push(enemy);
}
}
// Update and check fast enemies
for (var k = fastEnemies.length - 1; k >= 0; k--) {
var fastEnemy = fastEnemies[k];
// Mark fast enemies that go off screen for removal
if (fastEnemy.x < -200) {
// Fast enemy width is 200px
enemiesEscaped++;
if (enemiesEscaped >= 5) {
LK.showGameOver();
return;
}
fastEnemiesToRemove.push(fastEnemy);
continue;
}
// Skip processing for fast enemies outside meaningful interaction zones
if (fastEnemy.x < -250 || fastEnemy.x > 2100 || fastEnemy.y < -50 || fastEnemy.y > 2800) {
continue;
}
// Calculate distance once for collision detection
var deltaX = player.x - fastEnemy.x;
var deltaY = player.y - fastEnemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (fastEnemy.shoot()) {
var enemyBullet = new EnemyBullet();
var bulletOffset = 10; // Additional offset beyond enemy edge
enemyBullet.x = fastEnemy.x - (fastEnemy.width / 2 + bulletOffset); // Spawn relative to enemy width
enemyBullet.y = fastEnemy.y;
enemyBullet.lastX = enemyBullet.x;
enemyBullet.lastY = enemyBullet.y;
// Shoot straight left - no player tracking
enemyBullet.directionX = -1;
enemyBullet.directionY = 0;
enemyBullets.push(enemyBullet);
game.addChild(enemyBullet);
}
// Check if fast enemy collides with player center using same distance calculation
if (distance < 65) {
// Only hits if within 65 pixels of player center (slightly larger due to fast movement)
if (playerShieldTime <= 0) {
// Only take damage if not shielded
playerLives--;
// Update lives number display
if (livesNumberTxt) {
livesNumberTxt.setText(playerLives.toString());
}
LK.effects.flashScreen(0xFF0000, 500);
if (playerLives <= 0) {
LK.showGameOver();
return;
}
}
fastEnemiesToRemove.push(fastEnemy);
}
}
// Update and check shield enemies
for (var k = shieldEnemies.length - 1; k >= 0; k--) {
var shieldEnemy = shieldEnemies[k];
// Mark shield enemies that go off screen for removal
if (shieldEnemy.x < -200) {
// Shield enemy width is 200px
enemiesEscaped++;
if (enemiesEscaped >= 5) {
LK.showGameOver();
return;
}
shieldEnemiesToRemove.push(shieldEnemy);
continue;
}
// Skip processing for shield enemies outside meaningful interaction zones
if (shieldEnemy.x < -250 || shieldEnemy.x > 2100 || shieldEnemy.y < -50 || shieldEnemy.y > 2800) {
continue;
}
// Calculate distance once for collision detection
var deltaX = player.x - shieldEnemy.x;
var deltaY = player.y - shieldEnemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (shieldEnemy.shoot()) {
var enemyBullet = new EnemyBullet();
var bulletOffset = 10; // Additional offset beyond enemy edge
enemyBullet.x = shieldEnemy.x - (shieldEnemy.width / 2 + bulletOffset); // Spawn relative to enemy width
enemyBullet.y = shieldEnemy.y;
enemyBullet.lastX = enemyBullet.x;
enemyBullet.lastY = enemyBullet.y;
// Shoot straight left - no player tracking
enemyBullet.directionX = -1;
enemyBullet.directionY = 0;
enemyBullets.push(enemyBullet);
game.addChild(enemyBullet);
}
// Check if shield enemy collides with player center using same distance calculation
if (distance < 70) {
// Only hits if within 70 pixels of player center (larger due to shield size)
if (playerShieldTime <= 0) {
// Only take damage if not shielded
playerLives--;
// Update lives number display
if (livesNumberTxt) {
livesNumberTxt.setText(playerLives.toString());
}
LK.effects.flashScreen(0xFF0000, 500);
if (playerLives <= 0) {
LK.showGameOver();
return;
}
}
shieldEnemiesToRemove.push(shieldEnemy);
}
}
// Update and check health power-ups
for (var p = healthPowerups.length - 1; p >= 0; p--) {
var healthPowerup = healthPowerups[p];
// Remove if off screen
if (healthPowerup.x < -100) {
healthPowerupsToRemove.push(healthPowerup);
continue;
}
// Check collision with player
var deltaX = healthPowerup.x - player.x;
var deltaY = healthPowerup.y - player.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 60) {
// Player collected health power-up
if (playerLives < 5) {
// Max 5 lives
playerLives++;
// Update lives number display
if (livesNumberTxt) {
livesNumberTxt.setText(playerLives.toString());
}
LK.getSound('powerup').play();
LK.effects.flashScreen(0x00ff00, 300);
}
healthPowerupsToRemove.push(healthPowerup);
}
}
// Update and check shield power-ups
for (var p = shieldPowerups.length - 1; p >= 0; p--) {
var shieldPowerup = shieldPowerups[p];
// Remove if off screen
if (shieldPowerup.x < -100) {
shieldPowerupsToRemove.push(shieldPowerup);
continue;
}
// Check collision with player
var deltaX = shieldPowerup.x - player.x;
var deltaY = shieldPowerup.y - player.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 60) {
// Player collected shield power-up
playerShieldTime = 600; // 10 seconds of invincibility
LK.getSound('powerup').play();
LK.effects.flashScreen(0x0088ff, 300);
shieldPowerupsToRemove.push(shieldPowerup);
}
}
// Update and check weapon power-ups
for (var p = weaponPowerups.length - 1; p >= 0; p--) {
var weaponPowerup = weaponPowerups[p];
// Remove if off screen
if (weaponPowerup.x < -100) {
weaponPowerupsToRemove.push(weaponPowerup);
continue;
}
// Check collision with player
var deltaX = weaponPowerup.x - player.x;
var deltaY = weaponPowerup.y - player.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance < 60) {
// Player collected weapon power-up
playerWeaponLevel = Math.min(3, playerWeaponLevel + 1); // Max level 3
playerWeaponTime = 1800; // 30 seconds of upgrade
LK.getSound('powerup').play();
LK.effects.flashScreen(0xffaa00, 300);
weaponPowerupsToRemove.push(weaponPowerup);
}
}
// Update and check boss enemies
for (var k = bossEnemies.length - 1; k >= 0; k--) {
var bossEnemy = bossEnemies[k];
// Mark boss enemies that go off screen for removal
if (bossEnemy.x < -200) {
// Boss enemy width is 100px * 2 scale = 200px
enemiesEscaped++;
if (enemiesEscaped >= 5) {
LK.showGameOver();
return;
}
bossEnemiesToRemove.push(bossEnemy);
continue;
}
// Skip processing for boss enemies outside meaningful interaction zones
if (bossEnemy.x < -250 || bossEnemy.x > 2100 || bossEnemy.y < -50 || bossEnemy.y > 2800) {
continue;
}
// Calculate distance once for collision detection
var deltaX = player.x - bossEnemy.x;
var deltaY = player.y - bossEnemy.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (bossEnemy.shoot()) {
var enemyBullet = new EnemyBullet();
var bulletOffset = 10; // Additional offset beyond enemy edge
enemyBullet.x = bossEnemy.x - (bossEnemy.width / 2 + bulletOffset); // Spawn relative to enemy width (scaled)
enemyBullet.y = bossEnemy.y;
enemyBullet.lastX = enemyBullet.x;
enemyBullet.lastY = enemyBullet.y;
// Shoot straight left - no player tracking
enemyBullet.directionX = -1;
enemyBullet.directionY = 0;
enemyBullets.push(enemyBullet);
game.addChild(enemyBullet);
}
// Check if boss enemy collides with player center using same distance calculation
if (distance < 90) {
// Only hits if within 90 pixels of player center (much larger due to 2x scale and boss size)
if (playerShieldTime <= 0) {
// Only take damage if not shielded
playerLives--;
// Update lives number display
if (livesNumberTxt) {
livesNumberTxt.setText(playerLives.toString());
}
LK.effects.flashScreen(0xFF0000, 500);
if (playerLives <= 0) {
LK.showGameOver();
return;
}
}
bossEnemiesToRemove.push(bossEnemy);
}
}
// Deferred cleanup - process all marked objects for removal using safer object-based iteration
// Process bullets marked for removal
for (var i = bulletsToRemove.length - 1; i >= 0; i--) {
var bulletToRemove = bulletsToRemove[i];
if (bulletToRemove && bulletToRemove.parent) {
var index = bullets.indexOf(bulletToRemove);
if (index !== -1 && index < bullets.length) {
bullets[index].cleanup();
bullets[index].destroy();
bullets.splice(index, 1);
}
}
}
bulletsToRemove = [];
// Process enemy bullets marked for removal
for (var i = enemyBulletsToRemove.length - 1; i >= 0; i--) {
var enemyBulletToRemove = enemyBulletsToRemove[i];
if (enemyBulletToRemove && enemyBulletToRemove.parent) {
var index = enemyBullets.indexOf(enemyBulletToRemove);
if (index !== -1 && index < enemyBullets.length) {
enemyBullets[index].cleanup();
enemyBullets[index].destroy();
enemyBullets.splice(index, 1);
}
}
}
enemyBulletsToRemove = [];
// Process regular enemies marked for removal
for (var i = enemiesToRemove.length - 1; i >= 0; i--) {
var enemyToRemove = enemiesToRemove[i];
if (enemyToRemove && enemyToRemove.parent) {
var index = enemies.indexOf(enemyToRemove);
if (index !== -1 && index < enemies.length) {
enemies[index].cleanup();
enemies[index].destroy();
enemies.splice(index, 1);
}
}
}
enemiesToRemove = [];
// Process fast enemies marked for removal
for (var i = fastEnemiesToRemove.length - 1; i >= 0; i--) {
var fastEnemyToRemove = fastEnemiesToRemove[i];
if (fastEnemyToRemove && fastEnemyToRemove.parent) {
var index = fastEnemies.indexOf(fastEnemyToRemove);
if (index !== -1 && index < fastEnemies.length) {
fastEnemies[index].cleanup();
fastEnemies[index].destroy();
fastEnemies.splice(index, 1);
}
}
}
fastEnemiesToRemove = [];
// Process shield enemies marked for removal
for (var i = shieldEnemiesToRemove.length - 1; i >= 0; i--) {
var shieldEnemyToRemove = shieldEnemiesToRemove[i];
if (shieldEnemyToRemove && shieldEnemyToRemove.parent) {
var index = shieldEnemies.indexOf(shieldEnemyToRemove);
if (index !== -1 && index < shieldEnemies.length) {
shieldEnemies[index].cleanup();
shieldEnemies[index].destroy();
shieldEnemies.splice(index, 1);
}
}
}
shieldEnemiesToRemove = [];
// Process boss enemies marked for removal
for (var i = bossEnemiesToRemove.length - 1; i >= 0; i--) {
var bossEnemyToRemove = bossEnemiesToRemove[i];
if (bossEnemyToRemove && bossEnemyToRemove.parent) {
var index = bossEnemies.indexOf(bossEnemyToRemove);
if (index !== -1 && index < bossEnemies.length) {
bossEnemies[index].cleanup();
bossEnemies[index].destroy();
bossEnemies.splice(index, 1);
}
}
}
bossEnemiesToRemove = [];
// Process health power-ups marked for removal
for (var i = healthPowerupsToRemove.length - 1; i >= 0; i--) {
var healthPowerupToRemove = healthPowerupsToRemove[i];
if (healthPowerupToRemove && healthPowerupToRemove.parent) {
var index = healthPowerups.indexOf(healthPowerupToRemove);
if (index !== -1 && index < healthPowerups.length) {
healthPowerups[index].cleanup();
healthPowerups[index].destroy();
healthPowerups.splice(index, 1);
}
}
}
healthPowerupsToRemove = [];
// Process shield power-ups marked for removal
for (var i = shieldPowerupsToRemove.length - 1; i >= 0; i--) {
var shieldPowerupToRemove = shieldPowerupsToRemove[i];
if (shieldPowerupToRemove && shieldPowerupToRemove.parent) {
var index = shieldPowerups.indexOf(shieldPowerupToRemove);
if (index !== -1 && index < shieldPowerups.length) {
shieldPowerups[index].cleanup();
shieldPowerups[index].destroy();
shieldPowerups.splice(index, 1);
}
}
}
shieldPowerupsToRemove = [];
// Process weapon power-ups marked for removal
for (var i = weaponPowerupsToRemove.length - 1; i >= 0; i--) {
var weaponPowerupToRemove = weaponPowerupsToRemove[i];
if (weaponPowerupToRemove && weaponPowerupToRemove.parent) {
var index = weaponPowerups.indexOf(weaponPowerupToRemove);
if (index !== -1 && index < weaponPowerups.length) {
weaponPowerups[index].cleanup();
weaponPowerups[index].destroy();
weaponPowerups.splice(index, 1);
}
}
}
weaponPowerupsToRemove = [];
// Monitor array sizes and implement limits to prevent memory leaks
var maxBullets = 100;
var maxEnemyBullets = 200;
var maxEnemies = 50;
// Limit bullets array size with safety checks
if (bullets.length > maxBullets) {
for (var i = bullets.length - 1; i >= maxBullets; i--) {
if (i < bullets.length && bullets[i]) {
bullets[i].cleanup();
bullets[i].destroy();
bullets.splice(i, 1);
}
}
}
// Limit enemy bullets array size with safety checks
if (enemyBullets.length > maxEnemyBullets) {
for (var i = enemyBullets.length - 1; i >= maxEnemyBullets; i--) {
if (i < enemyBullets.length && enemyBullets[i]) {
enemyBullets[i].cleanup();
enemyBullets[i].destroy();
enemyBullets.splice(i, 1);
}
}
}
// Limit enemies array size with safety checks
if (enemies.length > maxEnemies) {
for (var i = enemies.length - 1; i >= maxEnemies; i--) {
if (i < enemies.length && enemies[i]) {
enemies[i].cleanup();
enemies[i].destroy();
enemies.splice(i, 1);
}
}
}
// Limit fast enemies array size with safety checks
if (fastEnemies.length > maxEnemies) {
for (var i = fastEnemies.length - 1; i >= maxEnemies; i--) {
if (i < fastEnemies.length && fastEnemies[i]) {
fastEnemies[i].cleanup();
fastEnemies[i].destroy();
fastEnemies.splice(i, 1);
}
}
}
// Limit shield enemies array size with safety checks
if (shieldEnemies.length > maxEnemies) {
for (var i = shieldEnemies.length - 1; i >= maxEnemies; i--) {
if (i < shieldEnemies.length && shieldEnemies[i]) {
shieldEnemies[i].cleanup();
shieldEnemies[i].destroy();
shieldEnemies.splice(i, 1);
}
}
}
// Limit boss enemies array size with safety checks
if (bossEnemies.length > maxEnemies) {
for (var i = bossEnemies.length - 1; i >= maxEnemies; i--) {
if (i < bossEnemies.length && bossEnemies[i]) {
bossEnemies[i].cleanup();
bossEnemies[i].destroy();
bossEnemies.splice(i, 1);
}
}
}
};
// Play background music
LK.playMusic('bgmusic');
tommy vercetti de gta vice city hecho con pixeles apuntando con un rifle de asalto m4 con la perspcetiva lateral. In-Game asset. 2d. High contrast. No shadows
un mafioso italiano usando un m4 con la perspectiva lateral de cuerpo completo In-Game asset. 2d. High contrast. No shadows
un pandillero mexicano apuntando con un SMG con la perspectiva lateral. In-Game asset. 2d. High contrast. No shadows de cuerpo completo
un pandillero negro con camisa azul oscuro y pantalon blanco usando una escopeta con la perspectiva lateral de cuerpo completo. In-Game asset. 2d. High contrast. No shadows
chaleco antibalas. In-Game asset. 2d. High contrast. No shadows
corazon verde dibujado. In-Game asset. 2d. High contrast. No shadows
escopeta dibujada. In-Game asset. 2d. High contrast. No shadows
claude speed de gta 3 apuntando con una ak47 desde una perspectiva lateral de cuerpo completo In-Game asset. 2d. High contrast. No shadows
las letras play con un color celeste y rosa combinados con una cursiva In-Game asset. 2d. High contrast. No shadows con contorno negro
letras que dicen seleccion de personaje con un color rosa y un color celeste y con contorno azul y rosa cursiva. In-Game asset. 2d. High contrast. No shadows
las letras vidas con un color rosa y azul con contorno rosa y azul combinados hechos con pixeles. In-Game asset. 2d. High contrast. No shadows
una mansion con piso rojo vista completa y totalmente desde arriba y desde dentro con una escalera grande igual que la de scarface la pelicula y pasillos a los lados. In-Game asset. 2d. High contrast. No shadows
no va a tener ese brazo especificamente
un motociclista vestido de negro apuntando con una smg de cuerpo completo con perspectiva lateral In-Game asset. 2d. High contrast. No shadows sin casco y sin moto
letras de color rosa y azul y con cursiva que digan vice city wars. In-Game asset. 2d. High contrast. No shadows
un hombre usando una chaqueta negra pantalones azules disparando un ak47. In-Game asset. 2d. High contrast. No shadows con la perspectiva lateral de cuerpo completo
letras de color rosa que dicen seleccion de personaje en cursiva y con contorno blanco