/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BossBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bossBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 5; self.update = function () { self.y += self.speed; }; return self; }); var BossFish = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('bossFish', { anchorX: 0.5, anchorY: 0.5 }); self.health = 500; self.speed = 2; self.shootCooldown = 30; self.moveDirection = 1; self.attackPattern = 0; // 0: triple shot, 1: spread shot, 2: rapid fire self.attackCooldown = 0; self.pauseCooldown = 0; self.attacksInPattern = 0; self.maxAttacksPerPattern = 5; self.update = function () { self.x += self.speed * self.moveDirection; if (self.x <= 100 || self.x >= 2048 - 100) { self.moveDirection *= -1; } // Handle pauses between attack patterns if (self.pauseCooldown > 0) { self.pauseCooldown--; return; } if (self.shootCooldown > 0) { self.shootCooldown--; } else { self.executeAttackPattern(); self.attacksInPattern++; // Check if we need to switch patterns or pause if (self.attacksInPattern >= self.maxAttacksPerPattern) { self.attacksInPattern = 0; self.attackPattern = (self.attackPattern + 1) % 3; self.pauseCooldown = 90; // 1.5 second pause between patterns } else { // Set cooldown based on attack pattern if (self.attackPattern === 2) { // Rapid fire self.shootCooldown = 10; } else { self.shootCooldown = 25; } } } }; self.executeAttackPattern = function () { if (self.attackPattern === 0) { // Pattern 1: Triple shot (original) for (var i = 0; i < 3; i++) { var bullet = new BossBullet(); bullet.x = self.x + (i - 1) * 50; bullet.y = self.y + 70; enemyBullets.push(bullet); game.addChild(bullet); } } else if (self.attackPattern === 1) { // Pattern 2: Spread shot (5 bullets in wide arc) for (var i = 0; i < 5; i++) { var bullet = new BossBullet(); bullet.x = self.x + (i - 2) * 80; bullet.y = self.y + 70; // Add slight angle variation var angle = (i - 2) * 0.2; bullet.speedX = Math.sin(angle) * 2; bullet.speedY = bullet.speed + Math.abs(Math.cos(angle)) * 2; bullet.update = function () { this.x += this.speedX || 0; this.y += this.speedY || this.speed; }; enemyBullets.push(bullet); game.addChild(bullet); } } else if (self.attackPattern === 2) { // Pattern 3: Rapid fire (single bullet aimed at player) var bullet = new BossBullet(); bullet.x = self.x; bullet.y = self.y + 70; // Aim towards player var deltaX = player.x - bullet.x; var deltaY = player.y - bullet.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 0) { bullet.speedX = deltaX / distance * 3; bullet.speedY = deltaY / distance * 3; bullet.update = function () { this.x += this.speedX; this.y += this.speedY; }; } enemyBullets.push(bullet); game.addChild(bullet); } }; self.shootEnemy = function () { for (var i = 0; i < 3; i++) { var bullet = new BossBullet(); bullet.x = self.x + (i - 1) * 50; bullet.y = self.y + 70; enemyBullets.push(bullet); game.addChild(bullet); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); // Visual feedback when boss takes significant damage if (self.health <= 75 && self.health > 50) { tween(self, { tint: 0xff9999 }, { duration: 300, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 300 }); } }); } else if (self.health <= 50 && self.health > 25) { tween(self, { tint: 0xff6666 }, { duration: 300, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 300 }); } }); } else if (self.health <= 25) { tween(self, { tint: 0xff3333 }, { duration: 300, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 300 }); } }); } if (self.health <= 0) { LK.getSound('enemyHit').play(); return true; } return false; }; return self; }); var Bubble = Container.expand(function () { var self = Container.call(this); var bubbleGraphics = self.attachAsset('bubble', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -16; self.damage = 1; self.update = function () { self.y += self.speed; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.damage = 1; self.update = function () { self.y += self.speed; }; return self; }); var EnemyBullet2 = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet2', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.damage = 2; self.update = function () { self.y += self.speed; }; return self; }); var EnemyBullet3 = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet3', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.damage = 3; self.update = function () { self.y += self.speed; }; return self; }); var EnemyBullet4 = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet4', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 7; self.damage = 4; self.update = function () { self.y += self.speed; }; return self; }); var EnemyFish = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('enemyFish', { anchorX: 0.5, anchorY: 0.5 }); self.health = 2; self.speed = 2; self.shootCooldown = Math.random() * 120 + 60; self.update = function () { // Keep enemy fish in upper zone (y between 100-400) if (self.y < 100) { self.y = 100; } else if (self.y > 400) { self.y = 400; } // Move horizontally instead of vertically if (self.moveDirection === undefined) { self.moveDirection = Math.random() > 0.5 ? 1 : -1; self.horizontalSpeed = 1; } self.x += self.horizontalSpeed * self.moveDirection; // Bounce off screen edges if (self.x <= 50 || self.x >= 2048 - 50) { self.moveDirection *= -1; } if (self.shootCooldown > 0) { self.shootCooldown--; } else { self.shootEnemy(); self.shootCooldown = Math.random() * 120 + 60; } }; self.shootEnemy = function () { if (self.y > 0 && self.y < 2732 - 200) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 40; enemyBullets.push(bullet); game.addChild(bullet); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); // Add tween effect for damage feedback tween(self, { tint: 0xff4444 }, { duration: 150, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 150 }); } }); if (self.health <= 0) { LK.getSound('enemyHit').play(); return true; } return false; }; return self; }); var EnemyFish2 = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('enemyFish2', { anchorX: 0.5, anchorY: 0.5 }); self.health = 4; self.speed = 2; self.shootCooldown = Math.random() * 100 + 50; self.update = function () { // Keep enemy fish in upper zone (y between 100-400) if (self.y < 100) { self.y = 100; } else if (self.y > 400) { self.y = 400; } // Move horizontally instead of vertically if (self.moveDirection === undefined) { self.moveDirection = Math.random() > 0.5 ? 1 : -1; self.horizontalSpeed = 1; } self.x += self.horizontalSpeed * self.moveDirection; // Bounce off screen edges if (self.x <= 50 || self.x >= 2048 - 50) { self.moveDirection *= -1; } if (self.shootCooldown > 0) { self.shootCooldown--; } else { self.shootEnemy(); self.shootCooldown = Math.random() * 100 + 50; } }; self.shootEnemy = function () { if (self.y > 0 && self.y < 2732 - 200) { var bullet = new EnemyBullet2(); bullet.x = self.x; bullet.y = self.y + 40; enemyBullets.push(bullet); game.addChild(bullet); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); // Add tween effect for damage feedback tween(self, { tint: 0xff4444 }, { duration: 150, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 150 }); } }); if (self.health <= 0) { LK.getSound('enemyHit').play(); return true; } return false; }; return self; }); var EnemyFish3 = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('enemyFish3', { anchorX: 0.5, anchorY: 0.5 }); self.health = 10; self.speed = 2; self.shootCooldown = Math.random() * 80 + 40; self.update = function () { // Keep enemy fish in upper zone (y between 100-400) if (self.y < 100) { self.y = 100; } else if (self.y > 400) { self.y = 400; } // Move horizontally instead of vertically if (self.moveDirection === undefined) { self.moveDirection = Math.random() > 0.5 ? 1 : -1; self.horizontalSpeed = 1; } self.x += self.horizontalSpeed * self.moveDirection; // Bounce off screen edges if (self.x <= 50 || self.x >= 2048 - 50) { self.moveDirection *= -1; } if (self.shootCooldown > 0) { self.shootCooldown--; } else { self.shootEnemy(); self.shootCooldown = Math.random() * 80 + 40; } }; self.shootEnemy = function () { if (self.y > 0 && self.y < 2732 - 200) { var bullet = new EnemyBullet3(); bullet.x = self.x; bullet.y = self.y + 40; enemyBullets.push(bullet); game.addChild(bullet); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); // Add tween effect for damage feedback tween(self, { tint: 0xff4444 }, { duration: 150, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 150 }); } }); if (self.health <= 0) { LK.getSound('enemyHit').play(); return true; } return false; }; return self; }); var EnemyFish4 = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('enemyFish4', { anchorX: 0.5, anchorY: 0.5 }); self.health = 15; self.speed = 2; self.shootCooldown = Math.random() * 60 + 30; self.update = function () { // Keep enemy fish in upper zone (y between 100-400) if (self.y < 100) { self.y = 100; } else if (self.y > 400) { self.y = 400; } // Move horizontally instead of vertically if (self.moveDirection === undefined) { self.moveDirection = Math.random() > 0.5 ? 1 : -1; self.horizontalSpeed = 1; } self.x += self.horizontalSpeed * self.moveDirection; // Bounce off screen edges if (self.x <= 50 || self.x >= 2048 - 50) { self.moveDirection *= -1; } if (self.shootCooldown > 0) { self.shootCooldown--; } else { self.shootEnemy(); self.shootCooldown = Math.random() * 60 + 30; } }; self.shootEnemy = function () { if (self.y > 0 && self.y < 2732 - 200) { var bullet = new EnemyBullet4(); bullet.x = self.x; bullet.y = self.y + 40; enemyBullets.push(bullet); game.addChild(bullet); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xffffff, 200); // Add tween effect for damage feedback tween(self, { tint: 0xff4444 }, { duration: 150, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 150 }); } }); if (self.health <= 0) { LK.getSound('enemyHit').play(); return true; } return false; }; return self; }); var PlayerFish = Container.expand(function () { var self = Container.call(this); var fishGraphics = self.attachAsset('playerFish', { anchorX: 0.5, anchorY: 0.5 }); self.health = 3; self.damage = 1; self.maxHealth = 3; self.shootCooldown = 0; self.update = function () { // Only shoot when game is in playing state if (gameState !== 'playing') { return; } if (self.shootCooldown > 0) { self.shootCooldown--; } else { // Auto shoot every 25 frames to match cooldown self.shoot(); } }; self.takeDamage = function (damage) { if (playerInvulnerable) return; // Cannot take damage while invulnerable if (damage === undefined) damage = 1; self.health -= damage; LK.effects.flashObject(self, 0xff0000, 500); LK.getSound('playerHit').play(); // Start invulnerability period playerInvulnerable = true; invulnerabilityTimer = invulnerabilityDuration; // Create blinking effect during invulnerability tween(self, { alpha: 0.3 }, { duration: 200, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 200 }); } }); if (self.health <= 0) { LK.showGameOver(); } }; self.shoot = function () { if (self.shootCooldown <= 0) { var bubble = new Bubble(); bubble.x = self.x; bubble.y = self.y - 50; bubble.damage = self.damage; bubbles.push(bubble); game.addChild(bubble); self.shootCooldown = 25; LK.getSound('bubbleShoot').play(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x001F3F }); /**** * Game Code ****/ // Add background var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(background); var player; var enemies = []; var bubbles = []; var enemyBullets = []; var currentLevel = 1; var maxLevel = 10; var enemiesSpawned = 0; var enemiesKilled = 0; var spawnTimer = 0; var gameState = 'menu'; var boss = null; var playerInvulnerable = false; var invulnerabilityTimer = 0; var invulnerabilityDuration = 120; // 2 seconds at 60fps // UI Elements var healthText = new Text2('Health: 3', { size: 60, fill: 0xFFFFFF }); healthText.anchor.set(0, 0); healthText.x = 150; healthText.y = 50; healthText.visible = false; LK.gui.topLeft.addChild(healthText); var levelText = new Text2('Level: 1', { size: 60, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0); levelText.visible = false; LK.gui.top.addChild(levelText); var damageText = new Text2('Damage: 1', { size: 60, fill: 0xFFFFFF }); damageText.anchor.set(1, 0); damageText.x = -50; damageText.y = 50; damageText.visible = false; LK.gui.topRight.addChild(damageText); var bossHealthText = new Text2('', { size: 60, fill: 0xFF0000 }); bossHealthText.anchor.set(0.5, 0); bossHealthText.x = 0; bossHealthText.y = 150; bossHealthText.visible = false; LK.gui.top.addChild(bossHealthText); // Start Menu UI var startMenuContainer = new Container(); startMenuContainer.visible = true; LK.gui.center.addChild(startMenuContainer); var gameTitle = new Text2('Ocean Battle', { size: 120, fill: 0x00FFFF }); gameTitle.anchor.set(0.5, 0.5); gameTitle.y = -200; startMenuContainer.addChild(gameTitle); var startButton = LK.getAsset('playerFish', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 50, scaleX: 2, scaleY: 2 }); startMenuContainer.addChild(startButton); var startButtonText = new Text2('Iniciar', { size: 80, fill: 0xFFFFFF }); startButtonText.anchor.set(0.5, 0.5); startButtonText.x = 0; startButtonText.y = 150; startMenuContainer.addChild(startButtonText); // Upgrade UI (hidden initially) var upgradeContainer = new Container(); upgradeContainer.visible = false; LK.gui.center.addChild(upgradeContainer); var upgradeTitle = new Text2('Choose Upgrade:', { size: 80, fill: 0xFFFFFF }); upgradeTitle.anchor.set(0.5, 0.5); upgradeTitle.y = -150; upgradeContainer.addChild(upgradeTitle); var healthUpgradeButton = LK.getAsset('playerFish', { anchorX: 0.5, anchorY: 0.5, x: -200, y: 0, scaleX: 1.5, scaleY: 1.5 }); upgradeContainer.addChild(healthUpgradeButton); var healthUpgradeText = new Text2('+2 Health', { size: 50, fill: 0xFFFFFF }); healthUpgradeText.anchor.set(0.5, 0); healthUpgradeText.x = -200; healthUpgradeText.y = 150; upgradeContainer.addChild(healthUpgradeText); var damageUpgradeButton = LK.getAsset('bubble', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 0, scaleX: 2, scaleY: 2 }); upgradeContainer.addChild(damageUpgradeButton); var damageUpgradeText = new Text2('+1 Damage', { size: 50, fill: 0xFFFFFF }); damageUpgradeText.anchor.set(0.5, 0); damageUpgradeText.x = 200; damageUpgradeText.y = 150; upgradeContainer.addChild(damageUpgradeText); // Initialize player player = game.addChild(new PlayerFish()); player.x = 1024; player.y = 2500; function updateUI() { healthText.setText('Health: ' + player.health); levelText.setText('Level: ' + currentLevel); damageText.setText('Damage: ' + player.damage); // Update boss health display if (boss && boss.health > 0) { bossHealthText.setText('Boss Health: ' + boss.health); bossHealthText.visible = true; // Position the text below the boss var bossPosition = game.toLocal(boss.parent.toGlobal(boss.position)); var guiPosition = LK.gui.top.toLocal(game.toGlobal(bossPosition)); bossHealthText.x = guiPosition.x; bossHealthText.y = guiPosition.y + 450; // Position below boss (boss height is ~400px + margin) } else { bossHealthText.visible = false; } } function spawnEnemy() { var enemy; if (currentLevel === 10 && !boss) { boss = new BossFish(); boss.x = 1024; boss.y = 200; enemies.push(boss); game.addChild(boss); } else if (currentLevel < 10) { // Count already spawned fish types var alreadySpawnedFish1 = 0; var alreadySpawnedFish2 = 0; var alreadySpawnedFish3 = 0; var alreadySpawnedFish4 = 0; for (var e = 0; e < enemies.length; e++) { if (enemies[e] instanceof EnemyFish4) { alreadySpawnedFish4++; } else if (enemies[e] instanceof EnemyFish3) { alreadySpawnedFish3++; } else if (enemies[e] instanceof EnemyFish2) { alreadySpawnedFish2++; } else if (enemies[e] instanceof EnemyFish) { alreadySpawnedFish1++; } } // Calculate required numbers for this level var requiredFish1 = 10; // Always 10 level 1 fish var requiredFish2 = Math.max(0, currentLevel - 1); // Level 2 = 1, Level 3 = 2, etc. var requiredFish3 = Math.max(0, currentLevel - 4); // Level 5 = 1, Level 6 = 2, etc. var requiredFish4 = Math.max(0, currentLevel - 6); // Level 7 = 1, Level 8 = 2, etc. // Decide what type to spawn based on requirements if (currentLevel >= 7 && alreadySpawnedFish4 < requiredFish4) { enemy = new EnemyFish4(); } else if (currentLevel >= 5 && alreadySpawnedFish3 < requiredFish3) { enemy = new EnemyFish3(); } else if (currentLevel >= 2 && alreadySpawnedFish2 < requiredFish2) { enemy = new EnemyFish2(); } else if (alreadySpawnedFish1 < requiredFish1) { enemy = new EnemyFish(); } else { // All required fish spawned, don't spawn more return; } enemy.x = Math.random() * (2048 - 200) + 100; enemy.y = Math.random() * 200 + 100; // Spawn in upper zone (100-300) enemies.push(enemy); game.addChild(enemy); enemiesSpawned++; } } function getEnemiesForLevel(level) { if (level === 1) { return 10; // 10 level 1 fish only } else if (level >= 2 && level <= 4) { return 10 + (level - 1); // 10 level 1 fish + increasing level 2 fish } else if (level >= 5 && level <= 6) { return 10 + (level - 1) + (level - 4); // 10 level 1 fish + level 2 fish + level 3 fish } else if (level >= 7 && level <= 9) { return 10 + (level - 1) + (level - 4) + (level - 6); // 10 level 1 fish + level 2 fish + level 3 fish + level 4 fish } else { return 0; // Level 10 is boss only } } function startLevel() { enemiesSpawned = 0; enemiesKilled = 0; spawnTimer = 0; gameState = 'playing'; boss = null; // Change background for level 6 and beyond if (currentLevel >= 6) { background.destroy(); background = LK.getAsset('background2', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChildAt(background, 0); // Add at bottom layer // Change music for level 6 LK.playMusic('oceanAmbient2'); } // Restore player health to maximum at start of each level player.health = player.maxHealth; if (currentLevel === 10) { spawnEnemy(); // Spawn boss immediately } } function showUpgradeScreen() { gameState = 'upgrading'; upgradeContainer.visible = true; // Update upgrade text based on current level if (currentLevel >= 7) { healthUpgradeText.setText('+4 Health'); damageUpgradeText.setText('+2 Damage'); } else { healthUpgradeText.setText('+2 Health'); damageUpgradeText.setText('+1 Damage'); } } function hideUpgradeScreen() { upgradeContainer.visible = false; } function selectHealthUpgrade() { if (gameState === 'upgrading') { var healthIncrease = currentLevel >= 7 ? 4 : 2; player.health += healthIncrease; player.maxHealth += healthIncrease; hideUpgradeScreen(); nextLevel(); } } function selectDamageUpgrade() { if (gameState === 'upgrading') { var damageIncrease = currentLevel >= 7 ? 2 : 1; player.damage += damageIncrease; hideUpgradeScreen(); nextLevel(); } } function startGame() { gameState = 'playing'; startMenuContainer.visible = false; healthText.visible = true; levelText.visible = true; damageText.visible = true; startLevel(); updateUI(); } function nextLevel() { currentLevel++; if (currentLevel > maxLevel) { LK.showYouWin(); return; } startLevel(); updateUI(); } // Event handlers startButton.down = function (x, y, obj) { startGame(); }; healthUpgradeButton.down = function (x, y, obj) { selectHealthUpgrade(); }; damageUpgradeButton.down = function (x, y, obj) { selectDamageUpgrade(); }; game.down = function (x, y, obj) { // Player now shoots automatically, no manual shooting needed }; game.move = function (x, y, obj) { if (gameState === 'playing') { player.x = Math.max(60, Math.min(2048 - 60, x)); player.y = Math.max(2000, Math.min(2732 - 60, y)); } }; // Play background music LK.playMusic('oceanAmbient'); game.update = function () { if (gameState === 'menu') { return; } if (gameState !== 'playing') { // Stop player from shooting when not in playing state player.shootCooldown = 25; return; } // Handle player invulnerability if (playerInvulnerable) { invulnerabilityTimer--; // Create blinking effect every 10 frames if (invulnerabilityTimer % 20 < 10) { player.alpha = 0.3; } else { player.alpha = 1; } // End invulnerability if (invulnerabilityTimer <= 0) { playerInvulnerable = false; player.alpha = 1; // Ensure player is fully visible tween.stop(player, { alpha: true }); // Stop any ongoing alpha tweens } } // Spawn enemies if (currentLevel < 10) { spawnTimer++; var maxEnemies = getEnemiesForLevel(currentLevel); if (spawnTimer >= 90 && enemiesSpawned < maxEnemies) { spawnEnemy(); spawnTimer = 0; } } // Update bubbles for (var i = bubbles.length - 1; i >= 0; i--) { var bubble = bubbles[i]; if (bubble.lastY === undefined) bubble.lastY = bubble.y; // Remove off-screen bubbles if (bubble.lastY >= -30 && bubble.y < -30) { bubble.destroy(); bubbles.splice(i, 1); continue; } // Check bubble-enemy collisions var hit = false; for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (bubble.intersects(enemy)) { if (enemy.takeDamage(bubble.damage)) { enemy.destroy(); enemies.splice(j, 1); enemiesKilled++; } bubble.destroy(); bubbles.splice(i, 1); hit = true; break; } } if (!hit) { bubble.lastY = bubble.y; } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Remove off-screen bullets if (bullet.lastY <= 2732 + 30 && bullet.y > 2732 + 30) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // Check bullet-player collision if (bullet.intersects(player) && !playerInvulnerable) { player.takeDamage(bullet.damage); bullet.destroy(); enemyBullets.splice(i, 1); continue; } bullet.lastY = bullet.y; } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.lastY === undefined) enemy.lastY = enemy.y; // Remove off-screen enemies (not boss) if (enemy !== boss && enemy.lastY <= 2732 + 50 && enemy.y > 2732 + 50) { enemy.destroy(); enemies.splice(i, 1); continue; } // Check enemy-player collision if (enemy.intersects(player) && !playerInvulnerable) { player.takeDamage(1); } enemy.lastY = enemy.y; } // Check level completion if (currentLevel < 10) { var maxEnemies = getEnemiesForLevel(currentLevel); if (enemiesKilled >= maxEnemies && enemies.length === 0) { // Clear all enemy bullets when level is completed for (var k = enemyBullets.length - 1; k >= 0; k--) { enemyBullets[k].destroy(); enemyBullets.splice(k, 1); } showUpgradeScreen(); } } else { // Boss level if (enemies.length === 0) { // Clear all enemy bullets when boss is defeated for (var k = enemyBullets.length - 1; k >= 0; k--) { enemyBullets[k].destroy(); enemyBullets.splice(k, 1); } LK.showYouWin(); } } updateUI(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BossBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bossBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 5;
self.update = function () {
self.y += self.speed;
};
return self;
});
var BossFish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('bossFish', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.speed = 2;
self.shootCooldown = 30;
self.moveDirection = 1;
self.attackPattern = 0; // 0: triple shot, 1: spread shot, 2: rapid fire
self.attackCooldown = 0;
self.pauseCooldown = 0;
self.attacksInPattern = 0;
self.maxAttacksPerPattern = 5;
self.update = function () {
self.x += self.speed * self.moveDirection;
if (self.x <= 100 || self.x >= 2048 - 100) {
self.moveDirection *= -1;
}
// Handle pauses between attack patterns
if (self.pauseCooldown > 0) {
self.pauseCooldown--;
return;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.executeAttackPattern();
self.attacksInPattern++;
// Check if we need to switch patterns or pause
if (self.attacksInPattern >= self.maxAttacksPerPattern) {
self.attacksInPattern = 0;
self.attackPattern = (self.attackPattern + 1) % 3;
self.pauseCooldown = 90; // 1.5 second pause between patterns
} else {
// Set cooldown based on attack pattern
if (self.attackPattern === 2) {
// Rapid fire
self.shootCooldown = 10;
} else {
self.shootCooldown = 25;
}
}
}
};
self.executeAttackPattern = function () {
if (self.attackPattern === 0) {
// Pattern 1: Triple shot (original)
for (var i = 0; i < 3; i++) {
var bullet = new BossBullet();
bullet.x = self.x + (i - 1) * 50;
bullet.y = self.y + 70;
enemyBullets.push(bullet);
game.addChild(bullet);
}
} else if (self.attackPattern === 1) {
// Pattern 2: Spread shot (5 bullets in wide arc)
for (var i = 0; i < 5; i++) {
var bullet = new BossBullet();
bullet.x = self.x + (i - 2) * 80;
bullet.y = self.y + 70;
// Add slight angle variation
var angle = (i - 2) * 0.2;
bullet.speedX = Math.sin(angle) * 2;
bullet.speedY = bullet.speed + Math.abs(Math.cos(angle)) * 2;
bullet.update = function () {
this.x += this.speedX || 0;
this.y += this.speedY || this.speed;
};
enemyBullets.push(bullet);
game.addChild(bullet);
}
} else if (self.attackPattern === 2) {
// Pattern 3: Rapid fire (single bullet aimed at player)
var bullet = new BossBullet();
bullet.x = self.x;
bullet.y = self.y + 70;
// Aim towards player
var deltaX = player.x - bullet.x;
var deltaY = player.y - bullet.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 0) {
bullet.speedX = deltaX / distance * 3;
bullet.speedY = deltaY / distance * 3;
bullet.update = function () {
this.x += this.speedX;
this.y += this.speedY;
};
}
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.shootEnemy = function () {
for (var i = 0; i < 3; i++) {
var bullet = new BossBullet();
bullet.x = self.x + (i - 1) * 50;
bullet.y = self.y + 70;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
// Visual feedback when boss takes significant damage
if (self.health <= 75 && self.health > 50) {
tween(self, {
tint: 0xff9999
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 300
});
}
});
} else if (self.health <= 50 && self.health > 25) {
tween(self, {
tint: 0xff6666
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 300
});
}
});
} else if (self.health <= 25) {
tween(self, {
tint: 0xff3333
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 300
});
}
});
}
if (self.health <= 0) {
LK.getSound('enemyHit').play();
return true;
}
return false;
};
return self;
});
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -16;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyBullet2 = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet2', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.damage = 2;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyBullet3 = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet3', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.damage = 3;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyBullet4 = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet4', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 7;
self.damage = 4;
self.update = function () {
self.y += self.speed;
};
return self;
});
var EnemyFish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('enemyFish', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 2;
self.speed = 2;
self.shootCooldown = Math.random() * 120 + 60;
self.update = function () {
// Keep enemy fish in upper zone (y between 100-400)
if (self.y < 100) {
self.y = 100;
} else if (self.y > 400) {
self.y = 400;
}
// Move horizontally instead of vertically
if (self.moveDirection === undefined) {
self.moveDirection = Math.random() > 0.5 ? 1 : -1;
self.horizontalSpeed = 1;
}
self.x += self.horizontalSpeed * self.moveDirection;
// Bounce off screen edges
if (self.x <= 50 || self.x >= 2048 - 50) {
self.moveDirection *= -1;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.shootEnemy();
self.shootCooldown = Math.random() * 120 + 60;
}
};
self.shootEnemy = function () {
if (self.y > 0 && self.y < 2732 - 200) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
// Add tween effect for damage feedback
tween(self, {
tint: 0xff4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 150
});
}
});
if (self.health <= 0) {
LK.getSound('enemyHit').play();
return true;
}
return false;
};
return self;
});
var EnemyFish2 = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('enemyFish2', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 4;
self.speed = 2;
self.shootCooldown = Math.random() * 100 + 50;
self.update = function () {
// Keep enemy fish in upper zone (y between 100-400)
if (self.y < 100) {
self.y = 100;
} else if (self.y > 400) {
self.y = 400;
}
// Move horizontally instead of vertically
if (self.moveDirection === undefined) {
self.moveDirection = Math.random() > 0.5 ? 1 : -1;
self.horizontalSpeed = 1;
}
self.x += self.horizontalSpeed * self.moveDirection;
// Bounce off screen edges
if (self.x <= 50 || self.x >= 2048 - 50) {
self.moveDirection *= -1;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.shootEnemy();
self.shootCooldown = Math.random() * 100 + 50;
}
};
self.shootEnemy = function () {
if (self.y > 0 && self.y < 2732 - 200) {
var bullet = new EnemyBullet2();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
// Add tween effect for damage feedback
tween(self, {
tint: 0xff4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 150
});
}
});
if (self.health <= 0) {
LK.getSound('enemyHit').play();
return true;
}
return false;
};
return self;
});
var EnemyFish3 = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('enemyFish3', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 10;
self.speed = 2;
self.shootCooldown = Math.random() * 80 + 40;
self.update = function () {
// Keep enemy fish in upper zone (y between 100-400)
if (self.y < 100) {
self.y = 100;
} else if (self.y > 400) {
self.y = 400;
}
// Move horizontally instead of vertically
if (self.moveDirection === undefined) {
self.moveDirection = Math.random() > 0.5 ? 1 : -1;
self.horizontalSpeed = 1;
}
self.x += self.horizontalSpeed * self.moveDirection;
// Bounce off screen edges
if (self.x <= 50 || self.x >= 2048 - 50) {
self.moveDirection *= -1;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.shootEnemy();
self.shootCooldown = Math.random() * 80 + 40;
}
};
self.shootEnemy = function () {
if (self.y > 0 && self.y < 2732 - 200) {
var bullet = new EnemyBullet3();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
// Add tween effect for damage feedback
tween(self, {
tint: 0xff4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 150
});
}
});
if (self.health <= 0) {
LK.getSound('enemyHit').play();
return true;
}
return false;
};
return self;
});
var EnemyFish4 = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('enemyFish4', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 15;
self.speed = 2;
self.shootCooldown = Math.random() * 60 + 30;
self.update = function () {
// Keep enemy fish in upper zone (y between 100-400)
if (self.y < 100) {
self.y = 100;
} else if (self.y > 400) {
self.y = 400;
}
// Move horizontally instead of vertically
if (self.moveDirection === undefined) {
self.moveDirection = Math.random() > 0.5 ? 1 : -1;
self.horizontalSpeed = 1;
}
self.x += self.horizontalSpeed * self.moveDirection;
// Bounce off screen edges
if (self.x <= 50 || self.x >= 2048 - 50) {
self.moveDirection *= -1;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
self.shootEnemy();
self.shootCooldown = Math.random() * 60 + 30;
}
};
self.shootEnemy = function () {
if (self.y > 0 && self.y < 2732 - 200) {
var bullet = new EnemyBullet4();
bullet.x = self.x;
bullet.y = self.y + 40;
enemyBullets.push(bullet);
game.addChild(bullet);
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xffffff, 200);
// Add tween effect for damage feedback
tween(self, {
tint: 0xff4444
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 150
});
}
});
if (self.health <= 0) {
LK.getSound('enemyHit').play();
return true;
}
return false;
};
return self;
});
var PlayerFish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('playerFish', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.damage = 1;
self.maxHealth = 3;
self.shootCooldown = 0;
self.update = function () {
// Only shoot when game is in playing state
if (gameState !== 'playing') {
return;
}
if (self.shootCooldown > 0) {
self.shootCooldown--;
} else {
// Auto shoot every 25 frames to match cooldown
self.shoot();
}
};
self.takeDamage = function (damage) {
if (playerInvulnerable) return; // Cannot take damage while invulnerable
if (damage === undefined) damage = 1;
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 500);
LK.getSound('playerHit').play();
// Start invulnerability period
playerInvulnerable = true;
invulnerabilityTimer = invulnerabilityDuration;
// Create blinking effect during invulnerability
tween(self, {
alpha: 0.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 200
});
}
});
if (self.health <= 0) {
LK.showGameOver();
}
};
self.shoot = function () {
if (self.shootCooldown <= 0) {
var bubble = new Bubble();
bubble.x = self.x;
bubble.y = self.y - 50;
bubble.damage = self.damage;
bubbles.push(bubble);
game.addChild(bubble);
self.shootCooldown = 25;
LK.getSound('bubbleShoot').play();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001F3F
});
/****
* Game Code
****/
// Add background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(background);
var player;
var enemies = [];
var bubbles = [];
var enemyBullets = [];
var currentLevel = 1;
var maxLevel = 10;
var enemiesSpawned = 0;
var enemiesKilled = 0;
var spawnTimer = 0;
var gameState = 'menu';
var boss = null;
var playerInvulnerable = false;
var invulnerabilityTimer = 0;
var invulnerabilityDuration = 120; // 2 seconds at 60fps
// UI Elements
var healthText = new Text2('Health: 3', {
size: 60,
fill: 0xFFFFFF
});
healthText.anchor.set(0, 0);
healthText.x = 150;
healthText.y = 50;
healthText.visible = false;
LK.gui.topLeft.addChild(healthText);
var levelText = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
levelText.visible = false;
LK.gui.top.addChild(levelText);
var damageText = new Text2('Damage: 1', {
size: 60,
fill: 0xFFFFFF
});
damageText.anchor.set(1, 0);
damageText.x = -50;
damageText.y = 50;
damageText.visible = false;
LK.gui.topRight.addChild(damageText);
var bossHealthText = new Text2('', {
size: 60,
fill: 0xFF0000
});
bossHealthText.anchor.set(0.5, 0);
bossHealthText.x = 0;
bossHealthText.y = 150;
bossHealthText.visible = false;
LK.gui.top.addChild(bossHealthText);
// Start Menu UI
var startMenuContainer = new Container();
startMenuContainer.visible = true;
LK.gui.center.addChild(startMenuContainer);
var gameTitle = new Text2('Ocean Battle', {
size: 120,
fill: 0x00FFFF
});
gameTitle.anchor.set(0.5, 0.5);
gameTitle.y = -200;
startMenuContainer.addChild(gameTitle);
var startButton = LK.getAsset('playerFish', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 50,
scaleX: 2,
scaleY: 2
});
startMenuContainer.addChild(startButton);
var startButtonText = new Text2('Iniciar', {
size: 80,
fill: 0xFFFFFF
});
startButtonText.anchor.set(0.5, 0.5);
startButtonText.x = 0;
startButtonText.y = 150;
startMenuContainer.addChild(startButtonText);
// Upgrade UI (hidden initially)
var upgradeContainer = new Container();
upgradeContainer.visible = false;
LK.gui.center.addChild(upgradeContainer);
var upgradeTitle = new Text2('Choose Upgrade:', {
size: 80,
fill: 0xFFFFFF
});
upgradeTitle.anchor.set(0.5, 0.5);
upgradeTitle.y = -150;
upgradeContainer.addChild(upgradeTitle);
var healthUpgradeButton = LK.getAsset('playerFish', {
anchorX: 0.5,
anchorY: 0.5,
x: -200,
y: 0,
scaleX: 1.5,
scaleY: 1.5
});
upgradeContainer.addChild(healthUpgradeButton);
var healthUpgradeText = new Text2('+2 Health', {
size: 50,
fill: 0xFFFFFF
});
healthUpgradeText.anchor.set(0.5, 0);
healthUpgradeText.x = -200;
healthUpgradeText.y = 150;
upgradeContainer.addChild(healthUpgradeText);
var damageUpgradeButton = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 0,
scaleX: 2,
scaleY: 2
});
upgradeContainer.addChild(damageUpgradeButton);
var damageUpgradeText = new Text2('+1 Damage', {
size: 50,
fill: 0xFFFFFF
});
damageUpgradeText.anchor.set(0.5, 0);
damageUpgradeText.x = 200;
damageUpgradeText.y = 150;
upgradeContainer.addChild(damageUpgradeText);
// Initialize player
player = game.addChild(new PlayerFish());
player.x = 1024;
player.y = 2500;
function updateUI() {
healthText.setText('Health: ' + player.health);
levelText.setText('Level: ' + currentLevel);
damageText.setText('Damage: ' + player.damage);
// Update boss health display
if (boss && boss.health > 0) {
bossHealthText.setText('Boss Health: ' + boss.health);
bossHealthText.visible = true;
// Position the text below the boss
var bossPosition = game.toLocal(boss.parent.toGlobal(boss.position));
var guiPosition = LK.gui.top.toLocal(game.toGlobal(bossPosition));
bossHealthText.x = guiPosition.x;
bossHealthText.y = guiPosition.y + 450; // Position below boss (boss height is ~400px + margin)
} else {
bossHealthText.visible = false;
}
}
function spawnEnemy() {
var enemy;
if (currentLevel === 10 && !boss) {
boss = new BossFish();
boss.x = 1024;
boss.y = 200;
enemies.push(boss);
game.addChild(boss);
} else if (currentLevel < 10) {
// Count already spawned fish types
var alreadySpawnedFish1 = 0;
var alreadySpawnedFish2 = 0;
var alreadySpawnedFish3 = 0;
var alreadySpawnedFish4 = 0;
for (var e = 0; e < enemies.length; e++) {
if (enemies[e] instanceof EnemyFish4) {
alreadySpawnedFish4++;
} else if (enemies[e] instanceof EnemyFish3) {
alreadySpawnedFish3++;
} else if (enemies[e] instanceof EnemyFish2) {
alreadySpawnedFish2++;
} else if (enemies[e] instanceof EnemyFish) {
alreadySpawnedFish1++;
}
}
// Calculate required numbers for this level
var requiredFish1 = 10; // Always 10 level 1 fish
var requiredFish2 = Math.max(0, currentLevel - 1); // Level 2 = 1, Level 3 = 2, etc.
var requiredFish3 = Math.max(0, currentLevel - 4); // Level 5 = 1, Level 6 = 2, etc.
var requiredFish4 = Math.max(0, currentLevel - 6); // Level 7 = 1, Level 8 = 2, etc.
// Decide what type to spawn based on requirements
if (currentLevel >= 7 && alreadySpawnedFish4 < requiredFish4) {
enemy = new EnemyFish4();
} else if (currentLevel >= 5 && alreadySpawnedFish3 < requiredFish3) {
enemy = new EnemyFish3();
} else if (currentLevel >= 2 && alreadySpawnedFish2 < requiredFish2) {
enemy = new EnemyFish2();
} else if (alreadySpawnedFish1 < requiredFish1) {
enemy = new EnemyFish();
} else {
// All required fish spawned, don't spawn more
return;
}
enemy.x = Math.random() * (2048 - 200) + 100;
enemy.y = Math.random() * 200 + 100; // Spawn in upper zone (100-300)
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
}
function getEnemiesForLevel(level) {
if (level === 1) {
return 10; // 10 level 1 fish only
} else if (level >= 2 && level <= 4) {
return 10 + (level - 1); // 10 level 1 fish + increasing level 2 fish
} else if (level >= 5 && level <= 6) {
return 10 + (level - 1) + (level - 4); // 10 level 1 fish + level 2 fish + level 3 fish
} else if (level >= 7 && level <= 9) {
return 10 + (level - 1) + (level - 4) + (level - 6); // 10 level 1 fish + level 2 fish + level 3 fish + level 4 fish
} else {
return 0; // Level 10 is boss only
}
}
function startLevel() {
enemiesSpawned = 0;
enemiesKilled = 0;
spawnTimer = 0;
gameState = 'playing';
boss = null;
// Change background for level 6 and beyond
if (currentLevel >= 6) {
background.destroy();
background = LK.getAsset('background2', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChildAt(background, 0); // Add at bottom layer
// Change music for level 6
LK.playMusic('oceanAmbient2');
}
// Restore player health to maximum at start of each level
player.health = player.maxHealth;
if (currentLevel === 10) {
spawnEnemy(); // Spawn boss immediately
}
}
function showUpgradeScreen() {
gameState = 'upgrading';
upgradeContainer.visible = true;
// Update upgrade text based on current level
if (currentLevel >= 7) {
healthUpgradeText.setText('+4 Health');
damageUpgradeText.setText('+2 Damage');
} else {
healthUpgradeText.setText('+2 Health');
damageUpgradeText.setText('+1 Damage');
}
}
function hideUpgradeScreen() {
upgradeContainer.visible = false;
}
function selectHealthUpgrade() {
if (gameState === 'upgrading') {
var healthIncrease = currentLevel >= 7 ? 4 : 2;
player.health += healthIncrease;
player.maxHealth += healthIncrease;
hideUpgradeScreen();
nextLevel();
}
}
function selectDamageUpgrade() {
if (gameState === 'upgrading') {
var damageIncrease = currentLevel >= 7 ? 2 : 1;
player.damage += damageIncrease;
hideUpgradeScreen();
nextLevel();
}
}
function startGame() {
gameState = 'playing';
startMenuContainer.visible = false;
healthText.visible = true;
levelText.visible = true;
damageText.visible = true;
startLevel();
updateUI();
}
function nextLevel() {
currentLevel++;
if (currentLevel > maxLevel) {
LK.showYouWin();
return;
}
startLevel();
updateUI();
}
// Event handlers
startButton.down = function (x, y, obj) {
startGame();
};
healthUpgradeButton.down = function (x, y, obj) {
selectHealthUpgrade();
};
damageUpgradeButton.down = function (x, y, obj) {
selectDamageUpgrade();
};
game.down = function (x, y, obj) {
// Player now shoots automatically, no manual shooting needed
};
game.move = function (x, y, obj) {
if (gameState === 'playing') {
player.x = Math.max(60, Math.min(2048 - 60, x));
player.y = Math.max(2000, Math.min(2732 - 60, y));
}
};
// Play background music
LK.playMusic('oceanAmbient');
game.update = function () {
if (gameState === 'menu') {
return;
}
if (gameState !== 'playing') {
// Stop player from shooting when not in playing state
player.shootCooldown = 25;
return;
}
// Handle player invulnerability
if (playerInvulnerable) {
invulnerabilityTimer--;
// Create blinking effect every 10 frames
if (invulnerabilityTimer % 20 < 10) {
player.alpha = 0.3;
} else {
player.alpha = 1;
}
// End invulnerability
if (invulnerabilityTimer <= 0) {
playerInvulnerable = false;
player.alpha = 1; // Ensure player is fully visible
tween.stop(player, {
alpha: true
}); // Stop any ongoing alpha tweens
}
}
// Spawn enemies
if (currentLevel < 10) {
spawnTimer++;
var maxEnemies = getEnemiesForLevel(currentLevel);
if (spawnTimer >= 90 && enemiesSpawned < maxEnemies) {
spawnEnemy();
spawnTimer = 0;
}
}
// Update bubbles
for (var i = bubbles.length - 1; i >= 0; i--) {
var bubble = bubbles[i];
if (bubble.lastY === undefined) bubble.lastY = bubble.y;
// Remove off-screen bubbles
if (bubble.lastY >= -30 && bubble.y < -30) {
bubble.destroy();
bubbles.splice(i, 1);
continue;
}
// Check bubble-enemy collisions
var hit = false;
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (bubble.intersects(enemy)) {
if (enemy.takeDamage(bubble.damage)) {
enemy.destroy();
enemies.splice(j, 1);
enemiesKilled++;
}
bubble.destroy();
bubbles.splice(i, 1);
hit = true;
break;
}
}
if (!hit) {
bubble.lastY = bubble.y;
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (bullet.lastY === undefined) bullet.lastY = bullet.y;
// Remove off-screen bullets
if (bullet.lastY <= 2732 + 30 && bullet.y > 2732 + 30) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check bullet-player collision
if (bullet.intersects(player) && !playerInvulnerable) {
player.takeDamage(bullet.damage);
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
bullet.lastY = bullet.y;
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.lastY === undefined) enemy.lastY = enemy.y;
// Remove off-screen enemies (not boss)
if (enemy !== boss && enemy.lastY <= 2732 + 50 && enemy.y > 2732 + 50) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check enemy-player collision
if (enemy.intersects(player) && !playerInvulnerable) {
player.takeDamage(1);
}
enemy.lastY = enemy.y;
}
// Check level completion
if (currentLevel < 10) {
var maxEnemies = getEnemiesForLevel(currentLevel);
if (enemiesKilled >= maxEnemies && enemies.length === 0) {
// Clear all enemy bullets when level is completed
for (var k = enemyBullets.length - 1; k >= 0; k--) {
enemyBullets[k].destroy();
enemyBullets.splice(k, 1);
}
showUpgradeScreen();
}
} else {
// Boss level
if (enemies.length === 0) {
// Clear all enemy bullets when boss is defeated
for (var k = enemyBullets.length - 1; k >= 0; k--) {
enemyBullets[k].destroy();
enemyBullets.splice(k, 1);
}
LK.showYouWin();
}
}
updateUI();
};
Water Bubble. In-Game asset. High contrast. No shadows. 2D
a orange water bubble. In-Game asset. 2d. High contrast. No shadows
a orange fish seen from top to bottom. In-Game asset. 2d. High contrast. No shadows
a blue fish seen from above, looking up. In-Game asset. 2d. High contrast. No shadows
a purple evil fish seen from top to bottom. In-Game asset. 2d. High contrast. No shadows
a red fish seen from top to bottom. In-Game asset. 2d. High contrast. No shadows
a gold fish seen from top to bottom. In-Game asset. 2d. High contrast. No shadows
a gold dark fish seen from top to bottom. In-Game asset. 2d. High contrast. No shadows
image of the ocean bottom. In-Game asset. 2d. High contrast. No shadows
image of the bottom of the ocean, dark background. In-Game asset. 2d. High contrast. No shadows