User prompt
creame un aset para el back groun que cambiara al apretar play y luego de ver el sketch emepezara el juego y se restablecera el background de siempre
User prompt
creame un background que solo este cuando este el sketch
User prompt
que despues de apretar play antes de jugar haya un aset para un sketch
User prompt
no me salio el pulpo
User prompt
que el pulpo que ayuda sea aun mas rapido ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
que salga un poco mas a la izquierda
User prompt
quiero que el poyectil del cañon salga un poco mas a la izquierda y un poquito mas arriba de la posicion actual
User prompt
la forma de obtener al pulpo es la misma que cualquier poder un enemigo bota una esfera, en este caso una que dice p y si le pego a esa esfera sale el pulpo
User prompt
y que los teledirijidos tambien impacten los botiquines
User prompt
se va apenas aparece a veces que el bote 5 barcos que se asegure es y ahi se vaya no que yo los bote ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
y que el pulpo no se vaya hasta que el haya logrado por su cuenta botar 5 barcos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
QUE EL amigo pulpo sea mas rapido el triple ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
generame un aser para la parte donde sale en el inicio para poder ponerle una imagen donde salga mi nombre
User prompt
quita lo de aumentar la velocidad de ataque por oleada
User prompt
quedo super bien ahora hace que sea menos probable que aparezca como 2 de 10 barcos en probabilidad a los jefes les quita 15 de vida y muere el pulpo no los mata al tiro, cada vez que el pulpo mata 5 embarcaciones se va ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que sea mas probable que el enemigo bote un brazo de pulpo al cual le disparo y mata 5 enemigos al unisono ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
creame el poder para poder invocar al brazo del pulpo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
quiero un aset abajo del play para poder crear una imagen que diga mi nombre como creador
User prompt
crea un aset del pulpo
User prompt
cada ronda mi velocidad de ataque aumente progresivamente, quiero agregar un poder que salga derrepente de un enemigo, que el poder te de un ayudante pulpo que pesca una 5 embarcacion y la hunde, las lleva al fondo del mar es una animacion corta y breve donde la embarcacion baja un poquito y corrije tambien la barra de los barcos medios que esta media bug cuando los matas o les haces daño ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
los barcos medianos tiran un error mejor quita el hecho de mejorar la estadisticas dejocomo estaba antes el juego
User prompt
pero como que me mejoro de manera exagerada que sea como te dije las mejoras y de manera aleatoria y que salga en grande lo que mejoro ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
pero que sea una animacion sencilla y que sigan apareciendo los enemigos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
mira mejor que se elija sola la mejora y que el juego siga su curso solo que salga una de la imagenes con una animacion en el centro diciento que mejor esa ronda ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
que al colisionar con una bala de cualquier tipo una de las estadisticas que quiero mejorar se pase a formar parte de la modificacion del caño para la otra oleada y se reanude el juego porque hasta ahora no ha vuelto
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); self.shipType = 'boss'; self.speed = 0.5; // Very slow base speed self.points = 500; // High points for boss self.graphics = null; self.healthBar = null; self.healthBarBg = null; self.isDying = false; self.init = function (wave) { self.shipType = 'boss'; self.graphics = self.attachAsset('bossShip', { anchorX: 0.5, anchorY: 0.5 }); // Make boss larger and more intimidating self.graphics.scaleX = 1.5; self.graphics.scaleY = 1.5; // Slow speed that slightly increases with wave self.speed = 0.5 + (wave - 4) * 0.1; self.points = 500 + (wave - 4) * 100; // Scaling points self.health = 40; self.maxHealth = 40; // Create larger health bar for boss var healthBarWidth = 300; self.healthBarBg = LK.getAsset('healthBarBg', { width: healthBarWidth, height: 20, color: 0x444444, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); self.healthBarBg.y = -self.graphics.height / 2 - 40; self.addChild(self.healthBarBg); self.healthBar = LK.getAsset('healthBar', { width: healthBarWidth - 4, height: 16, color: 0xFF0000, // Red health bar for boss shape: 'box', anchorX: 0.5, anchorY: 0.5 }); self.healthBar.y = -self.graphics.height / 2 - 40; self.addChild(self.healthBar); // Boss shoots more frequently self.lastShotTime = 0; self.shotDelay = 90; // 1.5 seconds }; self.updateHealthBar = function () { if (self.healthBar && self.maxHealth > 1) { var healthPercent = self.health / self.maxHealth; self.healthBar.scaleX = healthPercent; if (healthPercent > 0.6) { self.healthBar.tint = 0xFF4444; // Dark red } else if (healthPercent > 0.3) { self.healthBar.tint = 0xFF0000; // Red } else { self.healthBar.tint = 0x880000; // Dark red } } }; self.update = function () { self.x += self.speed; if (self.graphics) { self.graphics.visible = true; } }; self.canShoot = function () { return LK.ticks - self.lastShotTime >= self.shotDelay; }; self.shootAtCannon = function (cannonX, cannonY) { if (!self.canShoot()) return null; self.lastShotTime = LK.ticks; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; var dx = cannonX - self.x; var dy = cannonY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); bullet.velocityX = dx / distance * bullet.speed; bullet.velocityY = dy / distance * bullet.speed; bullet.damage = 2; // Boss bullets do more damage return bullet; }; return self; }); var Cannon = Container.expand(function () { var self = Container.call(this); self.graphics = self.attachAsset('cannon', { anchorX: 0.5, anchorY: 0.5 }); self.targetX = 0; self.targetY = 0; self.lastFireTime = 0; self.fireDelay = 300; self.health = 10; self.maxHealth = 10; self.isShielded = false; self.shieldTimer = 0; self.aimAt = function (x, y) { self.targetX = x; self.targetY = y; var dx = x - self.x; var dy = y - self.y; var angle = Math.atan2(dy, dx); self.graphics.rotation = angle; }; self.canFire = function () { var currentFireDelay = rapidFire ? self.fireDelay / 3 : self.fireDelay; currentFireDelay = currentFireDelay / cannonAttackSpeedMultiplier; return LK.ticks * 16.67 - self.lastFireTime >= currentFireDelay; }; self.fire = function () { if (!self.canFire()) return null; self.lastFireTime = LK.ticks * 16.67; var cannonballs = []; var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var baseAngle = Math.atan2(dy, dx); if (guidedShot) { // Fire guided projectile var guidedProjectile = new GuidedProjectile(); guidedProjectile.x = self.x - 20; guidedProjectile.y = self.y - 25; guidedProjectile.targetX = self.targetX; guidedProjectile.targetY = self.targetY; guidedProjectile.lastTargetUpdateTime = LK.ticks; guidedProjectile.creationTime = LK.ticks; // Set creation time guidedProjectiles.push(guidedProjectile); game.addChild(guidedProjectile); } else if (tripleShot) { // Fire three cannonballs with slight angle differences for (var i = 0; i < 3; i++) { var cannonball = new Cannonball(); cannonball.x = self.x - 20; cannonball.y = self.y - 25; var angleOffset = (i - 1) * 0.2; // -0.2, 0, 0.2 radians var adjustedAngle = baseAngle + angleOffset; cannonball.velocityX = Math.cos(adjustedAngle) * cannonball.speed; cannonball.velocityY = Math.sin(adjustedAngle) * cannonball.speed; cannonballs.push(cannonball); } } else { // Fire single cannonball var cannonball = new Cannonball(); cannonball.x = self.x - 20; cannonball.y = self.y - 25; cannonball.velocityX = dx / distance * cannonball.speed; cannonball.velocityY = dy / distance * cannonball.speed; cannonballs.push(cannonball); } LK.getSound('shoot').play(); return cannonballs; }; self.takeDamage = function (damage) { if (self.isShielded) { damage = Math.floor(damage / 2); // Shield reduces damage by half } self.health -= damage; if (self.health < 0) self.health = 0; // Flash red when taking damage tween(self.graphics, { tint: 0xff0000 }, { duration: 100, onFinish: function onFinish() { tween(self.graphics, { tint: 0xffffff }, { duration: 100 }); } }); }; self.heal = function (amount) { self.health += amount; if (self.health > self.maxHealth) self.health = self.maxHealth; // Flash green when healing tween(self.graphics, { tint: 0x00ff00 }, { duration: 200, onFinish: function onFinish() { tween(self.graphics, { tint: 0xffffff }, { duration: 200 }); } }); }; self.activateShield = function () { self.isShielded = true; self.shieldTimer = 900; // 15 seconds at 60fps self.graphics.tint = 0x88ccff; // Blue tint for shield }; return self; }); var Cannonball = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('cannonball', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.speed = 16; self.damage = 1; // Base damage, will be multiplied by cannonDamage when hitting self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.speed = 8; self.damage = 1; self.update = function () { self.x += self.velocityX; self.y += self.velocityY; }; return self; }); var GuidedProjectile = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('guidedProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 16; self.damage = 3; // Base damage for guided projectiles (will be enhanced by cannonDamage) self.targetX = 0; self.targetY = 0; self.lastTargetUpdateTime = 0; self.targetUpdateDelay = 30; // Update target every 0.5 seconds self.creationTime = 0; // Track when the projectile was created self.lifetime = 120; // 2 seconds at 60fps (2 * 60) self.update = function () { // Update target position periodically if (LK.ticks - self.lastTargetUpdateTime >= self.targetUpdateDelay) { // Find nearest enemy ship var nearestShip = null; var nearestDistance = Infinity; for (var i = 0; i < ships.length; i++) { var ship = ships[i]; if (ship && ship.graphics && ship.graphics.visible && !ship.isDying) { var dx = ship.x - self.x; var dy = ship.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < nearestDistance) { nearestDistance = distance; nearestShip = ship; } } } // If we found a ship, target it if (nearestShip) { self.targetX = nearestShip.x; self.targetY = nearestShip.y; } self.lastTargetUpdateTime = LK.ticks; } // Move toward target var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Add slight rotation for visual effect graphics.rotation += 0.1; }; return self; }); var GuidedShotPowerUp = Container.expand(function () { var self = Container.call(this); self.speed = 2; var graphics = self.attachAsset('guidedShotPowerUp', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.x += self.speed; }; return self; }); var HealthKit = Container.expand(function () { var self = Container.call(this); self.speed = 2; self.healAmount = 2; var graphics = self.attachAsset('healthKit', { anchorX: 0.5, anchorY: 0.5 }); // Add a cross symbol var crossV = LK.getAsset('healthKit', { width: 20, height: 60, color: 0xffffff, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); var crossH = LK.getAsset('healthKit', { width: 60, height: 20, color: 0xffffff, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); self.addChild(crossV); self.addChild(crossH); self.update = function () { self.x += self.speed; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); self.powerType = 'tripleShot'; self.speed = 2; self.graphics = null; self.init = function (type) { self.powerType = type; if (type === 'tripleShot') { self.graphics = self.attachAsset('tripleShotPowerUp', { anchorX: 0.5, anchorY: 0.5 }); } else if (type === 'rapidFire') { self.graphics = self.attachAsset('rapidFirePowerUp', { anchorX: 0.5, anchorY: 0.5 }); } }; self.update = function () { self.x += self.speed; }; return self; }); var ShieldPowerUp = Container.expand(function () { var self = Container.call(this); self.speed = 2; var graphics = self.attachAsset('shieldPowerUp', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { self.x += self.speed; }; return self; }); var Ship = Container.expand(function () { var self = Container.call(this); self.shipType = 'small'; self.speed = 2; self.points = 10; self.graphics = null; self.healthBar = null; self.healthBarBg = null; self.isDying = false; // Flag to prevent damage during death animation self.init = function (type) { self.shipType = type; if (type === 'small') { self.graphics = self.attachAsset('smallShip', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; // Small ships: 100 points in waves 1-2, then scale with wave if (currentWave <= 2) { self.points = 100; } else { self.points = 10 + (currentWave - 2) * 5; // Gradual increase after wave 2 } self.health = 1; self.maxHealth = 1; } else if (type === 'medium') { self.graphics = self.attachAsset('mediumShip', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; // Medium ships: 100 points when first appearing (waves 3-4), then scale if (currentWave <= 4) { self.points = 100; } else { self.points = 25 + (currentWave - 4) * 10; // Gradual increase after wave 4 } self.health = 3; self.maxHealth = 3; } else if (type === 'large') { self.graphics = self.attachAsset('largeShip', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1.5; // Large ships: 100 points when first appearing (waves 5-6), then scale if (currentWave <= 6) { self.points = 100; } else { self.points = 50 + (currentWave - 6) * 15; // Gradual increase after wave 6 } self.health = 5; self.maxHealth = 5; } // Create health bar background - wider for better visibility var healthBarWidth = Math.max(120, self.graphics.width * 1.2); self.healthBarBg = LK.getAsset('healthBarBg', { width: healthBarWidth, height: 12, color: 0x444444, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); self.healthBarBg.y = -self.graphics.height / 2 - 20; self.addChild(self.healthBarBg); // Create health bar foreground - wider for better visibility self.healthBar = LK.getAsset('healthBar', { width: healthBarWidth - 4, height: 8, color: 0x00FF00, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); self.healthBar.y = -self.graphics.height / 2 - 20; self.addChild(self.healthBar); // Show health bar for all ships now - no hiding for small ships // Set shooting properties based on ship type self.lastShotTime = 0; if (type === 'small') { self.shotDelay = 180; // 3 seconds } else if (type === 'medium') { self.shotDelay = 150; // 2.5 seconds } else if (type === 'large') { self.shotDelay = 120; // 2 seconds } }; self.updateHealthBar = function () { if (self.healthBar && self.maxHealth > 1) { var healthPercent = self.health / self.maxHealth; self.healthBar.scaleX = healthPercent; if (healthPercent > 0.6) { self.healthBar.tint = 0x00FF00; // Green } else if (healthPercent > 0.3) { self.healthBar.tint = 0xFFFF00; // Yellow } else { self.healthBar.tint = 0xFF0000; // Red } } }; self.update = function () { self.x += self.speed; // Ensure collision detection remains active if (self.graphics) { self.graphics.visible = true; } }; self.canShoot = function () { return LK.ticks - self.lastShotTime >= self.shotDelay; }; self.shootAtCannon = function (cannonX, cannonY) { if (!self.canShoot()) return null; self.lastShotTime = LK.ticks; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; var dx = cannonX - self.x; var dy = cannonY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); bullet.velocityX = dx / distance * bullet.speed; bullet.velocityY = dy / distance * bullet.speed; return bullet; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x001122 }); /**** * Game Code ****/ var oceanBackground = LK.getAsset('oceanBackground', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); game.addChild(oceanBackground); // Start menu variables var gameStarted = false; var startMenu = new Container(); game.addChild(startMenu); // Ocean title image var oceanTitleImage = startMenu.attachAsset('oceanTitle', { anchorX: 0.5, anchorY: 0.5 }); oceanTitleImage.x = 1024; oceanTitleImage.y = 750; // Defender title image var defenderTitleImage = startMenu.attachAsset('defenderTitle', { anchorX: 0.5, anchorY: 0.5 }); defenderTitleImage.x = 1024; defenderTitleImage.y = 1600; // Play button image var playButtonImage = startMenu.attachAsset('playButtonImage', { anchorX: 0.5, anchorY: 0.5 }); playButtonImage.x = 1024; playButtonImage.y = 2300; // Add pulsating animation to ocean title function pulseOceanTitle() { tween(oceanTitleImage, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(oceanTitleImage, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1000, easing: tween.easeInOut, onFinish: pulseOceanTitle }); } }); } pulseOceanTitle(); // Add pulsating animation to defender title function pulseDefenderTitle() { tween(defenderTitleImage, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1200, easing: tween.easeInOut, onFinish: function onFinish() { tween(defenderTitleImage, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1200, easing: tween.easeInOut, onFinish: pulseDefenderTitle }); } }); } pulseDefenderTitle(); // Add pulsating animation to play button function pulsePlayButton() { tween(playButtonImage, { scaleX: 1.15, scaleY: 1.15 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(playButtonImage, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: pulsePlayButton }); } }); } pulsePlayButton(); // Start playing the menu music LK.playMusic('menuMusic'); function showUpgradeMenu() { upgradeMenuVisible = true; gamePaused = true; upgradeMenu.visible = true; } function hideUpgradeMenu() { upgradeMenuVisible = false; gamePaused = false; upgradeMenu.visible = false; enemiesKilledThisWave = 0; waveCompleted = false; } function checkWaveCompletion() { // Check for 1000 point milestone upgrade if (LK.getScore() >= 1000 && LK.getScore() % 1000 === 0 && !upgradeMenuVisible) { // Destroy all remaining enemies for (var i = ships.length - 1; i >= 0; i--) { var ship = ships[i]; if (ship && ship.parent) { ship.destroy(); ships.splice(i, 1); } } // Clear all enemy bullets for (var j = enemyBullets.length - 1; j >= 0; j--) { var bullet = enemyBullets[j]; if (bullet && bullet.parent) { bullet.destroy(); enemyBullets.splice(j, 1); } } showUpgradeMenu(); return; } var requiredKills = enemiesRequiredPerWave + (currentWave - 1) * 5; if (enemiesKilledThisWave >= requiredKills && !waveCompleted) { waveCompleted = true; // Destroy all remaining enemies for (var i = ships.length - 1; i >= 0; i--) { var ship = ships[i]; if (ship && ship.parent) { ship.destroy(); ships.splice(i, 1); } } // Clear all enemy bullets for (var j = enemyBullets.length - 1; j >= 0; j--) { var bullet = enemyBullets[j]; if (bullet && bullet.parent) { bullet.destroy(); enemyBullets.splice(j, 1); } } showUpgradeMenu(); } } var cannon = game.addChild(new Cannon()); cannon.x = 1024; cannon.y = 2600; cannon.visible = false; // Shield effect visual var shieldEffect = LK.getAsset('shieldEffect', { anchorX: 0.5, anchorY: 0.5 }); shieldEffect.x = cannon.x; shieldEffect.y = cannon.y; shieldEffect.visible = false; shieldEffect.alpha = 0.6; shieldEffect.scaleX = 0.3; shieldEffect.scaleY = 0.3; game.addChild(shieldEffect); var ships = []; var cannonballs = []; var shipSpawnTimer = 0; var shipSpawnDelay = 120; var difficultyTimer = 0; var shipsEscaped = 0; var maxEscapedShips = 10; // Escaped ships counter UI var escapedShipsTxt = new Text2('Escaped: 0/10', { size: 50, fill: 0xFFFFFF }); escapedShipsTxt.anchor.set(0.5, 0); escapedShipsTxt.y = 60; escapedShipsTxt.visible = false; LK.gui.top.addChild(escapedShipsTxt); // Wave number display UI var waveNumberTxt = new Text2('Wave: 1', { size: 50, fill: 0xFFFFFF }); waveNumberTxt.anchor.set(0.5, 0); waveNumberTxt.y = 120; waveNumberTxt.visible = false; LK.gui.top.addChild(waveNumberTxt); var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.visible = false; LK.gui.top.addChild(scoreTxt); var powerUps = []; var tripleShot = false; var tripleShotTimer = 0; var rapidFire = false; var rapidFireTimer = 0; var powerUpDuration = 180; // 3 seconds at 60fps // Power-up timer UI var powerUpTimerTxt = new Text2('', { size: 70, fill: 0xFFFFFF }); powerUpTimerTxt.anchor.set(0.5, 0.5); powerUpTimerTxt.x = 0; powerUpTimerTxt.y = 100; LK.gui.center.addChild(powerUpTimerTxt); var activePowerUpType = ''; var activePowerUpTimer = 0; var isShooting = false; var shootingTimer = 0; // Wave system var currentWave = 1; var lastWaveScore = 0; var waveMessage = null; var waveMessageTimer = 0; var waveCompleted = false; var upgradeMenuVisible = false; var gamePaused = false; // Upgrade system var cannonDamage = 1; var cannonAttackSpeedMultiplier = 1; // Wave completion tracking var enemiesKilledThisWave = 0; var enemiesRequiredPerWave = 15; // Base number of enemies to kill per wave // Cannon health system var enemyBullets = []; var healthKits = []; var shieldPowerUps = []; var guidedShotPowerUps = []; var guidedProjectiles = []; var guidedShot = false; var guidedShotTimer = 0; var guidedShotDuration = 420; // 7 seconds at 60fps // Cannon health UI - positioned next to cannon var cannonHealthBarBg = LK.getAsset('cannonHealthBarBg', { width: 400, height: 20, color: 0x444444, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); cannonHealthBarBg.x = cannon.x; cannonHealthBarBg.y = cannon.y - 150; cannonHealthBarBg.visible = false; game.addChild(cannonHealthBarBg); var cannonHealthBar = LK.getAsset('cannonHealthBar', { width: 396, height: 16, color: 0x00ff00, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); cannonHealthBar.x = cannon.x; cannonHealthBar.y = cannon.y - 150; cannonHealthBar.visible = false; game.addChild(cannonHealthBar); var healthText = new Text2('Health: 10/10', { size: 40, fill: 0xFFFFFF }); healthText.anchor.set(0.5, 0.5); healthText.x = cannon.x; healthText.y = cannon.y - 100; healthText.visible = false; game.addChild(healthText); // Wave message text var waveMessageTxt = new Text2('', { size: 100, fill: 0xFFFF00 }); waveMessageTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(waveMessageTxt); waveMessageTxt.visible = false; // Upgrade menu UI var upgradeMenu = new Container(); var upgradeTitle = new Text2('¡Oleada Completada! Elige una mejora:', { size: 80, fill: 0xFFFFFF }); upgradeTitle.anchor.set(0.5, 0.5); upgradeTitle.y = -400; upgradeMenu.addChild(upgradeTitle); // Create upgrade menu positioned in front of cannon upgradeMenu.x = cannon.x; upgradeMenu.y = cannon.y - 400; upgradeMenu.visible = false; game.addChild(upgradeMenu); // Damage upgrade option var damageUpgradeBtn = LK.getAsset('damageUpgrade', { width: 300, height: 150, color: 0xFF4444, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); damageUpgradeBtn.x = -300; damageUpgradeBtn.y = 0; upgradeMenu.addChild(damageUpgradeBtn); var damageUpgradeText = new Text2('Aumentar\nDaño', { size: 50, fill: 0xFFFFFF }); damageUpgradeText.anchor.set(0.5, 0.5); damageUpgradeText.x = -300; damageUpgradeText.y = 0; upgradeMenu.addChild(damageUpgradeText); // Health upgrade option var healthUpgradeBtn = LK.getAsset('healthUpgrade', { width: 300, height: 150, color: 0x44FF44, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); healthUpgradeBtn.x = 0; healthUpgradeBtn.y = 0; upgradeMenu.addChild(healthUpgradeBtn); var healthUpgradeText = new Text2('Vida +5', { size: 50, fill: 0xFFFFFF }); healthUpgradeText.anchor.set(0.5, 0.5); healthUpgradeText.x = 0; healthUpgradeText.y = 0; upgradeMenu.addChild(healthUpgradeText); // Attack speed upgrade option var speedUpgradeBtn = LK.getAsset('speedUpgrade', { width: 300, height: 150, color: 0x4444FF, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); speedUpgradeBtn.x = 300; speedUpgradeBtn.y = 0; upgradeMenu.addChild(speedUpgradeBtn); var speedUpgradeText = new Text2('Velocidad\nde Ataque', { size: 50, fill: 0xFFFFFF }); speedUpgradeText.anchor.set(0.5, 0.5); speedUpgradeText.x = 300; speedUpgradeText.y = 0; upgradeMenu.addChild(speedUpgradeText); // Upgrade button handlers - defined after buttons are created damageUpgradeBtn.down = function (x, y, obj) { if (upgradeMenuVisible) { cannonDamage += 1; hideUpgradeMenu(); } }; healthUpgradeBtn.down = function (x, y, obj) { if (upgradeMenuVisible) { cannon.maxHealth += 5; cannon.health += 5; // Update health UI var healthPercent = cannon.health / cannon.maxHealth; cannonHealthBar.scaleX = healthPercent; healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth); hideUpgradeMenu(); } }; speedUpgradeBtn.down = function (x, y, obj) { if (upgradeMenuVisible) { cannonAttackSpeedMultiplier += 0.3; hideUpgradeMenu(); } }; // Add interactive property to upgrade buttons to ensure they can receive events damageUpgradeBtn.interactive = true; healthUpgradeBtn.interactive = true; speedUpgradeBtn.interactive = true; // Track if boss has been spawned this wave var bossSpawnedThisWave = false; function spawnShip() { // Check if it's a boss wave (every 4 waves) and boss hasn't been spawned yet if (currentWave % 4 === 0 && currentWave >= 4 && !bossSpawnedThisWave) { // Check if there's already a boss in the ships array var bossExists = false; for (var i = 0; i < ships.length; i++) { if (ships[i].shipType === 'boss') { bossExists = true; break; } } // Only spawn boss if none exists if (!bossExists) { var boss = new Boss(); boss.init(currentWave); boss.x = -300; // Start further left due to larger size boss.y = 1366; // Center vertically boss.zIndex = 10; // Highest priority for rendering ships.push(boss); game.addChild(boss); game.children.sort(function (a, b) { return (a.zIndex || 0) - (b.zIndex || 0); }); bossSpawnedThisWave = true; } } // Regular ship spawning for all waves (including boss waves) var ship = new Ship(); var shipType; // Wave-based enemy spawning if (currentWave <= 2) { // Waves 1-2: Only small ships shipType = 'small'; } else if (currentWave <= 4) { // Waves 3-4: Small and medium ships var types = ['small', 'small', 'medium']; shipType = types[Math.floor(Math.random() * types.length)]; } else if (currentWave <= 6) { // Waves 5-6: Medium and some large ships var types = ['small', 'medium', 'medium', 'large']; shipType = types[Math.floor(Math.random() * types.length)]; } else { // Wave 7+: All ship types with more large ships var types = ['small', 'medium', 'large', 'large']; shipType = types[Math.floor(Math.random() * types.length)]; } ship.init(shipType); ship.x = -150; ship.y = 1000 + Math.random() * 1200; // Apply wave-based speed multiplier var speedMultiplier = 1 + (currentWave - 1) * 0.3; // If it's a boss wave, apply slower speed to all enemies if (currentWave % 4 === 0 && currentWave >= 4) { speedMultiplier = speedMultiplier * 0.5; // Make all enemies slower on boss waves } ship.speed = ship.speed * speedMultiplier; // Set zIndex based on ship type for proper layering if (ship.shipType === 'large') { ship.zIndex = 3; } else if (ship.shipType === 'medium') { ship.zIndex = 2; } else { ship.zIndex = 1; } game.children.sort(function (a, b) { return (a.zIndex || 0) - (b.zIndex || 0); }); ships.push(ship); game.addChild(ship); } function updateDifficulty() { difficultyTimer++; if (difficultyTimer % 1800 === 0) { shipSpawnDelay = Math.max(60, shipSpawnDelay - 10); } } function checkWaveTransition() { var currentScore = LK.getScore(); var newWave = Math.floor(currentScore / 1000) + 1; if (newWave > currentWave) { currentWave = newWave; lastWaveScore = Math.floor(currentScore / 1000) * 1000; // Reset boss spawning flag for new wave bossSpawnedThisWave = false; // Reset escaped ships counter for new wave shipsEscaped = 0; escapedShipsTxt.setText('Escaped: 0/10'); // Update wave number display waveNumberTxt.setText('Wave: ' + currentWave); // Increase max health by 2 each wave cannon.maxHealth += 2; cannon.health = cannon.maxHealth; // Restore to full health // Show wave message if (currentWave === 2) { waveMessageTxt.setText('Segunda Oleada'); } else if (currentWave === 3) { waveMessageTxt.setText('Tercera Oleada'); } else if (currentWave === 4) { waveMessageTxt.setText('¡JEFE! - Oleada 4'); } else if (currentWave === 5) { waveMessageTxt.setText('Quinta Oleada'); } else if (currentWave % 4 === 0) { waveMessageTxt.setText('¡JEFE! - Oleada ' + currentWave); } else { waveMessageTxt.setText('Oleada ' + currentWave); } waveMessageTxt.visible = true; waveMessageTimer = 180; // Show for 3 seconds // Increase difficulty shipSpawnDelay = Math.max(30, 120 - (currentWave - 1) * 15); // Increase ship speeds for (var i = 0; i < ships.length; i++) { var ship = ships[i]; var speedMultiplier = 1 + (currentWave - 1) * 0.3; ship.speed = ship.speed * speedMultiplier; } } } // Touch/click handlers game.down = function (x, y, obj) { if (!gameStarted) return; // Handle upgrade menu clicks - allow shooting at buttons if (upgradeMenuVisible) { // Convert click coordinates to upgrade menu local coordinates var menuLocalX = x - upgradeMenu.x; var menuLocalY = y - upgradeMenu.y; // Check damage upgrade button (positioned at -300, 0 in upgrade menu) if (menuLocalX >= -450 && menuLocalX <= -150 && menuLocalY >= -75 && menuLocalY <= 75) { cannonDamage += 1; hideUpgradeMenu(); return; } // Check health upgrade button (positioned at 0, 0 in upgrade menu) if (menuLocalX >= -150 && menuLocalX <= 150 && menuLocalY >= -75 && menuLocalY <= 75) { cannon.maxHealth += 5; cannon.health += 5; // Update health UI var healthPercent = cannon.health / cannon.maxHealth; cannonHealthBar.scaleX = healthPercent; healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth); hideUpgradeMenu(); return; } // Check speed upgrade button (positioned at 300, 0 in upgrade menu) if (menuLocalX >= 150 && menuLocalX <= 450 && menuLocalY >= -75 && menuLocalY <= 75) { cannonAttackSpeedMultiplier += 0.3; hideUpgradeMenu(); return; } // Allow aiming even when upgrade menu is visible cannon.aimAt(x, y); // Allow shooting at upgrade buttons - fire cannonball to hit buttons isShooting = true; shootingTimer = 0; var newCannonballs = cannon.fire(); if (newCannonballs) { for (var c = 0; c < newCannonballs.length; c++) { cannonballs.push(newCannonballs[c]); game.addChild(newCannonballs[c]); } } return; } cannon.aimAt(x, y); isShooting = true; shootingTimer = 0; // Fire immediately on first click var newCannonballs = cannon.fire(); if (newCannonballs) { for (var c = 0; c < newCannonballs.length; c++) { cannonballs.push(newCannonballs[c]); game.addChild(newCannonballs[c]); } } }; game.up = function (x, y, obj) { if (!gameStarted || upgradeMenuVisible) return; isShooting = false; }; // Play button event handler playButtonImage.down = function (x, y, obj) { if (!gameStarted) { gameStarted = true; startMenu.visible = false; cannon.visible = true; cannonHealthBarBg.visible = true; cannonHealthBar.visible = true; healthText.visible = true; scoreTxt.visible = true; escapedShipsTxt.visible = true; waveNumberTxt.visible = true; // Stop button animations tween.stop(oceanTitleImage); tween.stop(defenderTitleImage); tween.stop(playButtonImage); // Stop menu music first, then switch to game music LK.stopMusic(); LK.playMusic('calmSeaMelody'); } }; game.update = function () { if (!gameStarted) return; if (gamePaused) return; // Don't update when upgrade menu is showing // Handle continuous shooting only if upgrade menu is not visible if (isShooting && !upgradeMenuVisible) { shootingTimer++; var currentFireDelay = rapidFire ? cannon.fireDelay / 3 : cannon.fireDelay; var fireDelayInTicks = currentFireDelay / 16.67; // Convert ms to ticks if (shootingTimer >= fireDelayInTicks) { var newCannonballs = cannon.fire(); if (newCannonballs) { for (var c = 0; c < newCannonballs.length; c++) { cannonballs.push(newCannonballs[c]); game.addChild(newCannonballs[c]); } } shootingTimer = 0; } } updateDifficulty(); checkWaveTransition(); // Update wave message timer if (waveMessageTimer > 0) { waveMessageTimer--; if (waveMessageTimer <= 0) { waveMessageTxt.visible = false; } } shipSpawnTimer++; if (shipSpawnTimer >= shipSpawnDelay) { spawnShip(); shipSpawnTimer = 0; } for (var i = ships.length - 1; i >= 0; i--) { var ship = ships[i]; if (ship.lastX === undefined) ship.lastX = ship.x; if (ship.lastX <= 2048 && ship.x > 2048) { shipsEscaped++; escapedShipsTxt.setText('Escaped: ' + shipsEscaped + '/10'); LK.getSound('shipEscape').play(); ship.destroy(); ships.splice(i, 1); // Check if too many ships escaped if (shipsEscaped >= maxEscapedShips) { // Show final score and wave reached var finalScore = LK.getScore(); var finalWave = currentWave; LK.showGameOver('Puntaje: ' + finalScore + '\nOleada alcanzada: ' + finalWave); return; } continue; } // Ships shoot at cannon if (ship.x > 200 && ship.x < 1800) { // Only shoot when in range var newBullet = ship.shootAtCannon(cannon.x, cannon.y); if (newBullet) { enemyBullets.push(newBullet); game.addChild(newBullet); } } ship.lastX = ship.x; } for (var j = cannonballs.length - 1; j >= 0; j--) { var cannonball = cannonballs[j]; if (cannonball.lastX === undefined) cannonball.lastX = cannonball.x; if (cannonball.lastY === undefined) cannonball.lastY = cannonball.y; if (cannonball.lastX >= 0 && cannonball.x < 0 || cannonball.lastX <= 2048 && cannonball.x > 2048 || cannonball.lastY >= 0 && cannonball.y < 0 || cannonball.lastY <= 2732 && cannonball.y > 2732) { cannonball.destroy(); cannonballs.splice(j, 1); continue; } // Check collision with upgrade buttons when upgrade menu is visible if (upgradeMenuVisible) { // Check collision with damage upgrade button var damageButtonX = upgradeMenu.x + damageUpgradeBtn.x; var damageButtonY = upgradeMenu.y + damageUpgradeBtn.y; if (cannonball.x >= damageButtonX - 150 && cannonball.x <= damageButtonX + 150 && cannonball.y >= damageButtonY - 75 && cannonball.y <= damageButtonY + 75) { cannonDamage += 1; LK.effects.flashScreen(0xff4444, 300); // Visual feedback for damage upgrade hideUpgradeMenu(); cannonball.destroy(); cannonballs.splice(j, 1); continue; } // Check collision with health upgrade button var healthButtonX = upgradeMenu.x + healthUpgradeBtn.x; var healthButtonY = upgradeMenu.y + healthUpgradeBtn.y; if (cannonball.x >= healthButtonX - 150 && cannonball.x <= healthButtonX + 150 && cannonball.y >= healthButtonY - 75 && cannonball.y <= healthButtonY + 75) { cannon.maxHealth += 5; cannon.health += 5; var healthPercent = cannon.health / cannon.maxHealth; cannonHealthBar.scaleX = healthPercent; healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth); LK.effects.flashScreen(0x44ff44, 300); // Visual feedback for health upgrade hideUpgradeMenu(); cannonball.destroy(); cannonballs.splice(j, 1); continue; } // Check collision with speed upgrade button var speedButtonX = upgradeMenu.x + speedUpgradeBtn.x; var speedButtonY = upgradeMenu.y + speedUpgradeBtn.y; if (cannonball.x >= speedButtonX - 150 && cannonball.x <= speedButtonX + 150 && cannonball.y >= speedButtonY - 75 && cannonball.y <= speedButtonY + 75) { cannonAttackSpeedMultiplier += 0.3; LK.effects.flashScreen(0x4444ff, 300); // Visual feedback for speed upgrade hideUpgradeMenu(); cannonball.destroy(); cannonballs.splice(j, 1); continue; } } for (var k = ships.length - 1; k >= 0; k--) { var ship = ships[k]; // Ensure ship is valid and has proper collision detection, and not dying if (ship && ship.graphics && ship.graphics.visible && !ship.isDying && cannonball.intersects(ship)) { ship.health -= cannonball.damage * cannonDamage; ship.updateHealthBar(); LK.effects.flashObject(ship, 0xff0000, 200); LK.getSound('hit').play(); cannonball.destroy(); cannonballs.splice(j, 1); if (ship.health <= 0) { enemiesKilledThisWave++; // Track kills for wave completion ship.isDying = true; // Mark as dying to prevent further damage LK.setScore(LK.getScore() + ship.points); scoreTxt.setText('Score: ' + LK.getScore()); LK.getSound('shipDestroyed').play(); // Add small jump death animation var originalY = ship.y; var deadShip = ship; // Store reference to avoid closure issues tween(ship, { y: originalY - 30, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(deadShip, { y: originalY + 10, scaleX: 0.8, scaleY: 0.8 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { // Random chance to spawn different power-ups (45% total chance) var spawnChance = Math.random(); if (spawnChance < 0.15) { // 15% chance for weapon power-ups var powerUp = new PowerUp(); var powerTypes = ['tripleShot', 'rapidFire']; var randomPowerType = powerTypes[Math.floor(Math.random() * powerTypes.length)]; powerUp.init(randomPowerType); powerUp.x = deadShip.x; powerUp.y = deadShip.y; powerUps.push(powerUp); game.addChild(powerUp); // Add brilliant glowing animation to power-up powerUp.graphics.tint = 0xffffff; tween(powerUp.graphics, { tint: 0xffff00, scaleX: 1.3, scaleY: 1.3 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(powerUp.graphics, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { // Continue pulsing glow function continuePulse() { if (powerUp.parent) { tween(powerUp.graphics, { tint: 0xffff00, scaleX: 1.2, scaleY: 1.2 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (powerUp.parent) { tween(powerUp.graphics, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 600, easing: tween.easeInOut, onFinish: continuePulse }); } } }); } } continuePulse(); } }); } }); } else if (spawnChance < 0.25) { // 10% chance for health kit var healthKit = new HealthKit(); healthKit.x = deadShip.x; healthKit.y = deadShip.y; healthKits.push(healthKit); game.addChild(healthKit); // Add brilliant glowing animation to health kit healthKit.tint = 0xffffff; tween(healthKit, { tint: 0x00ff00, scaleX: 1.3, scaleY: 1.3 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(healthKit, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { // Continue pulsing glow function continuePulse() { if (healthKit.parent) { tween(healthKit, { tint: 0x00ff00, scaleX: 1.15, scaleY: 1.15 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (healthKit.parent) { tween(healthKit, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: continuePulse }); } } }); } } continuePulse(); } }); } }); } else if (spawnChance < 0.35) { // 15% chance for guided shot var guidedShotPowerUp = new GuidedShotPowerUp(); guidedShotPowerUp.x = deadShip.x; guidedShotPowerUp.y = deadShip.y; guidedShotPowerUps.push(guidedShotPowerUp); game.addChild(guidedShotPowerUp); // Add brilliant glowing animation to guided shot power-up guidedShotPowerUp.tint = 0xffffff; tween(guidedShotPowerUp, { tint: 0xff6600, scaleX: 1.3, scaleY: 1.3 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(guidedShotPowerUp, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { // Continue pulsing glow function continuePulse() { if (guidedShotPowerUp.parent) { tween(guidedShotPowerUp, { tint: 0xff6600, scaleX: 1.2, scaleY: 1.2 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { if (guidedShotPowerUp.parent) { tween(guidedShotPowerUp, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 700, easing: tween.easeInOut, onFinish: continuePulse }); } } }); } } continuePulse(); } }); } }); } else if (spawnChance < 0.45) { // 10% chance for shield var shieldPowerUp = new ShieldPowerUp(); shieldPowerUp.x = deadShip.x; shieldPowerUp.y = deadShip.y; shieldPowerUps.push(shieldPowerUp); game.addChild(shieldPowerUp); // Add brilliant glowing animation to shield power-up shieldPowerUp.tint = 0xffffff; tween(shieldPowerUp, { tint: 0x0099ff, scaleX: 1.3, scaleY: 1.3 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(shieldPowerUp, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { // Continue pulsing glow function continuePulse() { if (shieldPowerUp.parent) { tween(shieldPowerUp, { tint: 0x0099ff, scaleX: 1.2, scaleY: 1.2 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { if (shieldPowerUp.parent) { tween(shieldPowerUp, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 700, easing: tween.easeInOut, onFinish: continuePulse }); } } }); } } continuePulse(); } }); } }); } if (deadShip.parent) { deadShip.destroy(); } // Remove from ships array by finding the ship for (var removeIndex = ships.length - 1; removeIndex >= 0; removeIndex--) { if (ships[removeIndex] === deadShip) { ships.splice(removeIndex, 1); break; } } } }); } }); } break; } } if (cannonballs[j]) { cannonball.lastX = cannonball.x; cannonball.lastY = cannonball.y; } } // Update power-ups for (var p = powerUps.length - 1; p >= 0; p--) { var powerUp = powerUps[p]; if (powerUp.lastX === undefined) powerUp.lastX = powerUp.x; // Remove power-up if it goes off screen if (powerUp.lastX <= 2048 && powerUp.x > 2048) { powerUp.destroy(); powerUps.splice(p, 1); continue; } // Check collision with cannonballs for collection for (var cb = cannonballs.length - 1; cb >= 0; cb--) { var cannonball = cannonballs[cb]; if (cannonball.intersects(powerUp)) { // Collect power-up LK.getSound('powerUpCollect').play(); LK.effects.flashScreen(0x00ff00, 300); // Clear any existing power-up to prevent accumulation tripleShot = false; rapidFire = false; activePowerUpTimer = 0; // Reset timer to prevent accumulation // Activate new power-up if (powerUp.powerType === 'tripleShot') { tripleShot = true; activePowerUpType = 'Triple Shot'; activePowerUpTimer = powerUpDuration; } else if (powerUp.powerType === 'rapidFire') { rapidFire = true; activePowerUpType = 'Rapid Fire'; activePowerUpTimer = powerUpDuration; } powerUp.destroy(); powerUps.splice(p, 1); break; } } if (powerUps[p]) { powerUp.lastX = powerUp.x; } } // Update enemy bullets for (var eb = enemyBullets.length - 1; eb >= 0; eb--) { var enemyBullet = enemyBullets[eb]; if (enemyBullet.lastY === undefined) enemyBullet.lastY = enemyBullet.y; // Remove if off screen if (enemyBullet.lastY <= 2732 && enemyBullet.y > 2732) { enemyBullet.destroy(); enemyBullets.splice(eb, 1); continue; } // Check collision with cannon if (enemyBullet.intersects(cannon)) { cannon.takeDamage(enemyBullet.damage); enemyBullet.destroy(); enemyBullets.splice(eb, 1); // Update health UI var healthPercent = cannon.health / cannon.maxHealth; cannonHealthBar.scaleX = healthPercent; if (healthPercent > 0.6) { cannonHealthBar.tint = 0x00FF00; } else if (healthPercent > 0.3) { cannonHealthBar.tint = 0xFFFF00; } else { cannonHealthBar.tint = 0xFF0000; } healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth); // Check game over if (cannon.health <= 0) { // Show final score and wave reached var finalScore = LK.getScore(); var finalWave = currentWave; LK.showGameOver('Puntaje: ' + finalScore + '\nOleada alcanzada: ' + finalWave); return; } continue; } if (enemyBullets[eb]) { enemyBullet.lastY = enemyBullet.y; } } // Update health kits for (var hk = healthKits.length - 1; hk >= 0; hk--) { var healthKit = healthKits[hk]; if (healthKit.lastX === undefined) healthKit.lastX = healthKit.x; // Remove if off screen if (healthKit.lastX <= 2048 && healthKit.x > 2048) { healthKit.destroy(); healthKits.splice(hk, 1); continue; } // Check collection by cannonball for (var cb = cannonballs.length - 1; cb >= 0; cb--) { var cannonball = cannonballs[cb]; if (cannonball.intersects(healthKit)) { cannon.heal(healthKit.healAmount); LK.getSound('powerUpCollect').play(); LK.effects.flashScreen(0x00ff00, 200); // Update health UI var healthPercent = cannon.health / cannon.maxHealth; cannonHealthBar.scaleX = healthPercent; if (healthPercent > 0.6) { cannonHealthBar.tint = 0x00FF00; } else if (healthPercent > 0.3) { cannonHealthBar.tint = 0xFFFF00; } else { cannonHealthBar.tint = 0xFF0000; } healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth); healthKit.destroy(); healthKits.splice(hk, 1); break; } } if (healthKits[hk]) { healthKit.lastX = healthKit.x; } } // Update shield power-ups for (var sp = shieldPowerUps.length - 1; sp >= 0; sp--) { var shieldPowerUp = shieldPowerUps[sp]; if (shieldPowerUp.lastX === undefined) shieldPowerUp.lastX = shieldPowerUp.x; // Remove if off screen if (shieldPowerUp.lastX <= 2048 && shieldPowerUp.x > 2048) { shieldPowerUp.destroy(); shieldPowerUps.splice(sp, 1); continue; } // Check collection by cannonball for (var cb = cannonballs.length - 1; cb >= 0; cb--) { var cannonball = cannonballs[cb]; if (cannonball.intersects(shieldPowerUp)) { cannon.activateShield(); LK.getSound('powerUpCollect').play(); LK.effects.flashScreen(0x0099ff, 300); // Add brilliant glowing animation to cannon when shield is collected tween(cannon.graphics, { tint: 0x88ccff, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(cannon.graphics, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeInOut }); } }); shieldPowerUp.destroy(); shieldPowerUps.splice(sp, 1); break; } } if (shieldPowerUps[sp]) { shieldPowerUp.lastX = shieldPowerUp.x; } } // Update guided shot power-ups for (var gsp = guidedShotPowerUps.length - 1; gsp >= 0; gsp--) { var guidedShotPowerUp = guidedShotPowerUps[gsp]; if (guidedShotPowerUp.lastX === undefined) guidedShotPowerUp.lastX = guidedShotPowerUp.x; // Remove if off screen if (guidedShotPowerUp.lastX <= 2048 && guidedShotPowerUp.x > 2048) { guidedShotPowerUp.destroy(); guidedShotPowerUps.splice(gsp, 1); continue; } // Check collection by cannonball for (var cb = cannonballs.length - 1; cb >= 0; cb--) { var cannonball = cannonballs[cb]; if (cannonball.intersects(guidedShotPowerUp)) { // Clear existing power-ups tripleShot = false; rapidFire = false; guidedShot = true; activePowerUpType = 'Guided Shot'; activePowerUpTimer = guidedShotDuration; LK.getSound('powerUpCollect').play(); LK.effects.flashScreen(0xff6600, 300); guidedShotPowerUp.destroy(); guidedShotPowerUps.splice(gsp, 1); break; } } if (guidedShotPowerUps[gsp]) { guidedShotPowerUp.lastX = guidedShotPowerUp.x; } } // Update guided projectiles for (var gp = guidedProjectiles.length - 1; gp >= 0; gp--) { var guidedProjectile = guidedProjectiles[gp]; // Check if projectile has exceeded its lifetime (4 seconds) if (LK.ticks - guidedProjectile.creationTime >= guidedProjectile.lifetime) { guidedProjectile.destroy(); guidedProjectiles.splice(gp, 1); continue; } // Check collision with enemy ships var hitShip = false; for (var k = ships.length - 1; k >= 0; k--) { var ship = ships[k]; // Ensure ship is valid and has proper collision detection, and not dying if (ship && ship.graphics && ship.graphics.visible && !ship.isDying && guidedProjectile.intersects(ship)) { ship.health -= guidedProjectile.damage - 2 + cannonDamage + 2; // Base guided damage plus cannon damage bonus ship.updateHealthBar(); LK.effects.flashObject(ship, 0xff0000, 200); LK.getSound('hit').play(); guidedProjectile.destroy(); guidedProjectiles.splice(gp, 1); hitShip = true; if (ship.health <= 0) { enemiesKilledThisWave++; // Track kills for wave completion ship.isDying = true; // Mark as dying to prevent further damage LK.setScore(LK.getScore() + ship.points); scoreTxt.setText('Score: ' + LK.getScore()); LK.getSound('shipDestroyed').play(); // Add small jump death animation var originalY = ship.y; var deadShip = ship; // Store reference to avoid closure issues tween(ship, { y: originalY - 30, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(deadShip, { y: originalY + 10, scaleX: 0.8, scaleY: 0.8 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { if (deadShip.parent) { deadShip.destroy(); } // Remove from ships array by finding the ship for (var removeIndex = ships.length - 1; removeIndex >= 0; removeIndex--) { if (ships[removeIndex] === deadShip) { ships.splice(removeIndex, 1); break; } } } }); } }); } break; } } if (hitShip) continue; // Remove if too far off screen if (guidedProjectile.x < -200 || guidedProjectile.x > 2248 || guidedProjectile.y < -200 || guidedProjectile.y > 2932) { guidedProjectile.destroy(); guidedProjectiles.splice(gp, 1); } } // Update cannon shield if (cannon.isShielded && cannon.shieldTimer > 0) { cannon.shieldTimer--; // Show shield effect when active if (!shieldEffect.visible) { shieldEffect.visible = true; shieldEffect.x = cannon.x; shieldEffect.y = cannon.y; } if (cannon.shieldTimer <= 0) { cannon.isShielded = false; shieldEffect.visible = false; tween(cannon.graphics, { tint: 0xffffff }, { duration: 500 }); } } else { shieldEffect.visible = false; } // Update power-up timer if (activePowerUpTimer > 0) { activePowerUpTimer--; // Update timer display var timeLeft = Math.ceil(activePowerUpTimer / 60); var timerText = activePowerUpType + ': ' + timeLeft + 's'; if (cannon.isShielded) { var shieldTimeLeft = Math.ceil(cannon.shieldTimer / 60); timerText += ' | Shield: ' + shieldTimeLeft + 's'; } powerUpTimerTxt.setText(timerText); // Check if power-up expired if (activePowerUpTimer <= 0) { tripleShot = false; rapidFire = false; guidedShot = false; activePowerUpType = ''; if (cannon.isShielded) { var shieldTimeLeft = Math.ceil(cannon.shieldTimer / 60); powerUpTimerTxt.setText('Shield: ' + shieldTimeLeft + 's'); } else { powerUpTimerTxt.setText(''); } } } else { // No active weapon power-up, but maybe shield if (cannon.isShielded) { var shieldTimeLeft = Math.ceil(cannon.shieldTimer / 60); powerUpTimerTxt.setText('Shield: ' + shieldTimeLeft + 's'); } else { powerUpTimerTxt.setText(''); } } // Check for wave completion at end of update loop checkWaveCompletion(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
self.shipType = 'boss';
self.speed = 0.5; // Very slow base speed
self.points = 500; // High points for boss
self.graphics = null;
self.healthBar = null;
self.healthBarBg = null;
self.isDying = false;
self.init = function (wave) {
self.shipType = 'boss';
self.graphics = self.attachAsset('bossShip', {
anchorX: 0.5,
anchorY: 0.5
});
// Make boss larger and more intimidating
self.graphics.scaleX = 1.5;
self.graphics.scaleY = 1.5;
// Slow speed that slightly increases with wave
self.speed = 0.5 + (wave - 4) * 0.1;
self.points = 500 + (wave - 4) * 100; // Scaling points
self.health = 40;
self.maxHealth = 40;
// Create larger health bar for boss
var healthBarWidth = 300;
self.healthBarBg = LK.getAsset('healthBarBg', {
width: healthBarWidth,
height: 20,
color: 0x444444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.healthBarBg.y = -self.graphics.height / 2 - 40;
self.addChild(self.healthBarBg);
self.healthBar = LK.getAsset('healthBar', {
width: healthBarWidth - 4,
height: 16,
color: 0xFF0000,
// Red health bar for boss
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.healthBar.y = -self.graphics.height / 2 - 40;
self.addChild(self.healthBar);
// Boss shoots more frequently
self.lastShotTime = 0;
self.shotDelay = 90; // 1.5 seconds
};
self.updateHealthBar = function () {
if (self.healthBar && self.maxHealth > 1) {
var healthPercent = self.health / self.maxHealth;
self.healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
self.healthBar.tint = 0xFF4444; // Dark red
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xFF0000; // Red
} else {
self.healthBar.tint = 0x880000; // Dark red
}
}
};
self.update = function () {
self.x += self.speed;
if (self.graphics) {
self.graphics.visible = true;
}
};
self.canShoot = function () {
return LK.ticks - self.lastShotTime >= self.shotDelay;
};
self.shootAtCannon = function (cannonX, cannonY) {
if (!self.canShoot()) return null;
self.lastShotTime = LK.ticks;
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var dx = cannonX - self.x;
var dy = cannonY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * bullet.speed;
bullet.velocityY = dy / distance * bullet.speed;
bullet.damage = 2; // Boss bullets do more damage
return bullet;
};
return self;
});
var Cannon = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('cannon', {
anchorX: 0.5,
anchorY: 0.5
});
self.targetX = 0;
self.targetY = 0;
self.lastFireTime = 0;
self.fireDelay = 300;
self.health = 10;
self.maxHealth = 10;
self.isShielded = false;
self.shieldTimer = 0;
self.aimAt = function (x, y) {
self.targetX = x;
self.targetY = y;
var dx = x - self.x;
var dy = y - self.y;
var angle = Math.atan2(dy, dx);
self.graphics.rotation = angle;
};
self.canFire = function () {
var currentFireDelay = rapidFire ? self.fireDelay / 3 : self.fireDelay;
currentFireDelay = currentFireDelay / cannonAttackSpeedMultiplier;
return LK.ticks * 16.67 - self.lastFireTime >= currentFireDelay;
};
self.fire = function () {
if (!self.canFire()) return null;
self.lastFireTime = LK.ticks * 16.67;
var cannonballs = [];
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var baseAngle = Math.atan2(dy, dx);
if (guidedShot) {
// Fire guided projectile
var guidedProjectile = new GuidedProjectile();
guidedProjectile.x = self.x - 20;
guidedProjectile.y = self.y - 25;
guidedProjectile.targetX = self.targetX;
guidedProjectile.targetY = self.targetY;
guidedProjectile.lastTargetUpdateTime = LK.ticks;
guidedProjectile.creationTime = LK.ticks; // Set creation time
guidedProjectiles.push(guidedProjectile);
game.addChild(guidedProjectile);
} else if (tripleShot) {
// Fire three cannonballs with slight angle differences
for (var i = 0; i < 3; i++) {
var cannonball = new Cannonball();
cannonball.x = self.x - 20;
cannonball.y = self.y - 25;
var angleOffset = (i - 1) * 0.2; // -0.2, 0, 0.2 radians
var adjustedAngle = baseAngle + angleOffset;
cannonball.velocityX = Math.cos(adjustedAngle) * cannonball.speed;
cannonball.velocityY = Math.sin(adjustedAngle) * cannonball.speed;
cannonballs.push(cannonball);
}
} else {
// Fire single cannonball
var cannonball = new Cannonball();
cannonball.x = self.x - 20;
cannonball.y = self.y - 25;
cannonball.velocityX = dx / distance * cannonball.speed;
cannonball.velocityY = dy / distance * cannonball.speed;
cannonballs.push(cannonball);
}
LK.getSound('shoot').play();
return cannonballs;
};
self.takeDamage = function (damage) {
if (self.isShielded) {
damage = Math.floor(damage / 2); // Shield reduces damage by half
}
self.health -= damage;
if (self.health < 0) self.health = 0;
// Flash red when taking damage
tween(self.graphics, {
tint: 0xff0000
}, {
duration: 100,
onFinish: function onFinish() {
tween(self.graphics, {
tint: 0xffffff
}, {
duration: 100
});
}
});
};
self.heal = function (amount) {
self.health += amount;
if (self.health > self.maxHealth) self.health = self.maxHealth;
// Flash green when healing
tween(self.graphics, {
tint: 0x00ff00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self.graphics, {
tint: 0xffffff
}, {
duration: 200
});
}
});
};
self.activateShield = function () {
self.isShielded = true;
self.shieldTimer = 900; // 15 seconds at 60fps
self.graphics.tint = 0x88ccff; // Blue tint for shield
};
return self;
});
var Cannonball = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('cannonball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 16;
self.damage = 1; // Base damage, will be multiplied by cannonDamage when hitting
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 8;
self.damage = 1;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
};
return self;
});
var GuidedProjectile = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('guidedProjectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 16;
self.damage = 3; // Base damage for guided projectiles (will be enhanced by cannonDamage)
self.targetX = 0;
self.targetY = 0;
self.lastTargetUpdateTime = 0;
self.targetUpdateDelay = 30; // Update target every 0.5 seconds
self.creationTime = 0; // Track when the projectile was created
self.lifetime = 120; // 2 seconds at 60fps (2 * 60)
self.update = function () {
// Update target position periodically
if (LK.ticks - self.lastTargetUpdateTime >= self.targetUpdateDelay) {
// Find nearest enemy ship
var nearestShip = null;
var nearestDistance = Infinity;
for (var i = 0; i < ships.length; i++) {
var ship = ships[i];
if (ship && ship.graphics && ship.graphics.visible && !ship.isDying) {
var dx = ship.x - self.x;
var dy = ship.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestShip = ship;
}
}
}
// If we found a ship, target it
if (nearestShip) {
self.targetX = nearestShip.x;
self.targetY = nearestShip.y;
}
self.lastTargetUpdateTime = LK.ticks;
}
// Move toward target
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Add slight rotation for visual effect
graphics.rotation += 0.1;
};
return self;
});
var GuidedShotPowerUp = Container.expand(function () {
var self = Container.call(this);
self.speed = 2;
var graphics = self.attachAsset('guidedShotPowerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x += self.speed;
};
return self;
});
var HealthKit = Container.expand(function () {
var self = Container.call(this);
self.speed = 2;
self.healAmount = 2;
var graphics = self.attachAsset('healthKit', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a cross symbol
var crossV = LK.getAsset('healthKit', {
width: 20,
height: 60,
color: 0xffffff,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
var crossH = LK.getAsset('healthKit', {
width: 60,
height: 20,
color: 0xffffff,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(crossV);
self.addChild(crossH);
self.update = function () {
self.x += self.speed;
};
return self;
});
var PowerUp = Container.expand(function () {
var self = Container.call(this);
self.powerType = 'tripleShot';
self.speed = 2;
self.graphics = null;
self.init = function (type) {
self.powerType = type;
if (type === 'tripleShot') {
self.graphics = self.attachAsset('tripleShotPowerUp', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'rapidFire') {
self.graphics = self.attachAsset('rapidFirePowerUp', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
self.update = function () {
self.x += self.speed;
};
return self;
});
var ShieldPowerUp = Container.expand(function () {
var self = Container.call(this);
self.speed = 2;
var graphics = self.attachAsset('shieldPowerUp', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.x += self.speed;
};
return self;
});
var Ship = Container.expand(function () {
var self = Container.call(this);
self.shipType = 'small';
self.speed = 2;
self.points = 10;
self.graphics = null;
self.healthBar = null;
self.healthBarBg = null;
self.isDying = false; // Flag to prevent damage during death animation
self.init = function (type) {
self.shipType = type;
if (type === 'small') {
self.graphics = self.attachAsset('smallShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
// Small ships: 100 points in waves 1-2, then scale with wave
if (currentWave <= 2) {
self.points = 100;
} else {
self.points = 10 + (currentWave - 2) * 5; // Gradual increase after wave 2
}
self.health = 1;
self.maxHealth = 1;
} else if (type === 'medium') {
self.graphics = self.attachAsset('mediumShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
// Medium ships: 100 points when first appearing (waves 3-4), then scale
if (currentWave <= 4) {
self.points = 100;
} else {
self.points = 25 + (currentWave - 4) * 10; // Gradual increase after wave 4
}
self.health = 3;
self.maxHealth = 3;
} else if (type === 'large') {
self.graphics = self.attachAsset('largeShip', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1.5;
// Large ships: 100 points when first appearing (waves 5-6), then scale
if (currentWave <= 6) {
self.points = 100;
} else {
self.points = 50 + (currentWave - 6) * 15; // Gradual increase after wave 6
}
self.health = 5;
self.maxHealth = 5;
}
// Create health bar background - wider for better visibility
var healthBarWidth = Math.max(120, self.graphics.width * 1.2);
self.healthBarBg = LK.getAsset('healthBarBg', {
width: healthBarWidth,
height: 12,
color: 0x444444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.healthBarBg.y = -self.graphics.height / 2 - 20;
self.addChild(self.healthBarBg);
// Create health bar foreground - wider for better visibility
self.healthBar = LK.getAsset('healthBar', {
width: healthBarWidth - 4,
height: 8,
color: 0x00FF00,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
self.healthBar.y = -self.graphics.height / 2 - 20;
self.addChild(self.healthBar);
// Show health bar for all ships now - no hiding for small ships
// Set shooting properties based on ship type
self.lastShotTime = 0;
if (type === 'small') {
self.shotDelay = 180; // 3 seconds
} else if (type === 'medium') {
self.shotDelay = 150; // 2.5 seconds
} else if (type === 'large') {
self.shotDelay = 120; // 2 seconds
}
};
self.updateHealthBar = function () {
if (self.healthBar && self.maxHealth > 1) {
var healthPercent = self.health / self.maxHealth;
self.healthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xFFFF00; // Yellow
} else {
self.healthBar.tint = 0xFF0000; // Red
}
}
};
self.update = function () {
self.x += self.speed;
// Ensure collision detection remains active
if (self.graphics) {
self.graphics.visible = true;
}
};
self.canShoot = function () {
return LK.ticks - self.lastShotTime >= self.shotDelay;
};
self.shootAtCannon = function (cannonX, cannonY) {
if (!self.canShoot()) return null;
self.lastShotTime = LK.ticks;
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var dx = cannonX - self.x;
var dy = cannonY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.velocityX = dx / distance * bullet.speed;
bullet.velocityY = dy / distance * bullet.speed;
return bullet;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x001122
});
/****
* Game Code
****/
var oceanBackground = LK.getAsset('oceanBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(oceanBackground);
// Start menu variables
var gameStarted = false;
var startMenu = new Container();
game.addChild(startMenu);
// Ocean title image
var oceanTitleImage = startMenu.attachAsset('oceanTitle', {
anchorX: 0.5,
anchorY: 0.5
});
oceanTitleImage.x = 1024;
oceanTitleImage.y = 750;
// Defender title image
var defenderTitleImage = startMenu.attachAsset('defenderTitle', {
anchorX: 0.5,
anchorY: 0.5
});
defenderTitleImage.x = 1024;
defenderTitleImage.y = 1600;
// Play button image
var playButtonImage = startMenu.attachAsset('playButtonImage', {
anchorX: 0.5,
anchorY: 0.5
});
playButtonImage.x = 1024;
playButtonImage.y = 2300;
// Add pulsating animation to ocean title
function pulseOceanTitle() {
tween(oceanTitleImage, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(oceanTitleImage, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: pulseOceanTitle
});
}
});
}
pulseOceanTitle();
// Add pulsating animation to defender title
function pulseDefenderTitle() {
tween(defenderTitleImage, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1200,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(defenderTitleImage, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1200,
easing: tween.easeInOut,
onFinish: pulseDefenderTitle
});
}
});
}
pulseDefenderTitle();
// Add pulsating animation to play button
function pulsePlayButton() {
tween(playButtonImage, {
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(playButtonImage, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: pulsePlayButton
});
}
});
}
pulsePlayButton();
// Start playing the menu music
LK.playMusic('menuMusic');
function showUpgradeMenu() {
upgradeMenuVisible = true;
gamePaused = true;
upgradeMenu.visible = true;
}
function hideUpgradeMenu() {
upgradeMenuVisible = false;
gamePaused = false;
upgradeMenu.visible = false;
enemiesKilledThisWave = 0;
waveCompleted = false;
}
function checkWaveCompletion() {
// Check for 1000 point milestone upgrade
if (LK.getScore() >= 1000 && LK.getScore() % 1000 === 0 && !upgradeMenuVisible) {
// Destroy all remaining enemies
for (var i = ships.length - 1; i >= 0; i--) {
var ship = ships[i];
if (ship && ship.parent) {
ship.destroy();
ships.splice(i, 1);
}
}
// Clear all enemy bullets
for (var j = enemyBullets.length - 1; j >= 0; j--) {
var bullet = enemyBullets[j];
if (bullet && bullet.parent) {
bullet.destroy();
enemyBullets.splice(j, 1);
}
}
showUpgradeMenu();
return;
}
var requiredKills = enemiesRequiredPerWave + (currentWave - 1) * 5;
if (enemiesKilledThisWave >= requiredKills && !waveCompleted) {
waveCompleted = true;
// Destroy all remaining enemies
for (var i = ships.length - 1; i >= 0; i--) {
var ship = ships[i];
if (ship && ship.parent) {
ship.destroy();
ships.splice(i, 1);
}
}
// Clear all enemy bullets
for (var j = enemyBullets.length - 1; j >= 0; j--) {
var bullet = enemyBullets[j];
if (bullet && bullet.parent) {
bullet.destroy();
enemyBullets.splice(j, 1);
}
}
showUpgradeMenu();
}
}
var cannon = game.addChild(new Cannon());
cannon.x = 1024;
cannon.y = 2600;
cannon.visible = false;
// Shield effect visual
var shieldEffect = LK.getAsset('shieldEffect', {
anchorX: 0.5,
anchorY: 0.5
});
shieldEffect.x = cannon.x;
shieldEffect.y = cannon.y;
shieldEffect.visible = false;
shieldEffect.alpha = 0.6;
shieldEffect.scaleX = 0.3;
shieldEffect.scaleY = 0.3;
game.addChild(shieldEffect);
var ships = [];
var cannonballs = [];
var shipSpawnTimer = 0;
var shipSpawnDelay = 120;
var difficultyTimer = 0;
var shipsEscaped = 0;
var maxEscapedShips = 10;
// Escaped ships counter UI
var escapedShipsTxt = new Text2('Escaped: 0/10', {
size: 50,
fill: 0xFFFFFF
});
escapedShipsTxt.anchor.set(0.5, 0);
escapedShipsTxt.y = 60;
escapedShipsTxt.visible = false;
LK.gui.top.addChild(escapedShipsTxt);
// Wave number display UI
var waveNumberTxt = new Text2('Wave: 1', {
size: 50,
fill: 0xFFFFFF
});
waveNumberTxt.anchor.set(0.5, 0);
waveNumberTxt.y = 120;
waveNumberTxt.visible = false;
LK.gui.top.addChild(waveNumberTxt);
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.visible = false;
LK.gui.top.addChild(scoreTxt);
var powerUps = [];
var tripleShot = false;
var tripleShotTimer = 0;
var rapidFire = false;
var rapidFireTimer = 0;
var powerUpDuration = 180; // 3 seconds at 60fps
// Power-up timer UI
var powerUpTimerTxt = new Text2('', {
size: 70,
fill: 0xFFFFFF
});
powerUpTimerTxt.anchor.set(0.5, 0.5);
powerUpTimerTxt.x = 0;
powerUpTimerTxt.y = 100;
LK.gui.center.addChild(powerUpTimerTxt);
var activePowerUpType = '';
var activePowerUpTimer = 0;
var isShooting = false;
var shootingTimer = 0;
// Wave system
var currentWave = 1;
var lastWaveScore = 0;
var waveMessage = null;
var waveMessageTimer = 0;
var waveCompleted = false;
var upgradeMenuVisible = false;
var gamePaused = false;
// Upgrade system
var cannonDamage = 1;
var cannonAttackSpeedMultiplier = 1;
// Wave completion tracking
var enemiesKilledThisWave = 0;
var enemiesRequiredPerWave = 15; // Base number of enemies to kill per wave
// Cannon health system
var enemyBullets = [];
var healthKits = [];
var shieldPowerUps = [];
var guidedShotPowerUps = [];
var guidedProjectiles = [];
var guidedShot = false;
var guidedShotTimer = 0;
var guidedShotDuration = 420; // 7 seconds at 60fps
// Cannon health UI - positioned next to cannon
var cannonHealthBarBg = LK.getAsset('cannonHealthBarBg', {
width: 400,
height: 20,
color: 0x444444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
cannonHealthBarBg.x = cannon.x;
cannonHealthBarBg.y = cannon.y - 150;
cannonHealthBarBg.visible = false;
game.addChild(cannonHealthBarBg);
var cannonHealthBar = LK.getAsset('cannonHealthBar', {
width: 396,
height: 16,
color: 0x00ff00,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
cannonHealthBar.x = cannon.x;
cannonHealthBar.y = cannon.y - 150;
cannonHealthBar.visible = false;
game.addChild(cannonHealthBar);
var healthText = new Text2('Health: 10/10', {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.x = cannon.x;
healthText.y = cannon.y - 100;
healthText.visible = false;
game.addChild(healthText);
// Wave message text
var waveMessageTxt = new Text2('', {
size: 100,
fill: 0xFFFF00
});
waveMessageTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(waveMessageTxt);
waveMessageTxt.visible = false;
// Upgrade menu UI
var upgradeMenu = new Container();
var upgradeTitle = new Text2('¡Oleada Completada! Elige una mejora:', {
size: 80,
fill: 0xFFFFFF
});
upgradeTitle.anchor.set(0.5, 0.5);
upgradeTitle.y = -400;
upgradeMenu.addChild(upgradeTitle);
// Create upgrade menu positioned in front of cannon
upgradeMenu.x = cannon.x;
upgradeMenu.y = cannon.y - 400;
upgradeMenu.visible = false;
game.addChild(upgradeMenu);
// Damage upgrade option
var damageUpgradeBtn = LK.getAsset('damageUpgrade', {
width: 300,
height: 150,
color: 0xFF4444,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
damageUpgradeBtn.x = -300;
damageUpgradeBtn.y = 0;
upgradeMenu.addChild(damageUpgradeBtn);
var damageUpgradeText = new Text2('Aumentar\nDaño', {
size: 50,
fill: 0xFFFFFF
});
damageUpgradeText.anchor.set(0.5, 0.5);
damageUpgradeText.x = -300;
damageUpgradeText.y = 0;
upgradeMenu.addChild(damageUpgradeText);
// Health upgrade option
var healthUpgradeBtn = LK.getAsset('healthUpgrade', {
width: 300,
height: 150,
color: 0x44FF44,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
healthUpgradeBtn.x = 0;
healthUpgradeBtn.y = 0;
upgradeMenu.addChild(healthUpgradeBtn);
var healthUpgradeText = new Text2('Vida +5', {
size: 50,
fill: 0xFFFFFF
});
healthUpgradeText.anchor.set(0.5, 0.5);
healthUpgradeText.x = 0;
healthUpgradeText.y = 0;
upgradeMenu.addChild(healthUpgradeText);
// Attack speed upgrade option
var speedUpgradeBtn = LK.getAsset('speedUpgrade', {
width: 300,
height: 150,
color: 0x4444FF,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
speedUpgradeBtn.x = 300;
speedUpgradeBtn.y = 0;
upgradeMenu.addChild(speedUpgradeBtn);
var speedUpgradeText = new Text2('Velocidad\nde Ataque', {
size: 50,
fill: 0xFFFFFF
});
speedUpgradeText.anchor.set(0.5, 0.5);
speedUpgradeText.x = 300;
speedUpgradeText.y = 0;
upgradeMenu.addChild(speedUpgradeText);
// Upgrade button handlers - defined after buttons are created
damageUpgradeBtn.down = function (x, y, obj) {
if (upgradeMenuVisible) {
cannonDamage += 1;
hideUpgradeMenu();
}
};
healthUpgradeBtn.down = function (x, y, obj) {
if (upgradeMenuVisible) {
cannon.maxHealth += 5;
cannon.health += 5;
// Update health UI
var healthPercent = cannon.health / cannon.maxHealth;
cannonHealthBar.scaleX = healthPercent;
healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth);
hideUpgradeMenu();
}
};
speedUpgradeBtn.down = function (x, y, obj) {
if (upgradeMenuVisible) {
cannonAttackSpeedMultiplier += 0.3;
hideUpgradeMenu();
}
};
// Add interactive property to upgrade buttons to ensure they can receive events
damageUpgradeBtn.interactive = true;
healthUpgradeBtn.interactive = true;
speedUpgradeBtn.interactive = true;
// Track if boss has been spawned this wave
var bossSpawnedThisWave = false;
function spawnShip() {
// Check if it's a boss wave (every 4 waves) and boss hasn't been spawned yet
if (currentWave % 4 === 0 && currentWave >= 4 && !bossSpawnedThisWave) {
// Check if there's already a boss in the ships array
var bossExists = false;
for (var i = 0; i < ships.length; i++) {
if (ships[i].shipType === 'boss') {
bossExists = true;
break;
}
}
// Only spawn boss if none exists
if (!bossExists) {
var boss = new Boss();
boss.init(currentWave);
boss.x = -300; // Start further left due to larger size
boss.y = 1366; // Center vertically
boss.zIndex = 10; // Highest priority for rendering
ships.push(boss);
game.addChild(boss);
game.children.sort(function (a, b) {
return (a.zIndex || 0) - (b.zIndex || 0);
});
bossSpawnedThisWave = true;
}
}
// Regular ship spawning for all waves (including boss waves)
var ship = new Ship();
var shipType;
// Wave-based enemy spawning
if (currentWave <= 2) {
// Waves 1-2: Only small ships
shipType = 'small';
} else if (currentWave <= 4) {
// Waves 3-4: Small and medium ships
var types = ['small', 'small', 'medium'];
shipType = types[Math.floor(Math.random() * types.length)];
} else if (currentWave <= 6) {
// Waves 5-6: Medium and some large ships
var types = ['small', 'medium', 'medium', 'large'];
shipType = types[Math.floor(Math.random() * types.length)];
} else {
// Wave 7+: All ship types with more large ships
var types = ['small', 'medium', 'large', 'large'];
shipType = types[Math.floor(Math.random() * types.length)];
}
ship.init(shipType);
ship.x = -150;
ship.y = 1000 + Math.random() * 1200;
// Apply wave-based speed multiplier
var speedMultiplier = 1 + (currentWave - 1) * 0.3;
// If it's a boss wave, apply slower speed to all enemies
if (currentWave % 4 === 0 && currentWave >= 4) {
speedMultiplier = speedMultiplier * 0.5; // Make all enemies slower on boss waves
}
ship.speed = ship.speed * speedMultiplier;
// Set zIndex based on ship type for proper layering
if (ship.shipType === 'large') {
ship.zIndex = 3;
} else if (ship.shipType === 'medium') {
ship.zIndex = 2;
} else {
ship.zIndex = 1;
}
game.children.sort(function (a, b) {
return (a.zIndex || 0) - (b.zIndex || 0);
});
ships.push(ship);
game.addChild(ship);
}
function updateDifficulty() {
difficultyTimer++;
if (difficultyTimer % 1800 === 0) {
shipSpawnDelay = Math.max(60, shipSpawnDelay - 10);
}
}
function checkWaveTransition() {
var currentScore = LK.getScore();
var newWave = Math.floor(currentScore / 1000) + 1;
if (newWave > currentWave) {
currentWave = newWave;
lastWaveScore = Math.floor(currentScore / 1000) * 1000;
// Reset boss spawning flag for new wave
bossSpawnedThisWave = false;
// Reset escaped ships counter for new wave
shipsEscaped = 0;
escapedShipsTxt.setText('Escaped: 0/10');
// Update wave number display
waveNumberTxt.setText('Wave: ' + currentWave);
// Increase max health by 2 each wave
cannon.maxHealth += 2;
cannon.health = cannon.maxHealth; // Restore to full health
// Show wave message
if (currentWave === 2) {
waveMessageTxt.setText('Segunda Oleada');
} else if (currentWave === 3) {
waveMessageTxt.setText('Tercera Oleada');
} else if (currentWave === 4) {
waveMessageTxt.setText('¡JEFE! - Oleada 4');
} else if (currentWave === 5) {
waveMessageTxt.setText('Quinta Oleada');
} else if (currentWave % 4 === 0) {
waveMessageTxt.setText('¡JEFE! - Oleada ' + currentWave);
} else {
waveMessageTxt.setText('Oleada ' + currentWave);
}
waveMessageTxt.visible = true;
waveMessageTimer = 180; // Show for 3 seconds
// Increase difficulty
shipSpawnDelay = Math.max(30, 120 - (currentWave - 1) * 15);
// Increase ship speeds
for (var i = 0; i < ships.length; i++) {
var ship = ships[i];
var speedMultiplier = 1 + (currentWave - 1) * 0.3;
ship.speed = ship.speed * speedMultiplier;
}
}
}
// Touch/click handlers
game.down = function (x, y, obj) {
if (!gameStarted) return;
// Handle upgrade menu clicks - allow shooting at buttons
if (upgradeMenuVisible) {
// Convert click coordinates to upgrade menu local coordinates
var menuLocalX = x - upgradeMenu.x;
var menuLocalY = y - upgradeMenu.y;
// Check damage upgrade button (positioned at -300, 0 in upgrade menu)
if (menuLocalX >= -450 && menuLocalX <= -150 && menuLocalY >= -75 && menuLocalY <= 75) {
cannonDamage += 1;
hideUpgradeMenu();
return;
}
// Check health upgrade button (positioned at 0, 0 in upgrade menu)
if (menuLocalX >= -150 && menuLocalX <= 150 && menuLocalY >= -75 && menuLocalY <= 75) {
cannon.maxHealth += 5;
cannon.health += 5;
// Update health UI
var healthPercent = cannon.health / cannon.maxHealth;
cannonHealthBar.scaleX = healthPercent;
healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth);
hideUpgradeMenu();
return;
}
// Check speed upgrade button (positioned at 300, 0 in upgrade menu)
if (menuLocalX >= 150 && menuLocalX <= 450 && menuLocalY >= -75 && menuLocalY <= 75) {
cannonAttackSpeedMultiplier += 0.3;
hideUpgradeMenu();
return;
}
// Allow aiming even when upgrade menu is visible
cannon.aimAt(x, y);
// Allow shooting at upgrade buttons - fire cannonball to hit buttons
isShooting = true;
shootingTimer = 0;
var newCannonballs = cannon.fire();
if (newCannonballs) {
for (var c = 0; c < newCannonballs.length; c++) {
cannonballs.push(newCannonballs[c]);
game.addChild(newCannonballs[c]);
}
}
return;
}
cannon.aimAt(x, y);
isShooting = true;
shootingTimer = 0;
// Fire immediately on first click
var newCannonballs = cannon.fire();
if (newCannonballs) {
for (var c = 0; c < newCannonballs.length; c++) {
cannonballs.push(newCannonballs[c]);
game.addChild(newCannonballs[c]);
}
}
};
game.up = function (x, y, obj) {
if (!gameStarted || upgradeMenuVisible) return;
isShooting = false;
};
// Play button event handler
playButtonImage.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
startMenu.visible = false;
cannon.visible = true;
cannonHealthBarBg.visible = true;
cannonHealthBar.visible = true;
healthText.visible = true;
scoreTxt.visible = true;
escapedShipsTxt.visible = true;
waveNumberTxt.visible = true;
// Stop button animations
tween.stop(oceanTitleImage);
tween.stop(defenderTitleImage);
tween.stop(playButtonImage);
// Stop menu music first, then switch to game music
LK.stopMusic();
LK.playMusic('calmSeaMelody');
}
};
game.update = function () {
if (!gameStarted) return;
if (gamePaused) return; // Don't update when upgrade menu is showing
// Handle continuous shooting only if upgrade menu is not visible
if (isShooting && !upgradeMenuVisible) {
shootingTimer++;
var currentFireDelay = rapidFire ? cannon.fireDelay / 3 : cannon.fireDelay;
var fireDelayInTicks = currentFireDelay / 16.67; // Convert ms to ticks
if (shootingTimer >= fireDelayInTicks) {
var newCannonballs = cannon.fire();
if (newCannonballs) {
for (var c = 0; c < newCannonballs.length; c++) {
cannonballs.push(newCannonballs[c]);
game.addChild(newCannonballs[c]);
}
}
shootingTimer = 0;
}
}
updateDifficulty();
checkWaveTransition();
// Update wave message timer
if (waveMessageTimer > 0) {
waveMessageTimer--;
if (waveMessageTimer <= 0) {
waveMessageTxt.visible = false;
}
}
shipSpawnTimer++;
if (shipSpawnTimer >= shipSpawnDelay) {
spawnShip();
shipSpawnTimer = 0;
}
for (var i = ships.length - 1; i >= 0; i--) {
var ship = ships[i];
if (ship.lastX === undefined) ship.lastX = ship.x;
if (ship.lastX <= 2048 && ship.x > 2048) {
shipsEscaped++;
escapedShipsTxt.setText('Escaped: ' + shipsEscaped + '/10');
LK.getSound('shipEscape').play();
ship.destroy();
ships.splice(i, 1);
// Check if too many ships escaped
if (shipsEscaped >= maxEscapedShips) {
// Show final score and wave reached
var finalScore = LK.getScore();
var finalWave = currentWave;
LK.showGameOver('Puntaje: ' + finalScore + '\nOleada alcanzada: ' + finalWave);
return;
}
continue;
}
// Ships shoot at cannon
if (ship.x > 200 && ship.x < 1800) {
// Only shoot when in range
var newBullet = ship.shootAtCannon(cannon.x, cannon.y);
if (newBullet) {
enemyBullets.push(newBullet);
game.addChild(newBullet);
}
}
ship.lastX = ship.x;
}
for (var j = cannonballs.length - 1; j >= 0; j--) {
var cannonball = cannonballs[j];
if (cannonball.lastX === undefined) cannonball.lastX = cannonball.x;
if (cannonball.lastY === undefined) cannonball.lastY = cannonball.y;
if (cannonball.lastX >= 0 && cannonball.x < 0 || cannonball.lastX <= 2048 && cannonball.x > 2048 || cannonball.lastY >= 0 && cannonball.y < 0 || cannonball.lastY <= 2732 && cannonball.y > 2732) {
cannonball.destroy();
cannonballs.splice(j, 1);
continue;
}
// Check collision with upgrade buttons when upgrade menu is visible
if (upgradeMenuVisible) {
// Check collision with damage upgrade button
var damageButtonX = upgradeMenu.x + damageUpgradeBtn.x;
var damageButtonY = upgradeMenu.y + damageUpgradeBtn.y;
if (cannonball.x >= damageButtonX - 150 && cannonball.x <= damageButtonX + 150 && cannonball.y >= damageButtonY - 75 && cannonball.y <= damageButtonY + 75) {
cannonDamage += 1;
LK.effects.flashScreen(0xff4444, 300); // Visual feedback for damage upgrade
hideUpgradeMenu();
cannonball.destroy();
cannonballs.splice(j, 1);
continue;
}
// Check collision with health upgrade button
var healthButtonX = upgradeMenu.x + healthUpgradeBtn.x;
var healthButtonY = upgradeMenu.y + healthUpgradeBtn.y;
if (cannonball.x >= healthButtonX - 150 && cannonball.x <= healthButtonX + 150 && cannonball.y >= healthButtonY - 75 && cannonball.y <= healthButtonY + 75) {
cannon.maxHealth += 5;
cannon.health += 5;
var healthPercent = cannon.health / cannon.maxHealth;
cannonHealthBar.scaleX = healthPercent;
healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth);
LK.effects.flashScreen(0x44ff44, 300); // Visual feedback for health upgrade
hideUpgradeMenu();
cannonball.destroy();
cannonballs.splice(j, 1);
continue;
}
// Check collision with speed upgrade button
var speedButtonX = upgradeMenu.x + speedUpgradeBtn.x;
var speedButtonY = upgradeMenu.y + speedUpgradeBtn.y;
if (cannonball.x >= speedButtonX - 150 && cannonball.x <= speedButtonX + 150 && cannonball.y >= speedButtonY - 75 && cannonball.y <= speedButtonY + 75) {
cannonAttackSpeedMultiplier += 0.3;
LK.effects.flashScreen(0x4444ff, 300); // Visual feedback for speed upgrade
hideUpgradeMenu();
cannonball.destroy();
cannonballs.splice(j, 1);
continue;
}
}
for (var k = ships.length - 1; k >= 0; k--) {
var ship = ships[k];
// Ensure ship is valid and has proper collision detection, and not dying
if (ship && ship.graphics && ship.graphics.visible && !ship.isDying && cannonball.intersects(ship)) {
ship.health -= cannonball.damage * cannonDamage;
ship.updateHealthBar();
LK.effects.flashObject(ship, 0xff0000, 200);
LK.getSound('hit').play();
cannonball.destroy();
cannonballs.splice(j, 1);
if (ship.health <= 0) {
enemiesKilledThisWave++; // Track kills for wave completion
ship.isDying = true; // Mark as dying to prevent further damage
LK.setScore(LK.getScore() + ship.points);
scoreTxt.setText('Score: ' + LK.getScore());
LK.getSound('shipDestroyed').play();
// Add small jump death animation
var originalY = ship.y;
var deadShip = ship; // Store reference to avoid closure issues
tween(ship, {
y: originalY - 30,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(deadShip, {
y: originalY + 10,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 150,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Random chance to spawn different power-ups (45% total chance)
var spawnChance = Math.random();
if (spawnChance < 0.15) {
// 15% chance for weapon power-ups
var powerUp = new PowerUp();
var powerTypes = ['tripleShot', 'rapidFire'];
var randomPowerType = powerTypes[Math.floor(Math.random() * powerTypes.length)];
powerUp.init(randomPowerType);
powerUp.x = deadShip.x;
powerUp.y = deadShip.y;
powerUps.push(powerUp);
game.addChild(powerUp);
// Add brilliant glowing animation to power-up
powerUp.graphics.tint = 0xffffff;
tween(powerUp.graphics, {
tint: 0xffff00,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(powerUp.graphics, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue pulsing glow
function continuePulse() {
if (powerUp.parent) {
tween(powerUp.graphics, {
tint: 0xffff00,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (powerUp.parent) {
tween(powerUp.graphics, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: continuePulse
});
}
}
});
}
}
continuePulse();
}
});
}
});
} else if (spawnChance < 0.25) {
// 10% chance for health kit
var healthKit = new HealthKit();
healthKit.x = deadShip.x;
healthKit.y = deadShip.y;
healthKits.push(healthKit);
game.addChild(healthKit);
// Add brilliant glowing animation to health kit
healthKit.tint = 0xffffff;
tween(healthKit, {
tint: 0x00ff00,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(healthKit, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue pulsing glow
function continuePulse() {
if (healthKit.parent) {
tween(healthKit, {
tint: 0x00ff00,
scaleX: 1.15,
scaleY: 1.15
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (healthKit.parent) {
tween(healthKit, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: continuePulse
});
}
}
});
}
}
continuePulse();
}
});
}
});
} else if (spawnChance < 0.35) {
// 15% chance for guided shot
var guidedShotPowerUp = new GuidedShotPowerUp();
guidedShotPowerUp.x = deadShip.x;
guidedShotPowerUp.y = deadShip.y;
guidedShotPowerUps.push(guidedShotPowerUp);
game.addChild(guidedShotPowerUp);
// Add brilliant glowing animation to guided shot power-up
guidedShotPowerUp.tint = 0xffffff;
tween(guidedShotPowerUp, {
tint: 0xff6600,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(guidedShotPowerUp, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue pulsing glow
function continuePulse() {
if (guidedShotPowerUp.parent) {
tween(guidedShotPowerUp, {
tint: 0xff6600,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (guidedShotPowerUp.parent) {
tween(guidedShotPowerUp, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: continuePulse
});
}
}
});
}
}
continuePulse();
}
});
}
});
} else if (spawnChance < 0.45) {
// 10% chance for shield
var shieldPowerUp = new ShieldPowerUp();
shieldPowerUp.x = deadShip.x;
shieldPowerUp.y = deadShip.y;
shieldPowerUps.push(shieldPowerUp);
game.addChild(shieldPowerUp);
// Add brilliant glowing animation to shield power-up
shieldPowerUp.tint = 0xffffff;
tween(shieldPowerUp, {
tint: 0x0099ff,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(shieldPowerUp, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue pulsing glow
function continuePulse() {
if (shieldPowerUp.parent) {
tween(shieldPowerUp, {
tint: 0x0099ff,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (shieldPowerUp.parent) {
tween(shieldPowerUp, {
tint: 0xffffff,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 700,
easing: tween.easeInOut,
onFinish: continuePulse
});
}
}
});
}
}
continuePulse();
}
});
}
});
}
if (deadShip.parent) {
deadShip.destroy();
}
// Remove from ships array by finding the ship
for (var removeIndex = ships.length - 1; removeIndex >= 0; removeIndex--) {
if (ships[removeIndex] === deadShip) {
ships.splice(removeIndex, 1);
break;
}
}
}
});
}
});
}
break;
}
}
if (cannonballs[j]) {
cannonball.lastX = cannonball.x;
cannonball.lastY = cannonball.y;
}
}
// Update power-ups
for (var p = powerUps.length - 1; p >= 0; p--) {
var powerUp = powerUps[p];
if (powerUp.lastX === undefined) powerUp.lastX = powerUp.x;
// Remove power-up if it goes off screen
if (powerUp.lastX <= 2048 && powerUp.x > 2048) {
powerUp.destroy();
powerUps.splice(p, 1);
continue;
}
// Check collision with cannonballs for collection
for (var cb = cannonballs.length - 1; cb >= 0; cb--) {
var cannonball = cannonballs[cb];
if (cannonball.intersects(powerUp)) {
// Collect power-up
LK.getSound('powerUpCollect').play();
LK.effects.flashScreen(0x00ff00, 300);
// Clear any existing power-up to prevent accumulation
tripleShot = false;
rapidFire = false;
activePowerUpTimer = 0; // Reset timer to prevent accumulation
// Activate new power-up
if (powerUp.powerType === 'tripleShot') {
tripleShot = true;
activePowerUpType = 'Triple Shot';
activePowerUpTimer = powerUpDuration;
} else if (powerUp.powerType === 'rapidFire') {
rapidFire = true;
activePowerUpType = 'Rapid Fire';
activePowerUpTimer = powerUpDuration;
}
powerUp.destroy();
powerUps.splice(p, 1);
break;
}
}
if (powerUps[p]) {
powerUp.lastX = powerUp.x;
}
}
// Update enemy bullets
for (var eb = enemyBullets.length - 1; eb >= 0; eb--) {
var enemyBullet = enemyBullets[eb];
if (enemyBullet.lastY === undefined) enemyBullet.lastY = enemyBullet.y;
// Remove if off screen
if (enemyBullet.lastY <= 2732 && enemyBullet.y > 2732) {
enemyBullet.destroy();
enemyBullets.splice(eb, 1);
continue;
}
// Check collision with cannon
if (enemyBullet.intersects(cannon)) {
cannon.takeDamage(enemyBullet.damage);
enemyBullet.destroy();
enemyBullets.splice(eb, 1);
// Update health UI
var healthPercent = cannon.health / cannon.maxHealth;
cannonHealthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
cannonHealthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
cannonHealthBar.tint = 0xFFFF00;
} else {
cannonHealthBar.tint = 0xFF0000;
}
healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth);
// Check game over
if (cannon.health <= 0) {
// Show final score and wave reached
var finalScore = LK.getScore();
var finalWave = currentWave;
LK.showGameOver('Puntaje: ' + finalScore + '\nOleada alcanzada: ' + finalWave);
return;
}
continue;
}
if (enemyBullets[eb]) {
enemyBullet.lastY = enemyBullet.y;
}
}
// Update health kits
for (var hk = healthKits.length - 1; hk >= 0; hk--) {
var healthKit = healthKits[hk];
if (healthKit.lastX === undefined) healthKit.lastX = healthKit.x;
// Remove if off screen
if (healthKit.lastX <= 2048 && healthKit.x > 2048) {
healthKit.destroy();
healthKits.splice(hk, 1);
continue;
}
// Check collection by cannonball
for (var cb = cannonballs.length - 1; cb >= 0; cb--) {
var cannonball = cannonballs[cb];
if (cannonball.intersects(healthKit)) {
cannon.heal(healthKit.healAmount);
LK.getSound('powerUpCollect').play();
LK.effects.flashScreen(0x00ff00, 200);
// Update health UI
var healthPercent = cannon.health / cannon.maxHealth;
cannonHealthBar.scaleX = healthPercent;
if (healthPercent > 0.6) {
cannonHealthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
cannonHealthBar.tint = 0xFFFF00;
} else {
cannonHealthBar.tint = 0xFF0000;
}
healthText.setText('Health: ' + cannon.health + '/' + cannon.maxHealth);
healthKit.destroy();
healthKits.splice(hk, 1);
break;
}
}
if (healthKits[hk]) {
healthKit.lastX = healthKit.x;
}
}
// Update shield power-ups
for (var sp = shieldPowerUps.length - 1; sp >= 0; sp--) {
var shieldPowerUp = shieldPowerUps[sp];
if (shieldPowerUp.lastX === undefined) shieldPowerUp.lastX = shieldPowerUp.x;
// Remove if off screen
if (shieldPowerUp.lastX <= 2048 && shieldPowerUp.x > 2048) {
shieldPowerUp.destroy();
shieldPowerUps.splice(sp, 1);
continue;
}
// Check collection by cannonball
for (var cb = cannonballs.length - 1; cb >= 0; cb--) {
var cannonball = cannonballs[cb];
if (cannonball.intersects(shieldPowerUp)) {
cannon.activateShield();
LK.getSound('powerUpCollect').play();
LK.effects.flashScreen(0x0099ff, 300);
// Add brilliant glowing animation to cannon when shield is collected
tween(cannon.graphics, {
tint: 0x88ccff,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cannon.graphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
shieldPowerUp.destroy();
shieldPowerUps.splice(sp, 1);
break;
}
}
if (shieldPowerUps[sp]) {
shieldPowerUp.lastX = shieldPowerUp.x;
}
}
// Update guided shot power-ups
for (var gsp = guidedShotPowerUps.length - 1; gsp >= 0; gsp--) {
var guidedShotPowerUp = guidedShotPowerUps[gsp];
if (guidedShotPowerUp.lastX === undefined) guidedShotPowerUp.lastX = guidedShotPowerUp.x;
// Remove if off screen
if (guidedShotPowerUp.lastX <= 2048 && guidedShotPowerUp.x > 2048) {
guidedShotPowerUp.destroy();
guidedShotPowerUps.splice(gsp, 1);
continue;
}
// Check collection by cannonball
for (var cb = cannonballs.length - 1; cb >= 0; cb--) {
var cannonball = cannonballs[cb];
if (cannonball.intersects(guidedShotPowerUp)) {
// Clear existing power-ups
tripleShot = false;
rapidFire = false;
guidedShot = true;
activePowerUpType = 'Guided Shot';
activePowerUpTimer = guidedShotDuration;
LK.getSound('powerUpCollect').play();
LK.effects.flashScreen(0xff6600, 300);
guidedShotPowerUp.destroy();
guidedShotPowerUps.splice(gsp, 1);
break;
}
}
if (guidedShotPowerUps[gsp]) {
guidedShotPowerUp.lastX = guidedShotPowerUp.x;
}
}
// Update guided projectiles
for (var gp = guidedProjectiles.length - 1; gp >= 0; gp--) {
var guidedProjectile = guidedProjectiles[gp];
// Check if projectile has exceeded its lifetime (4 seconds)
if (LK.ticks - guidedProjectile.creationTime >= guidedProjectile.lifetime) {
guidedProjectile.destroy();
guidedProjectiles.splice(gp, 1);
continue;
}
// Check collision with enemy ships
var hitShip = false;
for (var k = ships.length - 1; k >= 0; k--) {
var ship = ships[k];
// Ensure ship is valid and has proper collision detection, and not dying
if (ship && ship.graphics && ship.graphics.visible && !ship.isDying && guidedProjectile.intersects(ship)) {
ship.health -= guidedProjectile.damage - 2 + cannonDamage + 2; // Base guided damage plus cannon damage bonus
ship.updateHealthBar();
LK.effects.flashObject(ship, 0xff0000, 200);
LK.getSound('hit').play();
guidedProjectile.destroy();
guidedProjectiles.splice(gp, 1);
hitShip = true;
if (ship.health <= 0) {
enemiesKilledThisWave++; // Track kills for wave completion
ship.isDying = true; // Mark as dying to prevent further damage
LK.setScore(LK.getScore() + ship.points);
scoreTxt.setText('Score: ' + LK.getScore());
LK.getSound('shipDestroyed').play();
// Add small jump death animation
var originalY = ship.y;
var deadShip = ship; // Store reference to avoid closure issues
tween(ship, {
y: originalY - 30,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(deadShip, {
y: originalY + 10,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 150,
easing: tween.bounceOut,
onFinish: function onFinish() {
if (deadShip.parent) {
deadShip.destroy();
}
// Remove from ships array by finding the ship
for (var removeIndex = ships.length - 1; removeIndex >= 0; removeIndex--) {
if (ships[removeIndex] === deadShip) {
ships.splice(removeIndex, 1);
break;
}
}
}
});
}
});
}
break;
}
}
if (hitShip) continue;
// Remove if too far off screen
if (guidedProjectile.x < -200 || guidedProjectile.x > 2248 || guidedProjectile.y < -200 || guidedProjectile.y > 2932) {
guidedProjectile.destroy();
guidedProjectiles.splice(gp, 1);
}
}
// Update cannon shield
if (cannon.isShielded && cannon.shieldTimer > 0) {
cannon.shieldTimer--;
// Show shield effect when active
if (!shieldEffect.visible) {
shieldEffect.visible = true;
shieldEffect.x = cannon.x;
shieldEffect.y = cannon.y;
}
if (cannon.shieldTimer <= 0) {
cannon.isShielded = false;
shieldEffect.visible = false;
tween(cannon.graphics, {
tint: 0xffffff
}, {
duration: 500
});
}
} else {
shieldEffect.visible = false;
}
// Update power-up timer
if (activePowerUpTimer > 0) {
activePowerUpTimer--;
// Update timer display
var timeLeft = Math.ceil(activePowerUpTimer / 60);
var timerText = activePowerUpType + ': ' + timeLeft + 's';
if (cannon.isShielded) {
var shieldTimeLeft = Math.ceil(cannon.shieldTimer / 60);
timerText += ' | Shield: ' + shieldTimeLeft + 's';
}
powerUpTimerTxt.setText(timerText);
// Check if power-up expired
if (activePowerUpTimer <= 0) {
tripleShot = false;
rapidFire = false;
guidedShot = false;
activePowerUpType = '';
if (cannon.isShielded) {
var shieldTimeLeft = Math.ceil(cannon.shieldTimer / 60);
powerUpTimerTxt.setText('Shield: ' + shieldTimeLeft + 's');
} else {
powerUpTimerTxt.setText('');
}
}
} else {
// No active weapon power-up, but maybe shield
if (cannon.isShielded) {
var shieldTimeLeft = Math.ceil(cannon.shieldTimer / 60);
powerUpTimerTxt.setText('Shield: ' + shieldTimeLeft + 's');
} else {
powerUpTimerTxt.setText('');
}
}
// Check for wave completion at end of update loop
checkWaveCompletion();
};
The small warship is fast and agile, designed for quick attacks and evading enemy fire. It has light armor and carries a few small cannons. Its compact size makes it harder to hit but less durable.. In-Game asset. 2d. High contrast. No shadows
The medium warship balances speed and firepower. It is equipped with multiple cannons and moderate armor. It moves steadily and can withstand more hits than smaller ships, making it a tougher target.. In-Game asset. 2d. High contrast. No shadows
boton de play pero que sea con tematica oceanica y que tenga una cocha al lado. In-Game asset. 2d. High contrast. No shadows
un barco gigante de guerra. In-Game asset. 2d. High contrast. No shadows
escudo celestial. In-Game asset. 2d. High contrast. No shadows
mas velocidad pero escrito en ingles. In-Game asset. 2d. High contrast. No shadows
mas vida pero escrito en ingles. In-Game asset. 2d. High contrast. No shadows
mas daño pero escrito en ingles. In-Game asset. 2d. High contrast. No shadows
un mar con un fondo a lo lejos. In-Game asset. 2d. High contrast. No shadows
unas letras que digan : creado por ZURI HARDAWAY. In-Game asset. 2d. High contrast. No shadows
que un pulpo este sentado en un cañon. In-Game asset. 2d. High contrast. No shadows
crea un titulo que diga oceanic defence con un pulplo detras de las letras que esta sentado en un cañon negro. In-Game asset. 2d. High contrast. No shadows
crea una imagen de un barco estallando. In-Game asset. 2d. High contrast. No shadows
un pulpo naranja viendo que barcos de guerra vienen a atacar su hogar marino. In-Game asset. 2d. High contrast. No shadows
una sola viñeta en blanco de comic de manera rectangular horizontal en el centro de la pantalla, que el back ground este adornado con un fondo marino y brazos de pulpos. In-Game asset. 2d. High contrast. No shadows, que los tentaculos no toquen la viñeta q
un pulpo naranja abriendo un cofre del tesoro donde encuentra en el fondo marino dentro de ese cofre un cañon nergro donde. In-Game asset. 2d. High contrast. No shadows
un pulpo naranja frenetico disparandole a buques de guerras encima de un cañon negro. que se vea el fondo del cielo y el mar pero que no este enojado sino calmado
bala de cañon. In-Game asset. 2d. High contrast. No shadows
bala de cañon. In-Game asset. 2d. High contrast. No shadows
ojo de pulpo. In-Game asset. 2d. High contrast. No shadows
botiquin con un pulpo rodeandolo. In-Game asset. 2d. High contrast. No shadows
una esfera con una letra r dentro y un pulpo rodeando la esfera. In-Game asset. 2d. High contrast. No shadows
una letra t dentro de una esfera y un pulpo que la rodea. In-Game asset. 2d. High contrast. No shadows
una p dentro de una esfera con un brazo de un pulpo rojo rodeandola. In-Game asset. 2d. High contrast. No shadows
guerra de pulpos con cañones, que sean de diferentes colores los pulpos de diferentes, tipo dibujo contra buques de guerra