/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); var bossGraphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 10000; self.health = 10000; self.attackCooldown = 0; self.attackInterval = 180; // 3 seconds at 60fps self.moveDirection = 1; // 1 for right, -1 for left self.moveSpeed = 2; self.leftBound = 200; self.rightBound = 1848; self.verticalDirection = 1; // 1 for down, -1 for up self.verticalSpeed = 1; self.topBound = 300; self.bottomBound = 500; self.specialAttackTimer = 0; self.specialAttackInterval = 300; // 5 seconds at 60fps self.isCharging = false; self.chargeTime = 0; self.maxChargeTime = 60; // 1 second at 60fps self.indicator = null; self.finalAttackTriggered = false; self.isFinalAttacking = false; self.finalAttackTime = 0; self.maxFinalAttackTime = 180; // 3 seconds at 60fps self.finalAttackCanceled = false; self.laserAttackTimer = 0; self.laserAttackInterval = 900; // 15 seconds at 60fps self.laserCooldown = 0; // Cooldown timer for laser attacks self.laserCooldownTime = 300; // 5 seconds cooldown at 60fps self.isChargingLaser = false; // New state for laser charging self.laserChargeTime = 0; // Timer for laser charging self.maxLaserChargeTime = 180; // 3 seconds at 60fps self.laserUsed = false; // Track if laser has been used (only once allowed) self.takeDamage = function (damage) { self.health -= damage; if (self.health < 0) self.health = 0; // Flash red when taking damage tween(bossGraphics, { tint: 0xff8888 }, { duration: 100, onFinish: function onFinish() { tween(bossGraphics, { tint: 0xffffff }, { duration: 100 }); } }); }; self.attack = function () { return { x: self.x, y: self.y + 100, damage: 1 }; }; self.specialAttack = function () { return { x: self.x, y: self.y + 100, damage: 1 }; }; self.startFinalAttack = function () { self.isFinalAttacking = true; self.finalAttackTime = 0; self.finalAttackCanceled = false; // Boss stops moving during final attack }; self.cancelFinalAttack = function () { self.finalAttackCanceled = true; self.isFinalAttacking = false; self.finalAttackTime = 0; }; self.finalAttack = function () { return { x: 1024, // Center of screen horizontally y: 1366, // Center vertically to cover entire map damage: 1 }; }; self.laserAttack = function () { return { x: self.x, y: 1366, // Center vertically to cover full screen damage: 1 }; }; self.update = function () { // Check for final attack trigger (when health drops to 1000 or below) if (self.health <= 1000 && !self.finalAttackTriggered && !self.isFinalAttacking) { self.finalAttackTriggered = true; self.startFinalAttack(); return "finalAttack"; } // Handle final attack if (self.isFinalAttacking && !self.finalAttackCanceled) { self.finalAttackTime++; if (self.finalAttackTime >= self.maxFinalAttackTime) { // Final attack completes - deal damage to entire screen self.isFinalAttacking = false; return "finalAttackComplete"; } return false; // Boss doesn't move during final attack } // Handle special attack timing if (!self.isFinalAttacking) { self.specialAttackTimer++; // Fast special attack mode when boss health drops to half or below var currentSpecialInterval = self.health <= self.maxHealth / 2 ? 100 : self.specialAttackInterval; // Much faster when health <= 5000 // Check if it's time for special attack if (self.specialAttackTimer >= currentSpecialInterval && !self.isCharging) { self.isCharging = true; self.chargeTime = 0; // Create indicator if (!self.indicator) { self.indicator = self.addChild(LK.getAsset('bossSpecialIndicator', { anchorX: 0.5, anchorY: 0.5 })); self.indicator.y = 80; // Position above boss } } // Handle laser attack timing - only in fast mode if (self.health <= self.maxHealth / 2) { self.laserAttackTimer++; // Update laser cooldown if (self.laserCooldown > 0) { self.laserCooldown--; } // Only allow laser attack once when entering fast mode if (self.laserAttackTimer >= 300 && self.laserCooldown <= 0 && !self.isChargingLaser && !self.laserUsed) { self.isChargingLaser = true; self.laserChargeTime = 0; self.laserAttackTimer = 0; } } } // Handle laser charging phase (boss stops moving) if (self.isChargingLaser) { self.laserChargeTime++; // Make boss flash to indicate laser charging tween.stop(bossGraphics, { tint: true }); if (self.laserChargeTime % 30 === 0) { // Flash every 0.5 seconds tween(bossGraphics, { tint: 0xffff00 }, { duration: 150 }); } if (self.laserChargeTime >= self.maxLaserChargeTime) { // End laser charging, launch laser attack self.isChargingLaser = false; self.laserChargeTime = 0; self.laserCooldown = self.laserCooldownTime; // Start cooldown self.laserUsed = true; // Mark laser as used tween(bossGraphics, { tint: 0xffffff }, { duration: 100 }); // Reset boss color return "laser"; } } else if (self.isCharging) { self.chargeTime++; // Make indicator blink if (self.indicator) { self.indicator.alpha = Math.sin(self.chargeTime * 0.3) * 0.5 + 0.5; } if (self.chargeTime >= self.maxChargeTime) { // End charging, launch special attack self.isCharging = false; self.specialAttackTimer = 0; // Remove indicator if (self.indicator) { self.indicator.destroy(); self.indicator = null; } return "special"; // Signal to create special attack } } else if (!self.isFinalAttacking) { // Normal movement only when not charging and not doing final attack // Handle horizontal movement self.x += self.moveSpeed * self.moveDirection; // Change direction when hitting bounds if (self.x <= self.leftBound) { self.moveDirection = 1; // Move right self.x = self.leftBound; } else if (self.x >= self.rightBound) { self.moveDirection = -1; // Move left self.x = self.rightBound; } // Handle vertical movement self.y += self.verticalSpeed * self.verticalDirection; // Change direction when hitting vertical bounds if (self.y <= self.topBound) { self.verticalDirection = 1; // Move down self.y = self.topBound; } else if (self.y >= self.bottomBound) { self.verticalDirection = -1; // Move up self.y = self.bottomBound; } } // Normal attacks (only when not charging, not charging laser and not doing final attack) if (!self.isCharging && !self.isChargingLaser && !self.isFinalAttacking) { self.attackCooldown++; // Fast attack mode when boss health drops to half or below var currentAttackInterval = self.health <= self.maxHealth / 2 ? 10 : self.attackInterval; // Super fast when health <= 5000 if (self.attackCooldown >= currentAttackInterval) { self.attackCooldown = 0; return true; // Signal to create attack } } return false; }; return self; }); var BossAttack = Container.expand(function () { var self = Container.call(this); var attackGraphics = self.attachAsset('bossAttack', { anchorX: 0.5, anchorY: 0.5 }); // Speed increases in fast mode when boss health is low self.speed = boss && boss.health <= boss.maxHealth / 2 ? 8 : 4; self.damage = 1; self.update = function () { self.y += self.speed; }; return self; }); var BossFinalAttack = Container.expand(function () { var self = Container.call(this); var attackGraphics = self.attachAsset('bossFinalAttack', { anchorX: 0.5, anchorY: 0.5 }); attackGraphics.alpha = 0.4; // Semi-transparent red overlay covering entire screen self.damage = 1; self.lifeTime = 0; self.maxLifeTime = 60; // 1 second visible self.update = function () { self.lifeTime++; // Pulsing effect for entire screen coverage attackGraphics.alpha = 0.4 + Math.sin(self.lifeTime * 0.3) * 0.3; }; return self; }); var BossLaser = Container.expand(function () { var self = Container.call(this); var laserGraphics = self.attachAsset('bossLaser', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 1; self.lifeTime = 0; self.maxLifeTime = 600; // 10 seconds visible self.update = function () { self.lifeTime++; // Follow boss position horizontally self.x = boss.x; // Fade out effect in the last 3 seconds (180 frames) if (self.lifeTime >= self.maxLifeTime - 180) { var fadeProgress = (self.lifeTime - (self.maxLifeTime - 180)) / 180; laserGraphics.alpha = 0.7 * (1 - fadeProgress); } else { // Pulsing effect laserGraphics.alpha = 0.7 + Math.sin(self.lifeTime * 0.4) * 0.3; } }; return self; }); var BossSpecialAttack = Container.expand(function () { var self = Container.call(this); var attackGraphics = self.attachAsset('bossSpecialAttack', { anchorX: 0.5, anchorY: 0.5 }); // Speed increases in fast mode when boss health is low self.speed = boss && boss.health <= boss.maxHealth / 2 ? 12 : 6; self.damage = 1; self.update = function () { self.y += self.speed; }; return self; }); // Game variables var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 1; self.health = 1; self.attackDamage = 50; self.takeDamage = function (damage) { self.health -= damage; if (self.health < 0) self.health = 0; // Flash red when taking damage tween(playerGraphics, { tint: 0xff8888 }, { duration: 100, onFinish: function onFinish() { tween(playerGraphics, { tint: 0xffffff }, { duration: 100 }); } }); }; self.attack = function () { return { x: self.x, y: self.y - 50, damage: self.attackDamage }; }; return self; }); var PlayerAttack = Container.expand(function () { var self = Container.call(this); var attackGraphics = self.attachAsset('playerAttack', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -24; self.damage = 50; self.update = function () { self.y += self.speed; }; return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); self.speed = Math.random() * 2 + 1; // Random speed between 1-3 self.twinkleTime = 0; self.update = function () { self.y += self.speed; // Enhanced twinkling effect with random timing self.twinkleTime++; var twinkleIntensity = 0.4 + Math.sin(self.twinkleTime * (0.05 + Math.random() * 0.1)) * 0.3; starGraphics.alpha = Math.max(0.2, twinkleIntensity); // Add occasional bright flash if (Math.random() < 0.002) { tween(starGraphics, { alpha: 1.0, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, onFinish: function onFinish() { tween(starGraphics, { alpha: twinkleIntensity, scaleX: 1.0, scaleY: 1.0 }, { duration: 300 }); } }); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game variables // Game state var gameStarted = false; var startScreen = null; var startButton = null; var titleText = null; var instructionText = null; var explanationScreen = null; var showingExplanation = false; // Boss assets // Player assets // Attack button // Attack effects // Sounds var boss; var player; var playerAttacks = []; var bossAttacks = []; var bossSpecialAttacks = []; var bossFinalAttacks = []; var bossLasers = []; var stars = []; var startScreenStars = []; var dragNode = null; var playerLastX = 0; var playerLastY = 0; // Health bars var bossHealthBarBg, bossHealthBar; var playerHealthBarBg, playerHealthBar; // Health text var bossHealthText, playerHealthText; // Create start screen startScreen = game.addChild(new Container()); // Add immediate star burst effect for (var burstIndex = 0; burstIndex < 20; burstIndex++) { var burstStar = new Star(); burstStar.x = 1024 + (Math.random() - 0.5) * 400; burstStar.y = 1366 + (Math.random() - 0.5) * 400; burstStar.speed = Math.random() * 0.5 + 0.2; var burstGraphics = burstStar.children[0]; burstGraphics.alpha = 0; burstGraphics.scaleX = 2; burstGraphics.scaleY = 2; // Animate burst effect tween(burstGraphics, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 800 + Math.random() * 600, easing: tween.easeOut, onFinish: function onFinish() { // Add extra twinkle after burst tween(burstGraphics, { alpha: 0.3, scaleX: 1.5, scaleY: 1.5 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(burstGraphics, { alpha: 0.7, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeInOut }); } }); } }); startScreenStars.push(burstStar); startScreen.addChild(burstStar); } // Create initial stars for start screen background for (var starIndex = 0; starIndex < 50; starIndex++) { var startStar = new Star(); startStar.x = Math.random() * 2048; startStar.y = Math.random() * 2732; startStar.speed = Math.random() * 1 + 0.5; // Slower speed for start screen // Add immediate twinkling effect with random timing startStar.twinkleTime = Math.random() * 100; // Add enhanced initial visibility with tween effect var starGraphics = startStar.children[0]; starGraphics.alpha = 0; tween(starGraphics, { alpha: 0.6 + Math.random() * 0.4, scaleX: 0.8 + Math.random() * 0.4, scaleY: 0.8 + Math.random() * 0.4 }, { duration: 500 + Math.random() * 1000, easing: tween.easeOut }); startScreenStars.push(startStar); startScreen.addChild(startStar); } // Create title text titleText = new Text2('Space Ranger', { size: 120, fill: 0xFFFF00 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; startScreen.addChild(titleText); // Create instruction text instructionText = new Text2('Arrastra para moverte\nDerrota al jefe para ganar\n¡Ten cuidado con sus ataques!', { size: 60, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 1200; startScreen.addChild(instructionText); // Create start button startButton = new Text2('JUGAR', { size: 100, fill: 0x00FF00 }); startButton.anchor.set(0.5, 0.5); startButton.x = 900; startButton.y = 1600; startScreen.addChild(startButton); // Create explanation button var explanationButton = new Text2('?', { size: 100, fill: 0x4A90E2 }); explanationButton.anchor.set(0.5, 0.5); explanationButton.x = 1148; explanationButton.y = 1600; startScreen.addChild(explanationButton); // Add enhanced pulsing effect to start button with color changes and rotation var _buttonPulse = function buttonPulse() { if (startButton && !gameStarted) { // First phase: scale up with color change to bright yellow and slight rotation tween(startButton, { scaleX: 1.4, scaleY: 1.4, tint: 0xFFFF00, rotation: 0.1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Second phase: scale down with color change to bright green and rotation back tween(startButton, { scaleX: 1.0, scaleY: 1.0, tint: 0x00FF88, rotation: -0.05 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { // Third phase: final color pulse back to original green tween(startButton, { tint: 0x00FF00, rotation: 0 }, { duration: 300, easing: tween.easeInOut, onFinish: _buttonPulse }); } }); } }); } }; _buttonPulse(); // Add pulsing effect to explanation button var _explanationPulse = function explanationPulse() { if (explanationButton && !gameStarted && !showingExplanation) { tween(explanationButton, { scaleX: 1.3, scaleY: 1.3, tint: 0x87CEEB }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { tween(explanationButton, { scaleX: 1.0, scaleY: 1.0, tint: 0x4A90E2 }, { duration: 600, easing: tween.easeIn, onFinish: _explanationPulse }); } }); } }; _explanationPulse(); // Start button click handler startButton.down = function (x, y, obj) { if (!gameStarted && !showingExplanation) { gameStarted = true; // Clean up start screen stars for (var cleanupIndex = startScreenStars.length - 1; cleanupIndex >= 0; cleanupIndex--) { startScreenStars[cleanupIndex].destroy(); } startScreenStars = []; startScreen.destroy(); initializeGame(); } }; // Explanation button click handler explanationButton.down = function (x, y, obj) { if (!gameStarted && !showingExplanation) { showingExplanation = true; createExplanationScreen(); } }; // Function to create explanation screen function createExplanationScreen() { explanationScreen = game.addChild(new Container()); explanationScreen.alpha = 0; tween(explanationScreen, { alpha: 1 }, { duration: 300 }); // Semi-transparent background var overlay = explanationScreen.addChild(LK.getAsset('bossFinalAttack', { anchorX: 0.5, anchorY: 0.5 })); overlay.x = 1024; overlay.y = 1366; overlay.alpha = 0.8; overlay.tint = 0x000033; // Title var explanationTitle = new Text2('GUÍA DEL JUEGO', { size: 80, fill: 0xFFFF00 }); explanationTitle.anchor.set(0.5, 0.5); explanationTitle.x = 1024; explanationTitle.y = 400; explanationScreen.addChild(explanationTitle); // Game mechanics explanation var mechanicsText = new Text2('CONTROLES:\n• Arrastra para mover tu nave\n• Disparas automáticamente\n\nEL JEFE:\n• Tiene 10,000 puntos de vida\n• Se mueve por la pantalla\n• Tiene múltiples tipos de ataque', { size: 45, fill: 0xFFFFFF }); mechanicsText.anchor.set(0.5, 0.5); mechanicsText.x = 1024; mechanicsText.y = 800; explanationScreen.addChild(mechanicsText); // Boss attacks explanation var attacksText = new Text2('ATAQUES DEL JEFE:\n\n• ATAQUE NORMAL: Proyectiles rojos (1 daño)\n• ATAQUE ESPECIAL: Proyectiles pequeños rápidos (1 daño)\n• ATAQUE LÁSER: Rayo vertical que te sigue (1 daño)\n• ATAQUE FINAL: Pantalla roja completa (1 daño)', { size: 40, fill: 0xFF8888 }); attacksText.anchor.set(0.5, 0.5); attacksText.x = 1024; attacksText.y = 1200; explanationScreen.addChild(attacksText); // Special mechanics var specialText = new Text2('MECÁNICAS ESPECIALES:\n\n• Cuando el jefe tiene ≤5000 vida: MODO RÁPIDO\n - Ataques más frecuentes y rápidos\n - Puede usar el ataque láser\n\n• Cuando el jefe tiene ≤1000 vida:\n - Intenta el ATAQUE FINAL mortal\n - ¡Dispárale para cancelarlo!', { size: 38, fill: 0x88FF88 }); specialText.anchor.set(0.5, 0.5); specialText.x = 1024; specialText.y = 1650; explanationScreen.addChild(specialText); // Victory condition var victoryText = new Text2('OBJETIVO: Reduce la vida del jefe a 0 para ganar\nSOBREVIVE: Tienes solo 1 punto de vida', { size: 42, fill: 0xFFD700 }); victoryText.anchor.set(0.5, 0.5); victoryText.x = 1024; victoryText.y = 2000; explanationScreen.addChild(victoryText); // Back button var backButton = new Text2('VOLVER', { size: 70, fill: 0xFF6666 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 2300; explanationScreen.addChild(backButton); // Back button animation var _backPulse = function backPulse() { if (backButton && showingExplanation) { tween(backButton, { scaleX: 1.2, scaleY: 1.2, tint: 0xFF8888 }, { duration: 600, onFinish: function onFinish() { tween(backButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFF6666 }, { duration: 400, onFinish: _backPulse }); } }); } }; _backPulse(); // Back button click handler backButton.down = function (x, y, obj) { if (showingExplanation) { tween(explanationScreen, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { explanationScreen.destroy(); explanationScreen = null; showingExplanation = false; } }); } }; } // Function to initialize game elements function initializeGame() { // Initialize boss boss = game.addChild(new Boss()); boss.x = 1024; boss.y = 400; // Initialize player player = game.addChild(new Player()); player.x = 1024; player.y = 2200; playerLastX = player.x; playerLastY = player.y; // Create boss health bar bossHealthBarBg = game.addChild(LK.getAsset('bossHealthBarBg', { anchorX: 0.5, anchorY: 0.5 })); bossHealthBarBg.x = 1024; bossHealthBarBg.y = 200; bossHealthBar = game.addChild(LK.getAsset('bossHealthBar', { anchorX: 0.5, anchorY: 0.5 })); bossHealthBar.x = 1024; bossHealthBar.y = 200; // Create player health bar playerHealthBarBg = game.addChild(LK.getAsset('playerHealthBarBg', { anchorX: 0.5, anchorY: 0.5 })); playerHealthBarBg.x = 1024; playerHealthBarBg.y = 2500; playerHealthBar = game.addChild(LK.getAsset('playerHealthBar', { anchorX: 0.5, anchorY: 0.5 })); playerHealthBar.x = 1024; playerHealthBar.y = 2500; // Create health text bossHealthText = new Text2('10000/10000', { size: 40, fill: 0xFFFFFF }); bossHealthText.anchor.set(0.5, 0.5); bossHealthText.x = 1024; bossHealthText.y = 160; game.addChild(bossHealthText); playerHealthText = new Text2('1/1', { size: 30, fill: 0xFFFFFF }); playerHealthText.anchor.set(0.5, 0.5); playerHealthText.x = 1024; playerHealthText.y = 2540; game.addChild(playerHealthText); } // Player movement game.move = function (x, y, obj) { if (gameStarted && dragNode) { dragNode.x = x; dragNode.y = y; // Keep player within bounds if (dragNode.x < 50) dragNode.x = 50; if (dragNode.x > 1998) dragNode.x = 1998; if (dragNode.y < 800) dragNode.y = 800; if (dragNode.y > 2600) dragNode.y = 2600; } }; game.down = function (x, y, obj) { if (gameStarted) { dragNode = player; game.move(x, y, obj); } }; game.up = function (x, y, obj) { if (gameStarted) { dragNode = null; } }; // Main game update loop game.update = function () { // Only run game logic if game has started if (!gameStarted) { // Update start screen stars for (var startStarIndex = startScreenStars.length - 1; startStarIndex >= 0; startStarIndex--) { var startStar = startScreenStars[startStarIndex]; // Remove if off screen if (startStar.y > 2780) { startStar.destroy(); startScreenStars.splice(startStarIndex, 1); } } // Spawn new stars for start screen more frequently and consistently if (LK.ticks % 15 == 0 && Math.random() < 0.9) { var newStartStar = new Star(); newStartStar.x = Math.random() * 2048; newStartStar.y = -10; newStartStar.speed = Math.random() * 1.5 + 0.8; // Slightly faster and more visible // Add immediate fade-in effect for new stars var newStarGraphics = newStartStar.children[0]; newStarGraphics.alpha = 0; tween(newStarGraphics, { alpha: 0.5 + Math.random() * 0.5 }, { duration: 300 + Math.random() * 500, easing: tween.easeOut }); startScreenStars.push(newStartStar); startScreen.addChild(newStartStar); } return; } // Update player attacks for (var i = playerAttacks.length - 1; i >= 0; i--) { var attack = playerAttacks[i]; // Check if attack hit boss if (attack.intersects(boss)) { boss.takeDamage(attack.damage); // Cancel final attack if boss is doing it if (boss.isFinalAttacking && !boss.finalAttackCanceled) { boss.cancelFinalAttack(); } attack.destroy(); playerAttacks.splice(i, 1); continue; } // Remove if off screen if (attack.y < -50) { attack.destroy(); playerAttacks.splice(i, 1); } } // Update boss attacks for (var j = bossAttacks.length - 1; j >= 0; j--) { var bossAttack = bossAttacks[j]; // Check if attack hit player if (bossAttack.intersects(player)) { player.takeDamage(bossAttack.damage); bossAttack.destroy(); bossAttacks.splice(j, 1); continue; } // Remove if off screen if (bossAttack.y > 2780) { bossAttack.destroy(); bossAttacks.splice(j, 1); } } // Update boss special attacks for (var k = bossSpecialAttacks.length - 1; k >= 0; k--) { var specialAttack = bossSpecialAttacks[k]; // Check if special attack hit player if (specialAttack.intersects(player)) { player.takeDamage(specialAttack.damage); specialAttack.destroy(); bossSpecialAttacks.splice(k, 1); continue; } // Remove if off screen if (specialAttack.y > 2780) { specialAttack.destroy(); bossSpecialAttacks.splice(k, 1); } } // Player automatic attack - fast mode when health is half or below var attackInterval = player.health <= player.maxHealth / 2 ? 3 : 20; // Super fast when health <= 0.5 if (LK.ticks % attackInterval == 0) { var attackData = player.attack(); var newPlayerAttack = new PlayerAttack(); newPlayerAttack.x = attackData.x; newPlayerAttack.y = attackData.y; playerAttacks.push(newPlayerAttack); game.addChild(newPlayerAttack); } // Update boss final attacks for (var l = bossFinalAttacks.length - 1; l >= 0; l--) { var finalAttack = bossFinalAttacks[l]; // Check if final attack hit player (covers entire screen) if (finalAttack.intersects(player)) { player.takeDamage(finalAttack.damage); } // Remove after lifetime expires if (finalAttack.lifeTime >= finalAttack.maxLifeTime) { finalAttack.destroy(); bossFinalAttacks.splice(l, 1); } } // Update boss laser attacks for (var m = bossLasers.length - 1; m >= 0; m--) { var laser = bossLasers[m]; // Check if laser hit player AND player is moving var playerIsMoving = player.x !== playerLastX || player.y !== playerLastY; if (laser.intersects(player) && playerIsMoving) { player.takeDamage(laser.damage); } // Remove after lifetime expires if (laser.lifeTime >= laser.maxLifeTime) { laser.destroy(); bossLasers.splice(m, 1); } } // Update and spawn stars for (var s = stars.length - 1; s >= 0; s--) { var star = stars[s]; // Remove if off screen if (star.y > 2780) { star.destroy(); stars.splice(s, 1); } } // Spawn new stars randomly if (LK.ticks % 15 == 0 && Math.random() < 0.7) { var newStar = new Star(); newStar.x = Math.random() * 2048; newStar.y = -10; stars.push(newStar); game.addChild(newStar); } // Update player movement tracking playerLastX = player.x; playerLastY = player.y; // Boss attack logic var bossUpdateResult = boss.update(); if (bossUpdateResult === true) { var attackData = boss.attack(); var newBossAttack = new BossAttack(); newBossAttack.x = attackData.x; newBossAttack.y = attackData.y; bossAttacks.push(newBossAttack); game.addChild(newBossAttack); } else if (bossUpdateResult === "special") { var specialAttackData = boss.specialAttack(); var newSpecialAttack = new BossSpecialAttack(); newSpecialAttack.x = specialAttackData.x; newSpecialAttack.y = specialAttackData.y; bossSpecialAttacks.push(newSpecialAttack); game.addChild(newSpecialAttack); } else if (bossUpdateResult === "finalAttack") { // Flash screen red to indicate final attack starting LK.effects.flashScreen(0xff0000, 500); } else if (bossUpdateResult === "finalAttackComplete") { // Create final attack that covers entire map/screen var finalAttackData = boss.finalAttack(); var newFinalAttack = new BossFinalAttack(); newFinalAttack.x = 1024; // Center horizontally to cover entire map width newFinalAttack.y = 1366; // Center vertically to cover entire map height bossFinalAttacks.push(newFinalAttack); game.addChild(newFinalAttack); } else if (bossUpdateResult === "laser") { // Create laser attack var laserAttackData = boss.laserAttack(); var newLaser = new BossLaser(); newLaser.x = laserAttackData.x; newLaser.y = laserAttackData.y; bossLasers.push(newLaser); game.addChild(newLaser); } // Update health bars var bossHealthPercent = boss.health / boss.maxHealth; bossHealthBar.scaleX = bossHealthPercent; var playerHealthPercent = player.health / player.maxHealth; playerHealthBar.scaleX = playerHealthPercent; // Update health text bossHealthText.setText(boss.health + '/' + boss.maxHealth); playerHealthText.setText(player.health + '/' + player.maxHealth); // Check win condition if (boss.health <= 0) { LK.showYouWin(); } // Check lose condition if (player.health <= 0) { LK.showGameOver(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 10000;
self.health = 10000;
self.attackCooldown = 0;
self.attackInterval = 180; // 3 seconds at 60fps
self.moveDirection = 1; // 1 for right, -1 for left
self.moveSpeed = 2;
self.leftBound = 200;
self.rightBound = 1848;
self.verticalDirection = 1; // 1 for down, -1 for up
self.verticalSpeed = 1;
self.topBound = 300;
self.bottomBound = 500;
self.specialAttackTimer = 0;
self.specialAttackInterval = 300; // 5 seconds at 60fps
self.isCharging = false;
self.chargeTime = 0;
self.maxChargeTime = 60; // 1 second at 60fps
self.indicator = null;
self.finalAttackTriggered = false;
self.isFinalAttacking = false;
self.finalAttackTime = 0;
self.maxFinalAttackTime = 180; // 3 seconds at 60fps
self.finalAttackCanceled = false;
self.laserAttackTimer = 0;
self.laserAttackInterval = 900; // 15 seconds at 60fps
self.laserCooldown = 0; // Cooldown timer for laser attacks
self.laserCooldownTime = 300; // 5 seconds cooldown at 60fps
self.isChargingLaser = false; // New state for laser charging
self.laserChargeTime = 0; // Timer for laser charging
self.maxLaserChargeTime = 180; // 3 seconds at 60fps
self.laserUsed = false; // Track if laser has been used (only once allowed)
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health < 0) self.health = 0;
// Flash red when taking damage
tween(bossGraphics, {
tint: 0xff8888
}, {
duration: 100,
onFinish: function onFinish() {
tween(bossGraphics, {
tint: 0xffffff
}, {
duration: 100
});
}
});
};
self.attack = function () {
return {
x: self.x,
y: self.y + 100,
damage: 1
};
};
self.specialAttack = function () {
return {
x: self.x,
y: self.y + 100,
damage: 1
};
};
self.startFinalAttack = function () {
self.isFinalAttacking = true;
self.finalAttackTime = 0;
self.finalAttackCanceled = false;
// Boss stops moving during final attack
};
self.cancelFinalAttack = function () {
self.finalAttackCanceled = true;
self.isFinalAttacking = false;
self.finalAttackTime = 0;
};
self.finalAttack = function () {
return {
x: 1024,
// Center of screen horizontally
y: 1366,
// Center vertically to cover entire map
damage: 1
};
};
self.laserAttack = function () {
return {
x: self.x,
y: 1366,
// Center vertically to cover full screen
damage: 1
};
};
self.update = function () {
// Check for final attack trigger (when health drops to 1000 or below)
if (self.health <= 1000 && !self.finalAttackTriggered && !self.isFinalAttacking) {
self.finalAttackTriggered = true;
self.startFinalAttack();
return "finalAttack";
}
// Handle final attack
if (self.isFinalAttacking && !self.finalAttackCanceled) {
self.finalAttackTime++;
if (self.finalAttackTime >= self.maxFinalAttackTime) {
// Final attack completes - deal damage to entire screen
self.isFinalAttacking = false;
return "finalAttackComplete";
}
return false; // Boss doesn't move during final attack
}
// Handle special attack timing
if (!self.isFinalAttacking) {
self.specialAttackTimer++;
// Fast special attack mode when boss health drops to half or below
var currentSpecialInterval = self.health <= self.maxHealth / 2 ? 100 : self.specialAttackInterval; // Much faster when health <= 5000
// Check if it's time for special attack
if (self.specialAttackTimer >= currentSpecialInterval && !self.isCharging) {
self.isCharging = true;
self.chargeTime = 0;
// Create indicator
if (!self.indicator) {
self.indicator = self.addChild(LK.getAsset('bossSpecialIndicator', {
anchorX: 0.5,
anchorY: 0.5
}));
self.indicator.y = 80; // Position above boss
}
}
// Handle laser attack timing - only in fast mode
if (self.health <= self.maxHealth / 2) {
self.laserAttackTimer++;
// Update laser cooldown
if (self.laserCooldown > 0) {
self.laserCooldown--;
}
// Only allow laser attack once when entering fast mode
if (self.laserAttackTimer >= 300 && self.laserCooldown <= 0 && !self.isChargingLaser && !self.laserUsed) {
self.isChargingLaser = true;
self.laserChargeTime = 0;
self.laserAttackTimer = 0;
}
}
}
// Handle laser charging phase (boss stops moving)
if (self.isChargingLaser) {
self.laserChargeTime++;
// Make boss flash to indicate laser charging
tween.stop(bossGraphics, {
tint: true
});
if (self.laserChargeTime % 30 === 0) {
// Flash every 0.5 seconds
tween(bossGraphics, {
tint: 0xffff00
}, {
duration: 150
});
}
if (self.laserChargeTime >= self.maxLaserChargeTime) {
// End laser charging, launch laser attack
self.isChargingLaser = false;
self.laserChargeTime = 0;
self.laserCooldown = self.laserCooldownTime; // Start cooldown
self.laserUsed = true; // Mark laser as used
tween(bossGraphics, {
tint: 0xffffff
}, {
duration: 100
}); // Reset boss color
return "laser";
}
} else if (self.isCharging) {
self.chargeTime++;
// Make indicator blink
if (self.indicator) {
self.indicator.alpha = Math.sin(self.chargeTime * 0.3) * 0.5 + 0.5;
}
if (self.chargeTime >= self.maxChargeTime) {
// End charging, launch special attack
self.isCharging = false;
self.specialAttackTimer = 0;
// Remove indicator
if (self.indicator) {
self.indicator.destroy();
self.indicator = null;
}
return "special"; // Signal to create special attack
}
} else if (!self.isFinalAttacking) {
// Normal movement only when not charging and not doing final attack
// Handle horizontal movement
self.x += self.moveSpeed * self.moveDirection;
// Change direction when hitting bounds
if (self.x <= self.leftBound) {
self.moveDirection = 1; // Move right
self.x = self.leftBound;
} else if (self.x >= self.rightBound) {
self.moveDirection = -1; // Move left
self.x = self.rightBound;
}
// Handle vertical movement
self.y += self.verticalSpeed * self.verticalDirection;
// Change direction when hitting vertical bounds
if (self.y <= self.topBound) {
self.verticalDirection = 1; // Move down
self.y = self.topBound;
} else if (self.y >= self.bottomBound) {
self.verticalDirection = -1; // Move up
self.y = self.bottomBound;
}
}
// Normal attacks (only when not charging, not charging laser and not doing final attack)
if (!self.isCharging && !self.isChargingLaser && !self.isFinalAttacking) {
self.attackCooldown++;
// Fast attack mode when boss health drops to half or below
var currentAttackInterval = self.health <= self.maxHealth / 2 ? 10 : self.attackInterval; // Super fast when health <= 5000
if (self.attackCooldown >= currentAttackInterval) {
self.attackCooldown = 0;
return true; // Signal to create attack
}
}
return false;
};
return self;
});
var BossAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('bossAttack', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed increases in fast mode when boss health is low
self.speed = boss && boss.health <= boss.maxHealth / 2 ? 8 : 4;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
var BossFinalAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('bossFinalAttack', {
anchorX: 0.5,
anchorY: 0.5
});
attackGraphics.alpha = 0.4; // Semi-transparent red overlay covering entire screen
self.damage = 1;
self.lifeTime = 0;
self.maxLifeTime = 60; // 1 second visible
self.update = function () {
self.lifeTime++;
// Pulsing effect for entire screen coverage
attackGraphics.alpha = 0.4 + Math.sin(self.lifeTime * 0.3) * 0.3;
};
return self;
});
var BossLaser = Container.expand(function () {
var self = Container.call(this);
var laserGraphics = self.attachAsset('bossLaser', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 1;
self.lifeTime = 0;
self.maxLifeTime = 600; // 10 seconds visible
self.update = function () {
self.lifeTime++;
// Follow boss position horizontally
self.x = boss.x;
// Fade out effect in the last 3 seconds (180 frames)
if (self.lifeTime >= self.maxLifeTime - 180) {
var fadeProgress = (self.lifeTime - (self.maxLifeTime - 180)) / 180;
laserGraphics.alpha = 0.7 * (1 - fadeProgress);
} else {
// Pulsing effect
laserGraphics.alpha = 0.7 + Math.sin(self.lifeTime * 0.4) * 0.3;
}
};
return self;
});
var BossSpecialAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('bossSpecialAttack', {
anchorX: 0.5,
anchorY: 0.5
});
// Speed increases in fast mode when boss health is low
self.speed = boss && boss.health <= boss.maxHealth / 2 ? 12 : 6;
self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Game variables
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 1;
self.health = 1;
self.attackDamage = 50;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health < 0) self.health = 0;
// Flash red when taking damage
tween(playerGraphics, {
tint: 0xff8888
}, {
duration: 100,
onFinish: function onFinish() {
tween(playerGraphics, {
tint: 0xffffff
}, {
duration: 100
});
}
});
};
self.attack = function () {
return {
x: self.x,
y: self.y - 50,
damage: self.attackDamage
};
};
return self;
});
var PlayerAttack = Container.expand(function () {
var self = Container.call(this);
var attackGraphics = self.attachAsset('playerAttack', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -24;
self.damage = 50;
self.update = function () {
self.y += self.speed;
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 2 + 1; // Random speed between 1-3
self.twinkleTime = 0;
self.update = function () {
self.y += self.speed;
// Enhanced twinkling effect with random timing
self.twinkleTime++;
var twinkleIntensity = 0.4 + Math.sin(self.twinkleTime * (0.05 + Math.random() * 0.1)) * 0.3;
starGraphics.alpha = Math.max(0.2, twinkleIntensity);
// Add occasional bright flash
if (Math.random() < 0.002) {
tween(starGraphics, {
alpha: 1.0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onFinish: function onFinish() {
tween(starGraphics, {
alpha: twinkleIntensity,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game variables
// Game state
var gameStarted = false;
var startScreen = null;
var startButton = null;
var titleText = null;
var instructionText = null;
var explanationScreen = null;
var showingExplanation = false;
// Boss assets
// Player assets
// Attack button
// Attack effects
// Sounds
var boss;
var player;
var playerAttacks = [];
var bossAttacks = [];
var bossSpecialAttacks = [];
var bossFinalAttacks = [];
var bossLasers = [];
var stars = [];
var startScreenStars = [];
var dragNode = null;
var playerLastX = 0;
var playerLastY = 0;
// Health bars
var bossHealthBarBg, bossHealthBar;
var playerHealthBarBg, playerHealthBar;
// Health text
var bossHealthText, playerHealthText;
// Create start screen
startScreen = game.addChild(new Container());
// Add immediate star burst effect
for (var burstIndex = 0; burstIndex < 20; burstIndex++) {
var burstStar = new Star();
burstStar.x = 1024 + (Math.random() - 0.5) * 400;
burstStar.y = 1366 + (Math.random() - 0.5) * 400;
burstStar.speed = Math.random() * 0.5 + 0.2;
var burstGraphics = burstStar.children[0];
burstGraphics.alpha = 0;
burstGraphics.scaleX = 2;
burstGraphics.scaleY = 2;
// Animate burst effect
tween(burstGraphics, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800 + Math.random() * 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Add extra twinkle after burst
tween(burstGraphics, {
alpha: 0.3,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(burstGraphics, {
alpha: 0.7,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
});
startScreenStars.push(burstStar);
startScreen.addChild(burstStar);
}
// Create initial stars for start screen background
for (var starIndex = 0; starIndex < 50; starIndex++) {
var startStar = new Star();
startStar.x = Math.random() * 2048;
startStar.y = Math.random() * 2732;
startStar.speed = Math.random() * 1 + 0.5; // Slower speed for start screen
// Add immediate twinkling effect with random timing
startStar.twinkleTime = Math.random() * 100;
// Add enhanced initial visibility with tween effect
var starGraphics = startStar.children[0];
starGraphics.alpha = 0;
tween(starGraphics, {
alpha: 0.6 + Math.random() * 0.4,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.8 + Math.random() * 0.4
}, {
duration: 500 + Math.random() * 1000,
easing: tween.easeOut
});
startScreenStars.push(startStar);
startScreen.addChild(startStar);
}
// Create title text
titleText = new Text2('Space Ranger', {
size: 120,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
startScreen.addChild(titleText);
// Create instruction text
instructionText = new Text2('Arrastra para moverte\nDerrota al jefe para ganar\n¡Ten cuidado con sus ataques!', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1200;
startScreen.addChild(instructionText);
// Create start button
startButton = new Text2('JUGAR', {
size: 100,
fill: 0x00FF00
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 900;
startButton.y = 1600;
startScreen.addChild(startButton);
// Create explanation button
var explanationButton = new Text2('?', {
size: 100,
fill: 0x4A90E2
});
explanationButton.anchor.set(0.5, 0.5);
explanationButton.x = 1148;
explanationButton.y = 1600;
startScreen.addChild(explanationButton);
// Add enhanced pulsing effect to start button with color changes and rotation
var _buttonPulse = function buttonPulse() {
if (startButton && !gameStarted) {
// First phase: scale up with color change to bright yellow and slight rotation
tween(startButton, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFFFF00,
rotation: 0.1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second phase: scale down with color change to bright green and rotation back
tween(startButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x00FF88,
rotation: -0.05
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
// Third phase: final color pulse back to original green
tween(startButton, {
tint: 0x00FF00,
rotation: 0
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: _buttonPulse
});
}
});
}
});
}
};
_buttonPulse();
// Add pulsing effect to explanation button
var _explanationPulse = function explanationPulse() {
if (explanationButton && !gameStarted && !showingExplanation) {
tween(explanationButton, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x87CEEB
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(explanationButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x4A90E2
}, {
duration: 600,
easing: tween.easeIn,
onFinish: _explanationPulse
});
}
});
}
};
_explanationPulse();
// Start button click handler
startButton.down = function (x, y, obj) {
if (!gameStarted && !showingExplanation) {
gameStarted = true;
// Clean up start screen stars
for (var cleanupIndex = startScreenStars.length - 1; cleanupIndex >= 0; cleanupIndex--) {
startScreenStars[cleanupIndex].destroy();
}
startScreenStars = [];
startScreen.destroy();
initializeGame();
}
};
// Explanation button click handler
explanationButton.down = function (x, y, obj) {
if (!gameStarted && !showingExplanation) {
showingExplanation = true;
createExplanationScreen();
}
};
// Function to create explanation screen
function createExplanationScreen() {
explanationScreen = game.addChild(new Container());
explanationScreen.alpha = 0;
tween(explanationScreen, {
alpha: 1
}, {
duration: 300
});
// Semi-transparent background
var overlay = explanationScreen.addChild(LK.getAsset('bossFinalAttack', {
anchorX: 0.5,
anchorY: 0.5
}));
overlay.x = 1024;
overlay.y = 1366;
overlay.alpha = 0.8;
overlay.tint = 0x000033;
// Title
var explanationTitle = new Text2('GUÍA DEL JUEGO', {
size: 80,
fill: 0xFFFF00
});
explanationTitle.anchor.set(0.5, 0.5);
explanationTitle.x = 1024;
explanationTitle.y = 400;
explanationScreen.addChild(explanationTitle);
// Game mechanics explanation
var mechanicsText = new Text2('CONTROLES:\n• Arrastra para mover tu nave\n• Disparas automáticamente\n\nEL JEFE:\n• Tiene 10,000 puntos de vida\n• Se mueve por la pantalla\n• Tiene múltiples tipos de ataque', {
size: 45,
fill: 0xFFFFFF
});
mechanicsText.anchor.set(0.5, 0.5);
mechanicsText.x = 1024;
mechanicsText.y = 800;
explanationScreen.addChild(mechanicsText);
// Boss attacks explanation
var attacksText = new Text2('ATAQUES DEL JEFE:\n\n• ATAQUE NORMAL: Proyectiles rojos (1 daño)\n• ATAQUE ESPECIAL: Proyectiles pequeños rápidos (1 daño)\n• ATAQUE LÁSER: Rayo vertical que te sigue (1 daño)\n• ATAQUE FINAL: Pantalla roja completa (1 daño)', {
size: 40,
fill: 0xFF8888
});
attacksText.anchor.set(0.5, 0.5);
attacksText.x = 1024;
attacksText.y = 1200;
explanationScreen.addChild(attacksText);
// Special mechanics
var specialText = new Text2('MECÁNICAS ESPECIALES:\n\n• Cuando el jefe tiene ≤5000 vida: MODO RÁPIDO\n - Ataques más frecuentes y rápidos\n - Puede usar el ataque láser\n\n• Cuando el jefe tiene ≤1000 vida:\n - Intenta el ATAQUE FINAL mortal\n - ¡Dispárale para cancelarlo!', {
size: 38,
fill: 0x88FF88
});
specialText.anchor.set(0.5, 0.5);
specialText.x = 1024;
specialText.y = 1650;
explanationScreen.addChild(specialText);
// Victory condition
var victoryText = new Text2('OBJETIVO: Reduce la vida del jefe a 0 para ganar\nSOBREVIVE: Tienes solo 1 punto de vida', {
size: 42,
fill: 0xFFD700
});
victoryText.anchor.set(0.5, 0.5);
victoryText.x = 1024;
victoryText.y = 2000;
explanationScreen.addChild(victoryText);
// Back button
var backButton = new Text2('VOLVER', {
size: 70,
fill: 0xFF6666
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 2300;
explanationScreen.addChild(backButton);
// Back button animation
var _backPulse = function backPulse() {
if (backButton && showingExplanation) {
tween(backButton, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF8888
}, {
duration: 600,
onFinish: function onFinish() {
tween(backButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFF6666
}, {
duration: 400,
onFinish: _backPulse
});
}
});
}
};
_backPulse();
// Back button click handler
backButton.down = function (x, y, obj) {
if (showingExplanation) {
tween(explanationScreen, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
explanationScreen.destroy();
explanationScreen = null;
showingExplanation = false;
}
});
}
};
}
// Function to initialize game elements
function initializeGame() {
// Initialize boss
boss = game.addChild(new Boss());
boss.x = 1024;
boss.y = 400;
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 2200;
playerLastX = player.x;
playerLastY = player.y;
// Create boss health bar
bossHealthBarBg = game.addChild(LK.getAsset('bossHealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
}));
bossHealthBarBg.x = 1024;
bossHealthBarBg.y = 200;
bossHealthBar = game.addChild(LK.getAsset('bossHealthBar', {
anchorX: 0.5,
anchorY: 0.5
}));
bossHealthBar.x = 1024;
bossHealthBar.y = 200;
// Create player health bar
playerHealthBarBg = game.addChild(LK.getAsset('playerHealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
}));
playerHealthBarBg.x = 1024;
playerHealthBarBg.y = 2500;
playerHealthBar = game.addChild(LK.getAsset('playerHealthBar', {
anchorX: 0.5,
anchorY: 0.5
}));
playerHealthBar.x = 1024;
playerHealthBar.y = 2500;
// Create health text
bossHealthText = new Text2('10000/10000', {
size: 40,
fill: 0xFFFFFF
});
bossHealthText.anchor.set(0.5, 0.5);
bossHealthText.x = 1024;
bossHealthText.y = 160;
game.addChild(bossHealthText);
playerHealthText = new Text2('1/1', {
size: 30,
fill: 0xFFFFFF
});
playerHealthText.anchor.set(0.5, 0.5);
playerHealthText.x = 1024;
playerHealthText.y = 2540;
game.addChild(playerHealthText);
}
// Player movement
game.move = function (x, y, obj) {
if (gameStarted && dragNode) {
dragNode.x = x;
dragNode.y = y;
// Keep player within bounds
if (dragNode.x < 50) dragNode.x = 50;
if (dragNode.x > 1998) dragNode.x = 1998;
if (dragNode.y < 800) dragNode.y = 800;
if (dragNode.y > 2600) dragNode.y = 2600;
}
};
game.down = function (x, y, obj) {
if (gameStarted) {
dragNode = player;
game.move(x, y, obj);
}
};
game.up = function (x, y, obj) {
if (gameStarted) {
dragNode = null;
}
};
// Main game update loop
game.update = function () {
// Only run game logic if game has started
if (!gameStarted) {
// Update start screen stars
for (var startStarIndex = startScreenStars.length - 1; startStarIndex >= 0; startStarIndex--) {
var startStar = startScreenStars[startStarIndex];
// Remove if off screen
if (startStar.y > 2780) {
startStar.destroy();
startScreenStars.splice(startStarIndex, 1);
}
}
// Spawn new stars for start screen more frequently and consistently
if (LK.ticks % 15 == 0 && Math.random() < 0.9) {
var newStartStar = new Star();
newStartStar.x = Math.random() * 2048;
newStartStar.y = -10;
newStartStar.speed = Math.random() * 1.5 + 0.8; // Slightly faster and more visible
// Add immediate fade-in effect for new stars
var newStarGraphics = newStartStar.children[0];
newStarGraphics.alpha = 0;
tween(newStarGraphics, {
alpha: 0.5 + Math.random() * 0.5
}, {
duration: 300 + Math.random() * 500,
easing: tween.easeOut
});
startScreenStars.push(newStartStar);
startScreen.addChild(newStartStar);
}
return;
}
// Update player attacks
for (var i = playerAttacks.length - 1; i >= 0; i--) {
var attack = playerAttacks[i];
// Check if attack hit boss
if (attack.intersects(boss)) {
boss.takeDamage(attack.damage);
// Cancel final attack if boss is doing it
if (boss.isFinalAttacking && !boss.finalAttackCanceled) {
boss.cancelFinalAttack();
}
attack.destroy();
playerAttacks.splice(i, 1);
continue;
}
// Remove if off screen
if (attack.y < -50) {
attack.destroy();
playerAttacks.splice(i, 1);
}
}
// Update boss attacks
for (var j = bossAttacks.length - 1; j >= 0; j--) {
var bossAttack = bossAttacks[j];
// Check if attack hit player
if (bossAttack.intersects(player)) {
player.takeDamage(bossAttack.damage);
bossAttack.destroy();
bossAttacks.splice(j, 1);
continue;
}
// Remove if off screen
if (bossAttack.y > 2780) {
bossAttack.destroy();
bossAttacks.splice(j, 1);
}
}
// Update boss special attacks
for (var k = bossSpecialAttacks.length - 1; k >= 0; k--) {
var specialAttack = bossSpecialAttacks[k];
// Check if special attack hit player
if (specialAttack.intersects(player)) {
player.takeDamage(specialAttack.damage);
specialAttack.destroy();
bossSpecialAttacks.splice(k, 1);
continue;
}
// Remove if off screen
if (specialAttack.y > 2780) {
specialAttack.destroy();
bossSpecialAttacks.splice(k, 1);
}
}
// Player automatic attack - fast mode when health is half or below
var attackInterval = player.health <= player.maxHealth / 2 ? 3 : 20; // Super fast when health <= 0.5
if (LK.ticks % attackInterval == 0) {
var attackData = player.attack();
var newPlayerAttack = new PlayerAttack();
newPlayerAttack.x = attackData.x;
newPlayerAttack.y = attackData.y;
playerAttacks.push(newPlayerAttack);
game.addChild(newPlayerAttack);
}
// Update boss final attacks
for (var l = bossFinalAttacks.length - 1; l >= 0; l--) {
var finalAttack = bossFinalAttacks[l];
// Check if final attack hit player (covers entire screen)
if (finalAttack.intersects(player)) {
player.takeDamage(finalAttack.damage);
}
// Remove after lifetime expires
if (finalAttack.lifeTime >= finalAttack.maxLifeTime) {
finalAttack.destroy();
bossFinalAttacks.splice(l, 1);
}
}
// Update boss laser attacks
for (var m = bossLasers.length - 1; m >= 0; m--) {
var laser = bossLasers[m];
// Check if laser hit player AND player is moving
var playerIsMoving = player.x !== playerLastX || player.y !== playerLastY;
if (laser.intersects(player) && playerIsMoving) {
player.takeDamage(laser.damage);
}
// Remove after lifetime expires
if (laser.lifeTime >= laser.maxLifeTime) {
laser.destroy();
bossLasers.splice(m, 1);
}
}
// Update and spawn stars
for (var s = stars.length - 1; s >= 0; s--) {
var star = stars[s];
// Remove if off screen
if (star.y > 2780) {
star.destroy();
stars.splice(s, 1);
}
}
// Spawn new stars randomly
if (LK.ticks % 15 == 0 && Math.random() < 0.7) {
var newStar = new Star();
newStar.x = Math.random() * 2048;
newStar.y = -10;
stars.push(newStar);
game.addChild(newStar);
}
// Update player movement tracking
playerLastX = player.x;
playerLastY = player.y;
// Boss attack logic
var bossUpdateResult = boss.update();
if (bossUpdateResult === true) {
var attackData = boss.attack();
var newBossAttack = new BossAttack();
newBossAttack.x = attackData.x;
newBossAttack.y = attackData.y;
bossAttacks.push(newBossAttack);
game.addChild(newBossAttack);
} else if (bossUpdateResult === "special") {
var specialAttackData = boss.specialAttack();
var newSpecialAttack = new BossSpecialAttack();
newSpecialAttack.x = specialAttackData.x;
newSpecialAttack.y = specialAttackData.y;
bossSpecialAttacks.push(newSpecialAttack);
game.addChild(newSpecialAttack);
} else if (bossUpdateResult === "finalAttack") {
// Flash screen red to indicate final attack starting
LK.effects.flashScreen(0xff0000, 500);
} else if (bossUpdateResult === "finalAttackComplete") {
// Create final attack that covers entire map/screen
var finalAttackData = boss.finalAttack();
var newFinalAttack = new BossFinalAttack();
newFinalAttack.x = 1024; // Center horizontally to cover entire map width
newFinalAttack.y = 1366; // Center vertically to cover entire map height
bossFinalAttacks.push(newFinalAttack);
game.addChild(newFinalAttack);
} else if (bossUpdateResult === "laser") {
// Create laser attack
var laserAttackData = boss.laserAttack();
var newLaser = new BossLaser();
newLaser.x = laserAttackData.x;
newLaser.y = laserAttackData.y;
bossLasers.push(newLaser);
game.addChild(newLaser);
}
// Update health bars
var bossHealthPercent = boss.health / boss.maxHealth;
bossHealthBar.scaleX = bossHealthPercent;
var playerHealthPercent = player.health / player.maxHealth;
playerHealthBar.scaleX = playerHealthPercent;
// Update health text
bossHealthText.setText(boss.health + '/' + boss.maxHealth);
playerHealthText.setText(player.health + '/' + player.maxHealth);
// Check win condition
if (boss.health <= 0) {
LK.showYouWin();
}
// Check lose condition
if (player.health <= 0) {
LK.showGameOver();
}
};
Una nave espacial futurista mirada desde arriba en pixel art. In-Game asset. 2d. High contrast. No shadows
Un mounstro gigante con alas mirado desde arriba en pixel art. In-Game asset. 2d. High contrast. No shadows
Fuego amarillo en pixel art. In-Game asset. 2d. High contrast. No shadows
Una barra de jefe final en juego pixel art. In-Game asset. 2d. High contrast. No shadows
Ráfaga de fuego roja en pixel art. In-Game asset. 2d. High contrast. No shadows
Estrella blanca en pixel art. In-Game asset. 2d. High contrast. No shadows