/****
* 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