User prompt
Quiero que hagas todos tus esfuerzos para que ese cuando aparezca la lógica de avances de los enmigos se detenga y el jefe saque un globo de texto diciendo "me derrotas te antes, no tendrás la misma suerte!!!" Y ahí empieza todo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Y ahora quiero que me cres el nuevo activo del secret boos
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
unitUpgrades: {
archer: {
damage: 0,
power: 0
},
spearman: {
damage: 0,
power: 0
},
cavalry: {
damage: 0,
power: 0
}
},
shopCoins: 50
});
/****
* Classes
****/
var Avion = Container.expand(function () {
var self = Container.call(this);
var avionGraphics = self.attachAsset('avion', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Flight properties
self.speed = 3;
self.altitude = Math.random() * 200 + 100; // Random altitude
self.direction = Math.random() < 0.5 ? 1 : -1; // Random direction
self.bobAmount = 0;
self.bobSpeed = 0.05;
// Set initial position based on direction
if (self.direction > 0) {
self.x = -250; // Start from left
} else {
self.x = 2298; // Start from right
}
self.y = Math.random() * 800 + 200;
// Flip airplane if moving right to left
if (self.direction < 0) {
avionGraphics.scaleX = -1.5;
}
self.update = function () {
// Move directly toward fortress instead of horizontally
if (fortress) {
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 250) {
// Move toward fortress
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
// Add bobbing motion for realistic flight
self.bobAmount += self.bobSpeed;
self.y += Math.sin(self.bobAmount) * 2;
// Slight rotation for banking effect
avionGraphics.rotation = Math.sin(self.bobAmount) * 0.1;
// Check collision with fortress
if (fortress) {
var fortressDist = Math.sqrt(Math.pow(fortress.x - self.x, 2) + Math.pow(fortress.y - self.y, 2));
if (fortressDist < 250) {
// Direct collision without cinematic
var explosion = new Explosion(self.x, self.y);
game.addChild(explosion);
// Play explosion sound
LK.getSound('explosion').play();
// Airplane crashed into fortress - hide all non-alternate enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Hide enemies that are not airplanes (check if enemy doesn't have airplane properties)
if (enemy.direction === undefined && enemy.speed === undefined) {
enemy.visible = false;
}
}
// Stop enemy spawning by setting current scenario permanently
currentScenario = 'alternate';
// Airplane crashed into fortress - game over
LK.effects.flashObject(fortress, 0xFF0000, 1000);
LK.effects.flashScreen(0xFF0000, 1000);
gameOver = true;
self.markForDestroy = true;
// Show game over immediately
LK.showGameOver();
return;
}
}
// Remove when off screen
if (self.x < -300 || self.x > 2348) {
self.markForDestroy = true;
}
};
return self;
});
var BigPoderProjectile = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 6; // Slower than regular Poder
// Calculate direction to target
var dx = target.x - startX;
var dy = target.y - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
// Set rotation to face target
projectileGraphics.rotation = Math.atan2(dy, dx);
// Start with growing animation
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut
});
// Add pulsing effect
var spinSpeed = 0.08;
self.pulseDirection = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Spin and pulse the projectile
projectileGraphics.rotation += spinSpeed;
// Pulsing scale effect
if (self.scaleX >= 1.8) {
self.pulseDirection = -1;
} else if (self.scaleX <= 1.2) {
self.pulseDirection = 1;
}
self.scaleX += self.pulseDirection * 0.02;
self.scaleY += self.pulseDirection * 0.02;
// Check if hit fortress (secret boss attacks fortress directly)
if (fortress && fortress.takeDamage) {
var fortressDist = Math.sqrt(Math.pow(fortress.x - self.x, 2) + Math.pow(fortress.y - self.y, 2));
if (fortressDist < 250) {
// Ensure damage is exactly 2 points for reduced boss attack power
fortress.takeDamage(2);
// Big explosion effect
LK.effects.flashObject(fortress, 0x00FFFF, 800);
// Shrinking effect on hit
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
return;
}
}
// Remove if off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.markForDestroy = true;
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('Bossogro', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
// Make boss twice as big
scaleY: 2.0
});
// Add boss details
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
eyesAsset.x = 0;
eyesAsset.y = -240; // Adjusted for larger size
eyesAsset.tint = 0xFF0000; // Red eyes for boss
// Add crown/spikes
var crownAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.6,
scaleY: 1.2
});
crownAsset.x = 0;
crownAsset.y = -320; // Adjusted for larger size
crownAsset.tint = 0xFFD700; // Gold crown
self.health = 500; // Same health as secret boss
self.maxHealth = 500;
self.speed = 0.5; // Slower than regular enemies
self.damage = 30; // More damage than regular enemies
self.coinValue = 50; // Lots of coins when defeated
self.enemyType = 'boss';
self.isBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 300; // 5 seconds at 60fps
self.update = function () {
// Add intimidating pulsing aura effect
if (LK.ticks % 120 === 0) {
// Every 2 seconds
LK.effects.flashObject(self, 0xFF4500, 300); // Orange intimidation flash
// Slight scale pulse for intimidation
tween(self, {
scaleX: 2.1,
scaleY: 2.1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
}
// Enemy special power - speed boost when in range of units
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 300) {
// Within range of a unit
self.lastSpecialPower = LK.ticks;
// Temporary speed boost and visual effect
var originalSpeed = self.speed;
self.speed *= 2.5; // 2.5x speed boost
LK.effects.flashObject(self, 0xFF8800, 300); // Orange flash
// Special attack when in range but not at fortress yet
// Speed boost animation
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
// Return to normal after 1 second
LK.setTimeout(function () {
self.speed = originalSpeed;
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 400 && distance > 215 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Boss special power - weakens fortress
LK.effects.flashObject(self, 0xFF00FF, 500);
fortress.takeDamage(15); // Special attack damage
tween(self, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut
});
tween(self, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
if (distance > 300) {
// Not at fortress yet - move closer
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Boss doesn't reach fortress - just damages it from distance and disappears
fortress.takeDamage(5);
enemiesEntered++; // Count boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when boss dies to allow repeat triggers
trollfaceShown = false;
gameWon = true; // Win when boss is defeated!
self.markForDestroy = true;
}
};
return self;
});
var DefensiveUnit = Container.expand(function (unitType) {
var self = Container.call(this);
var unitGraphics = self.attachAsset(unitType, {
anchorX: 0.5,
anchorY: 1.0
});
// Add character details for Mario-style appearance
var hatAsset, weaponAsset;
if (unitType === 'archer') {
// Add archer hat
hatAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.6
});
hatAsset.x = 0;
hatAsset.y = -140;
hatAsset.tint = 0x228B22;
} else if (unitType === 'spearman') {
// Add spearman helmet
hatAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.9,
scaleY: 0.7
});
hatAsset.x = 0;
hatAsset.y = -140;
hatAsset.tint = 0x4169E1;
} else if (unitType === 'cavalry') {
// Add cavalry plume
hatAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 0.8
});
hatAsset.x = 0;
hatAsset.y = -140;
hatAsset.tint = 0x9932CC;
}
if (unitType === 'archer') {
self.damage = 15;
self.range = 350;
self.attackSpeed = 45; // frames between attacks
self.cost = 10;
} else if (unitType === 'spearman') {
self.damage = 25;
self.range = 225;
self.attackSpeed = 30;
self.cost = 15;
} else if (unitType === 'cavalry') {
self.damage = 35;
self.range = 250;
self.attackSpeed = 20;
self.cost = 25;
}
// Apply shop power upgrades to range and attack speed with validation
var powerUpgrade = 0;
if (unitUpgrades[unitType] && typeof unitUpgrades[unitType].power === 'number') {
powerUpgrade = Math.max(0, unitUpgrades[unitType].power);
}
self.range += powerUpgrade * 25; // Each power level adds 25 range
self.attackSpeed = Math.max(5, self.attackSpeed - powerUpgrade * 3); // Each power level improves speed (lower = faster), minimum 5
self.unitType = unitType;
self.attackTimer = 0;
self.target = null;
self.battlesCount = 0; // Track number of battles
self.maxDamage = self.damage; // Store original damage for weakening calculation
self.fatigueLevel = 0; // Unit fatigue from battles
self.update = function () {
self.attackTimer--;
if (self.attackTimer <= 0) {
self.findTarget();
if (self.target && self.isInRange(self.target)) {
self.attack();
self.attackTimer = self.attackSpeed;
}
}
};
self.findTarget = function () {
var closestDistance = self.range;
self.target = null;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = enemy;
}
}
};
self.isInRange = function (target) {
var distance = Math.sqrt(Math.pow(target.x - self.x, 2) + Math.pow(target.y - self.y, 2));
return distance <= self.range;
};
self.attack = function () {
if (self.target) {
// Apply battle fatigue - units get weaker with each battle
self.battlesCount++;
self.fatigueLevel = Math.min(self.battlesCount * 0.05, 0.4); // Max 40% damage reduction
var currentDamage = Math.max(1, Math.floor(self.maxDamage * (1 - self.fatigueLevel))); // Ensure minimum 1 damage
// Apply shop damage upgrades with validation
var damageUpgrade = 0;
if (unitUpgrades[self.unitType] && typeof unitUpgrades[self.unitType].damage === 'number') {
damageUpgrade = Math.max(0, unitUpgrades[self.unitType].damage);
}
currentDamage += damageUpgrade * 5;
// Apply combo multiplier to damage
if (comboSystem && comboSystem.comboMultiplier) {
currentDamage = Math.floor(currentDamage * comboSystem.comboMultiplier);
}
var projectile = new Projectile(self.unitType, self.x, self.y, self.target, currentDamage);
// Mark projectile with unit type for tracking
projectile.sourceUnitType = self.unitType;
projectiles.push(projectile);
game.addChild(projectile);
// Play general attack sound for all units
LK.getSound('Ataque').play();
if (self.unitType === 'cavalry') {
LK.getSound('Espada').play();
} else if (self.unitType === 'archer') {
LK.getSound('Arco').play();
}
// Visual feedback for tired units
if (self.fatigueLevel > 0.2) {
tween(self, {
alpha: 0.7
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
alpha: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
}
};
// Nuclear defense cubes are 100% invincible - no takeDamage method needed
return self;
});
var DialogueSound = Container.expand(function () {
var self = Container.call(this);
self.playDialogueSound = function () {
// Play dialogue sound effect when characters speak
LK.getSound('dialogo').play();
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
var enemyGraphics = self.attachAsset(enemyType, {
anchorX: 0.5,
anchorY: 1.0
});
// Add enemy details for Mario-style appearance
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
eyesAsset.x = 0;
eyesAsset.y = -120;
eyesAsset.tint = 0xFFFFFF;
if (enemyType === 'strongEnemy') {
// Add spikes for strong enemy
var spikesAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.6
});
spikesAsset.x = 0;
spikesAsset.y = -160;
spikesAsset.tint = 0x000000;
}
if (enemyType === 'enemy') {
self.health = 30;
self.maxHealth = 30;
self.speed = 1;
self.damage = 10;
self.coinValue = 2;
} else if (enemyType === 'strongEnemy') {
self.health = 60;
self.maxHealth = 60;
self.speed = 0.8;
self.damage = 20;
self.coinValue = 5;
}
self.enemyType = enemyType;
self.lastSpecialPower = 0;
self.specialPowerCooldown = 120; // 2 seconds at 60fps (shorter than other units)
self.canUseSpecialPower = true;
self.update = function () {
// Enemy special power - launch Poder attack when in range of units
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 300) {
// Within range of a unit - launch Poder attack
self.lastSpecialPower = LK.ticks;
var poderProjectile = new PoderProjectile(self.x, self.y, unit, self.damage * 0.5);
projectiles.push(poderProjectile);
game.addChild(poderProjectile);
// Play power sound effect
LK.getSound('Poder').play();
// Visual effect for enemy using power
LK.effects.flashObject(self, 0x00FFFF, 300);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 215) {
// Not at fortress yet
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack fortress
fortress.takeDamage(self.damage);
enemiesEntered++; // Count enemy entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when enemy dies to allow repeat triggers
trollfaceShown = false;
self.markForDestroy = true;
}
};
return self;
});
var Explosion = Container.expand(function (x, y) {
var self = Container.call(this);
// Create multiple explosion particles with more variety
var particles = [];
var particleCount = 12 + Math.floor(Math.random() * 6); // 12-18 particles
for (var i = 0; i < particleCount; i++) {
var particle = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + Math.random() * 0.6,
scaleY: 0.2 + Math.random() * 0.6
});
// Random position offset in circular pattern
var angle = Math.PI * 2 * i / particleCount + Math.random() * 0.5;
var distance = Math.random() * 80;
particle.x = Math.cos(angle) * distance;
particle.y = Math.sin(angle) * distance;
// Enhanced color variety with bright explosion colors
var colors = [0xFF0000, 0xFF4500, 0xFF6600, 0xFF8800, 0xFFAA00, 0xFFCC00, 0xFFFFFF];
particle.tint = colors[Math.floor(Math.random() * colors.length)];
// Random rotation for variety
particle.rotation = Math.random() * Math.PI * 2;
particles.push(particle);
}
self.x = x;
self.y = y;
// Create central flash effect
var centralFlash = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
centralFlash.tint = 0xFFFFFF;
centralFlash.alpha = 1.0;
// Central flash expansion and fade
tween(centralFlash, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut
});
// Animate explosion particles with improved effects
for (var j = 0; j < particles.length; j++) {
var particle = particles[j];
var finalDistance = 150 + Math.random() * 100;
var particleAngle = Math.atan2(particle.y, particle.x);
var finalX = particle.x + Math.cos(particleAngle) * finalDistance;
var finalY = particle.y + Math.sin(particleAngle) * finalDistance;
// Expanding animation with rotation
tween(particle, {
x: finalX,
y: finalY,
scaleX: particle.scaleX * 1.5,
scaleY: particle.scaleY * 1.5,
rotation: particle.rotation + Math.PI * 3,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
// Varied duration
easing: tween.easeOut
});
}
// Screen shake effect for big explosions
if (Math.random() < 0.3) {
// 30% chance for screen shake
LK.effects.flashScreen(0xFFAA00, 300);
}
// Auto-destroy after animation
LK.setTimeout(function () {
self.markForDestroy = true;
}, 1200);
return self;
});
var Fortress = Container.expand(function () {
var self = Container.call(this);
var fortressGraphics = self.attachAsset('fortress', {
anchorX: 0.5,
anchorY: 1.0
});
// Add castle tower details
var leftTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
leftTower.x = -125;
leftTower.y = -75;
leftTower.tint = 0x696969;
var rightTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
rightTower.x = 125;
rightTower.y = -75;
rightTower.tint = 0x696969;
// Add flag
var flag = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.5
});
flag.x = 0;
flag.y = -350;
flag.tint = 0xFF4500;
// Add shield visual effect
var shieldGraphics = self.attachAsset('fortress', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
shieldGraphics.alpha = 0;
shieldGraphics.tint = 0x00FFFF;
self.health = fortressMaxHealth;
self.maxHealth = fortressMaxHealth;
self.activateShield = function () {
if (shieldCooldown <= 0) {
shieldActive = true;
shieldCooldown = shieldMaxCooldown;
shieldGraphics.alpha = 0.5;
tween(shieldGraphics, {
alpha: 0.8
}, {
duration: 500,
easing: tween.easeInOut
});
LK.setTimeout(function () {
shieldActive = false;
tween(shieldGraphics, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000); // Shield lasts 3 seconds
}
};
self.takeDamage = function (damage) {
if (!shieldActive) {
fortressCurrentHealth -= damage;
self.health = fortressCurrentHealth;
LK.effects.flashObject(self, 0xFF0000, 300);
if (fortressCurrentHealth <= 0) {
gameOver = true;
}
} else {
// Shield blocks damage
LK.effects.flashObject(shieldGraphics, 0xFFFFFF, 200);
}
};
return self;
});
var Fortress2 = Container.expand(function () {
var self = Container.call(this);
var fortressGraphics = self.attachAsset('fortress2', {
anchorX: 0.5,
anchorY: 1.0
});
// Add castle tower details
var leftTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
leftTower.x = -125;
leftTower.y = -75;
leftTower.tint = 0x696969;
var rightTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
rightTower.x = 125;
rightTower.y = -75;
rightTower.tint = 0x696969;
// Add flag
var flag = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.5
});
flag.x = 0;
flag.y = -350;
flag.tint = 0xFF4500;
// Add shield visual effect
var shieldGraphics = self.attachAsset('fortress2', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
shieldGraphics.alpha = 0;
shieldGraphics.tint = 0x00FFFF;
self.health = fortressMaxHealth;
self.maxHealth = fortressMaxHealth;
self.activateShield = function () {
if (shieldCooldown <= 0) {
shieldActive = true;
shieldCooldown = shieldMaxCooldown;
shieldGraphics.alpha = 0.5;
tween(shieldGraphics, {
alpha: 0.8
}, {
duration: 500,
easing: tween.easeInOut
});
LK.setTimeout(function () {
shieldActive = false;
tween(shieldGraphics, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000); // Shield lasts 3 seconds
}
};
self.takeDamage = function (damage) {
if (!shieldActive) {
fortressCurrentHealth -= damage;
self.health = fortressCurrentHealth;
LK.effects.flashObject(self, 0xFF0000, 300);
if (fortressCurrentHealth <= 0) {
gameOver = true;
}
} else {
// Shield blocks damage
LK.effects.flashObject(shieldGraphics, 0xFFFFFF, 200);
}
};
return self;
});
var MiniBoss = Container.expand(function () {
var self = Container.call(this);
var miniBossGraphics = self.attachAsset('miniboos1', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
// Smaller than regular boss but bigger than normal enemy
scaleY: 1.5
});
// Add mini boss details
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
eyesAsset.x = 0;
eyesAsset.y = -180; // Adjusted for smaller size
eyesAsset.tint = 0xFFFF00; // Yellow eyes for mini boss
// Add small crown/spikes
var crownAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.0
});
crownAsset.x = 0;
crownAsset.y = -240; // Adjusted for smaller size
crownAsset.tint = 0xC0C0C0; // Silver crown
self.health = 150; // More health than enemies but less than boss
self.maxHealth = 150;
self.speed = 0.8; // Faster than regular boss but slower than normal enemies
self.damage = 20; // Less damage than regular boss
self.coinValue = 25; // Medium reward
self.enemyType = 'miniboss';
self.isMiniBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 240; // 4 seconds at 60fps
self.lastSpecialPower = 0;
self.specialPowerCooldown = 120; // 2 seconds at 60fps
self.canUseSpecialPower = true;
self.update = function () {
// Mini boss special power - similar to regular boss but weaker
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 250) {
// Shorter range than regular boss
// Within range of a unit
self.lastSpecialPower = LK.ticks;
// Temporary speed boost and visual effect
var originalSpeed = self.speed;
self.speed *= 2.0; // 2x speed boost (less than regular boss)
LK.effects.flashObject(self, 0xFFFF00, 300); // Yellow flash
// Speed boost animation
tween(self, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 300,
easing: tween.easeOut
});
// Return to normal after 1 second
LK.setTimeout(function () {
self.speed = originalSpeed;
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 350 && distance > 215 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Mini boss special power - weakens fortress but less than regular boss
LK.effects.flashObject(self, 0xFFFF00, 400);
fortress.takeDamage(10); // Less special attack damage than regular boss
tween(self, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
if (distance > 300) {
// Not at fortress yet - move closer
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Mini boss doesn't reach fortress - just damages it from distance and disappears
fortress.takeDamage(5);
enemiesEntered++; // Count mini boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when mini boss dies to allow repeat triggers
trollfaceShown = false;
self.markForDestroy = true;
}
};
return self;
});
var MiniBoss2 = Container.expand(function () {
var self = Container.call(this);
var miniBossGraphics = self.attachAsset('Miniboos2', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
// Smaller than regular boss but bigger than normal enemy
scaleY: 1.5
});
// Add mini boss details
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
eyesAsset.x = 0;
eyesAsset.y = -180; // Adjusted for smaller size
eyesAsset.tint = 0xFFFF00; // Yellow eyes for mini boss
// Add small crown/spikes
var crownAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.0
});
crownAsset.x = 0;
crownAsset.y = -240; // Adjusted for smaller size
crownAsset.tint = 0xC0C0C0; // Silver crown
self.health = 150; // More health than enemies but less than boss
self.maxHealth = 150;
self.speed = 0.8; // Faster than regular boss but slower than normal enemies
self.damage = 20; // Less damage than regular boss
self.coinValue = 25; // Medium reward
self.enemyType = 'miniboss';
self.isMiniBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 240; // 4 seconds at 60fps
self.lastSpecialPower = 0;
self.specialPowerCooldown = 120; // 2 seconds at 60fps
self.canUseSpecialPower = true;
self.update = function () {
// Mini boss special power - similar to regular boss but weaker
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 250) {
// Shorter range than regular boss
// Within range of a unit
self.lastSpecialPower = LK.ticks;
// Temporary speed boost and visual effect
var originalSpeed = self.speed;
self.speed *= 2.0; // 2x speed boost (less than regular boss)
LK.effects.flashObject(self, 0xFFFF00, 300); // Yellow flash
// Speed boost animation
tween(self, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 300,
easing: tween.easeOut
});
// Return to normal after 1 second
LK.setTimeout(function () {
self.speed = originalSpeed;
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 350 && distance > 215 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Mini boss special power - weakens fortress but less than regular boss
LK.effects.flashObject(self, 0xFFFF00, 400);
fortress.takeDamage(10); // Less special attack damage than regular boss
tween(self, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
if (distance > 300) {
// Not at fortress yet - move closer
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Mini boss doesn't reach fortress - just damages it from distance and disappears
fortress.takeDamage(5);
enemiesEntered++; // Count mini boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when mini boss dies to allow repeat triggers
trollfaceShown = false;
self.markForDestroy = true;
}
};
return self;
});
// Missile class for missile rain events
var Missile = Container.expand(function (x) {
var self = Container.call(this);
var missileAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
self.x = x;
self.y = -60;
self.speed = 18 + Math.random() * 8;
self.hasDamaged = false;
self.update = function () {
self.y += self.speed;
// Check collision with fortress (centered)
if (!self.hasDamaged && fortress && Math.abs(self.x - fortress.x) < 180 && Math.abs(self.y - fortress.y) < 220) {
self.hasDamaged = true;
if (!shieldActive) {
fortressCurrentHealth = Math.max(0, fortressCurrentHealth - 5);
fortress.health = fortressCurrentHealth;
LK.effects.flashObject(fortress, 0xFF0000, 200);
} else {
LK.effects.flashObject(fortress, 0x00FFFF, 200);
}
// Explosion effect
var explosion = new Explosion(self.x, self.y);
game.addChild(explosion);
self.markForDestroy = true;
}
// Remove if off screen
if (self.y > 2800) {
self.markForDestroy = true;
}
};
return self;
});
// MissileRain class for random missile rain event
var MissileRain = Container.expand(function () {
var self = Container.call(this);
self.duration = 1200; // 20 seconds at 60fps
self.ticks = 0;
self.missiles = [];
self.active = true;
// Show warning overlay
var warningOverlay = LK.getAsset('Alerta', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 7
});
warningOverlay.x = 1024;
warningOverlay.y = 600;
warningOverlay.alpha = 1.0;
self.addChild(warningOverlay);
// Animate warning overlay
tween(warningOverlay, {
alpha: 0
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
warningOverlay.destroy();
}
});
// Play alert sound
LK.getSound('alerta').play();
self.update = function () {
self.ticks++;
// Spawn missiles randomly across the screen every 8-16 ticks
if (self.ticks % (8 + Math.floor(Math.random() * 8)) === 0 && self.ticks < self.duration) {
var missileX = 100 + Math.random() * 1848;
var missile = new Missile(missileX);
self.missiles.push(missile);
game.addChild(missile);
}
// Update all missiles
for (var i = self.missiles.length - 1; i >= 0; i--) {
var m = self.missiles[i];
m.update();
if (m.markForDestroy) {
m.destroy();
self.missiles.splice(i, 1);
}
}
// End rain after duration
if (self.ticks >= self.duration && self.missiles.length === 0) {
self.active = false;
self.markForDestroy = true;
}
};
return self;
});
var NuclearDefenseCube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Show the green cube
cubeGraphics.visible = true;
// Add Peligro asset as danger indicator - larger size
var dangerGraphics = self.attachAsset('Peligro', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
dangerGraphics.x = 0;
dangerGraphics.y = 0;
// Visual warning indicators
self.warningLevel = 0;
self.maxWarningLevel = 240; // 4 seconds at 60fps
self.isDefused = false;
// Warning colors from green to red
var warningColors = [0x00FF00, 0x66FF00, 0xCCFF00, 0xFFFF00, 0xFFCC00, 0xFF8800, 0xFF4400, 0xFF0000];
self.update = function () {
if (!self.isDefused) {
self.warningLevel++;
// Change color based on warning level
var colorIndex = Math.min(Math.floor(self.warningLevel / 30), warningColors.length - 1);
dangerGraphics.tint = warningColors[colorIndex];
// Pulsing effect gets faster as time runs out
var pulseSpeed = 0.02 + self.warningLevel / self.maxWarningLevel * 0.08;
dangerGraphics.scaleX = 1.5 + Math.sin(LK.ticks * pulseSpeed) * 0.2;
dangerGraphics.scaleY = 1.5 + Math.sin(LK.ticks * pulseSpeed) * 0.2;
// Rotation effect
dangerGraphics.rotation += 0.05 + self.warningLevel / self.maxWarningLevel * 0.1;
// Check if time is up
if (self.warningLevel >= self.maxWarningLevel) {
// Nuclear bomb hits fortress
self.triggerNuclearBomb();
}
}
};
self.triggerNuclearBomb = function () {
// Massive damage to fortress
if (!shieldActive) {
fortressCurrentHealth = Math.max(0, fortressCurrentHealth - 50);
fortress.health = fortressCurrentHealth;
} else {
// Even with shield, some damage gets through
fortressCurrentHealth = Math.max(0, fortressCurrentHealth - 15);
fortress.health = fortressCurrentHealth;
}
// Massive explosion at fortress
var nuclearExplosion = new Explosion(fortress.x, fortress.y);
// Scale up the explosion
tween(nuclearExplosion, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 800,
easing: tween.easeOut
});
game.addChild(nuclearExplosion);
// Screen effects
LK.effects.flashScreen(0xFFFFFF, 1200);
LK.effects.flashObject(fortress, 0xFF0000, 1500);
// Play explosion sound
LK.getSound('explosion').play();
self.markForDestroy = true;
};
self.down = function (x, y, obj) {
if (!self.isDefused) {
// Successfully defused
self.isDefused = true;
// Visual feedback
dangerGraphics.tint = 0x00FF00;
LK.effects.flashObject(self, 0x00FF00, 500);
// Reward player with coins
coins += 5;
// Play success sound
LK.getSound('Poder').play();
// Shrinking animation
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
}
};
return self;
});
var PoderProjectile = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 8; // Slower than regular projectiles
// Calculate direction to target
var dx = target.x - startX;
var dy = target.y - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
// Set rotation to face target
projectileGraphics.rotation = Math.atan2(dy, dx);
// Add spinning effect
var spinSpeed = 0.1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Spin the projectile
projectileGraphics.rotation += spinSpeed;
// Check if hit target
if (self.target && self.target.takeDamage && Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2)) < 80) {
self.target.takeDamage(self.damage);
// Add special effect when hitting
LK.effects.flashObject(self.target, 0x00FFFF, 500);
self.markForDestroy = true;
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.markForDestroy = true;
}
};
return self;
});
var Projectile = Container.expand(function (unitType, startX, startY, target, damage) {
var self = Container.call(this);
var assetType = unitType === 'archer' ? 'arrow' : 'spear';
var projectileGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 12;
// Calculate direction to target
var dx = target.x - startX;
var dy = target.y - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
// Set rotation to face target
projectileGraphics.rotation = Math.atan2(dy, dx);
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Check if hit target
if (self.target && self.target.takeDamage && Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2)) < 80) {
// Track health before damage for kill detection
var targetHealthBefore = self.target.health;
self.target.takeDamage(self.damage);
// Check if this projectile killed the target and we're tracking
if (trackingActive && targetHealthBefore > 0 && self.target.health <= 0) {
// Only count kills from archer or spearman projectiles
if (self.sourceUnitType === 'archer' || self.sourceUnitType === 'spearman') {
enemiesKilledByTrackedUnits++;
// Check if we've killed 5 enemies with only our tracked units
if (enemiesKilledByTrackedUnits >= 5 && !secretBossSpawned) {
secretBossConditionMet = true;
}
}
}
// Check if this projectile killed the target and we're tracking for second condition
if (trackingActive2 && targetHealthBefore > 0 && self.target.health <= 0) {
// Only count kills from spearman projectiles for second condition
if (self.sourceUnitType === 'spearman') {
if (enemiesKilledByTrackedUnits >= 5 && !secondSecretBossSpawned) {
secondSecretBossConditionMet = true;
}
}
}
// Trigger combo on kill
if (targetHealthBefore > 0 && self.target.health <= 0) {
// Find the source unit and add to combo
for (var cu = 0; cu < defensiveUnits.length; cu++) {
var checkUnit = defensiveUnits[cu];
if (checkUnit && checkUnit.unitType === self.sourceUnitType) {
comboSystem.addKill(checkUnit);
break;
}
}
}
self.markForDestroy = true;
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.markForDestroy = true;
}
};
return self;
});
var ScenarioCube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('ScenarioCube', {
anchorX: 0.5,
anchorY: 0.5
});
// Add pulsing animation
self.pulseDirection = 1;
self.spawnTime = LK.ticks;
self.update = function () {
// Pulsing effect
if (cubeGraphics.scaleX >= 1.3) {
self.pulseDirection = -1;
} else if (cubeGraphics.scaleX <= 0.8) {
self.pulseDirection = 1;
}
cubeGraphics.scaleX += self.pulseDirection * 0.02;
cubeGraphics.scaleY += self.pulseDirection * 0.02;
// Rotation effect
cubeGraphics.rotation += 0.1;
// Auto-disappear after 10 seconds
if (LK.ticks - self.spawnTime > 600) {
self.markForDestroy = true;
}
};
self.down = function (x, y, obj) {
// Trigger scenario change
changeScenario();
self.markForDestroy = true;
};
return self;
});
var SecretBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('Jefesecreto', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 3.0,
scaleY: 3.0
});
// Add mystical aura
var auraAsset = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
auraAsset.x = 0;
auraAsset.y = -200;
auraAsset.alpha = 0.3;
auraAsset.tint = 0xFF00FF;
// Add health bar background
var healthBarBg = self.attachAsset('HealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -350;
// Add health bar fill
var healthBarFill = self.attachAsset('HealthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -200;
healthBarFill.y = -350;
self.health = 500; // Even more health than regular boss
self.maxHealth = 500;
self.speed = 0.3; // Very slow but powerful
self.damage = 50; // Massive damage
self.coinValue = 100; // Huge reward
self.enemyType = 'secretboss';
self.isSecretBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 180; // 3 seconds
self.update = function () {
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 400 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Mystical aura spinning effect
auraAsset.rotation += 0.05;
// Check distance to fortress for BigPoder attack
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// BigPoder attack when in range but not too close
if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown && distance <= 600 && distance > 300) {
self.lastSpecialAttack = LK.ticks;
// Launch BigPoder projectile at fortress with reduced damage
var bigPoderProjectile = new BigPoderProjectile(self.x, self.y, fortress, 2);
projectiles.push(bigPoderProjectile);
game.addChild(bigPoderProjectile);
// Play power sound effect louder
LK.getSound('Poder').play();
// Visual effect with growing animation
LK.effects.flashObject(self, 0xFF00FF, 500);
tween(self, {
scaleX: 3.5,
scaleY: 3.5
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 400,
easing: tween.easeIn
});
}
});
} else if (distance > 600) {
// Move toward fortress when far away
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else if (distance <= 600) {
// Stay in attack range, don't get too close
// Do nothing, just stay and attack
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
LK.getSound('jefesecreto').play();
// Switch back to normal background music when secret boss is defeated
LK.playMusic('background');
gameWon = true; // Win when secret boss is defeated!
self.markForDestroy = true;
}
};
return self;
});
var SecretBoss2 = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('Jefesecreto', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.5,
scaleY: 2.5
});
// Add mystical aura
var auraAsset = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
auraAsset.x = 0;
auraAsset.y = -150;
auraAsset.alpha = 0.3;
auraAsset.tint = 0x00FF00;
// Add health bar background
var healthBarBg = self.attachAsset('HealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -280;
// Add health bar fill
var healthBarFill = self.attachAsset('HealthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -200;
healthBarFill.y = -280;
self.health = 400; // Slightly less than first secret boss
self.maxHealth = 400;
self.speed = 0.35; // Slightly faster than first secret boss
self.damage = 45; // Less damage than first secret boss
self.coinValue = 80; // Good reward
self.enemyType = 'secretboss2';
self.isSecretBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 200; // 3.3 seconds
self.update = function () {
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 400 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Mystical aura spinning effect
auraAsset.rotation += 0.08;
// Check distance to fortress for BigPoder attack
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// BigPoder attack when in range but not too close
if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown && distance <= 550 && distance > 300) {
self.lastSpecialAttack = LK.ticks;
// Launch BigPoder projectile at fortress with reduced damage
var bigPoderProjectile = new BigPoderProjectile(self.x, self.y, fortress, 2);
projectiles.push(bigPoderProjectile);
game.addChild(bigPoderProjectile);
// Play power sound effect
LK.getSound('Poder').play();
// Visual effect
LK.effects.flashObject(self, 0x00FF00, 400);
tween(self, {
scaleX: 2.8,
scaleY: 2.8
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 350,
easing: tween.easeIn
});
}
});
} else if (distance > 550) {
// Move toward fortress when far away
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else if (distance <= 550) {
// Stay in attack range
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
LK.getSound('jefesecreto').play();
// Switch back to normal background music when secret boss is defeated
LK.playMusic('background');
gameWon = true; // Win when secret boss is defeated!
self.markForDestroy = true;
}
};
return self;
});
var TimeDistortion = Container.expand(function () {
var self = Container.call(this);
// Visual effect circle that expands outward
var distortionRing = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
distortionRing.tint = 0x00FFFF;
distortionRing.alpha = 0.6;
// Inner energy core
var energyCore = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
energyCore.tint = 0xFFFFFF;
energyCore.alpha = 0.8;
self.duration = 600; // 10 seconds
self.ticks = 0;
self.active = true;
self.affectedEnemies = [];
self.affectedProjectiles = [];
// Expansion animation for ring
tween(distortionRing, {
scaleX: 15,
scaleY: 15,
alpha: 0.2
}, {
duration: 1000,
easing: tween.easeOut
});
// Pulsing animation for core
function pulseCore() {
if (self.active) {
tween(energyCore, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.active) {
tween(energyCore, {
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.6
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
pulseCore();
}
});
}
}
});
}
}
pulseCore();
self.update = function () {
self.ticks++;
energyCore.rotation += 0.15; // Spinning energy core
distortionRing.rotation -= 0.05; // Counter-rotating ring
// Apply time distortion effects to all enemies and projectiles within range
var distortionRadius = 800;
// Slow down enemies within range
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= distortionRadius) {
// Mark enemy as affected if not already
if (self.affectedEnemies.indexOf(enemy) === -1) {
self.affectedEnemies.push(enemy);
enemy.originalSpeed = enemy.speed;
enemy.speed *= 0.3; // Slow to 30% speed
// Visual effect - blue tint
tween(enemy, {
tint: 0x0088FF
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
// Slow down enemy projectiles within range
for (var j = 0; j < projectiles.length; j++) {
var proj = projectiles[j];
if (proj.target && proj.target === fortress) {
// Only affect enemy projectiles (those targeting fortress)
var projDistance = Math.sqrt(Math.pow(proj.x - self.x, 2) + Math.pow(proj.y - self.y, 2));
if (projDistance <= distortionRadius) {
if (self.affectedProjectiles.indexOf(proj) === -1) {
self.affectedProjectiles.push(proj);
proj.originalSpeed = proj.speed;
proj.speed *= 0.2; // Slow projectiles even more
// Visual trail effect
tween(proj, {
tint: 0x00DDFF
}, {
duration: 200,
easing: tween.easeOut
});
}
}
}
}
// End distortion after duration
if (self.ticks >= self.duration) {
self.active = false;
// Restore original speeds and remove effects
for (var k = 0; k < self.affectedEnemies.length; k++) {
var affectedEnemy = self.affectedEnemies[k];
if (affectedEnemy.originalSpeed !== undefined) {
affectedEnemy.speed = affectedEnemy.originalSpeed;
// Remove tint
tween(affectedEnemy, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeOut
});
}
}
for (var l = 0; l < self.affectedProjectiles.length; l++) {
var affectedProj = self.affectedProjectiles[l];
if (affectedProj.originalSpeed !== undefined) {
affectedProj.speed = affectedProj.originalSpeed;
// Remove tint
tween(affectedProj, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Fade out effect
tween(self, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
}
};
return self;
});
// Unit Leveling and Combo System
var UnitCombo = Container.expand(function () {
var self = Container.call(this);
self.comboCount = 0;
self.comboTimer = 0;
self.maxComboTimer = 300; // 5 seconds at 60fps
self.comboMultiplier = 1.0;
self.comboLevel = 0; // 0-5 levels
self.participatingUnits = [];
self.update = function () {
if (self.comboTimer > 0) {
self.comboTimer--;
} else if (self.comboCount > 0) {
// Combo expired
self.resetCombo();
}
};
self.addKill = function (unit) {
self.comboCount++;
self.comboTimer = self.maxComboTimer;
// Update combo multiplier based on count
if (self.comboCount >= 5) {
self.comboLevel = 5;
self.comboMultiplier = 3.0;
} else if (self.comboCount >= 4) {
self.comboLevel = 4;
self.comboMultiplier = 2.5;
} else if (self.comboCount >= 3) {
self.comboLevel = 3;
self.comboMultiplier = 2.0;
} else if (self.comboCount >= 2) {
self.comboLevel = 2;
self.comboMultiplier = 1.5;
} else {
self.comboLevel = 1;
self.comboMultiplier = 1.2;
}
// Track participating unit
if (self.participatingUnits.indexOf(unit) === -1) {
self.participatingUnits.push(unit);
}
// Bonus coins for combo
var bonusCoins = Math.floor(self.comboCount * 2);
coins += bonusCoins;
// Visual feedback
LK.effects.flashScreen(0xFFFF00, 200);
};
self.resetCombo = function () {
self.comboCount = 0;
self.comboLevel = 0;
self.comboMultiplier = 1.0;
self.participatingUnits = [];
self.comboTimer = 0;
};
return self;
});
var UnitLevel = Container.expand(function () {
var self = Container.call(this);
self.level = 1;
self.experience = 0;
self.experienceToNextLevel = 100;
self.bonusDamage = 0;
self.bonusRange = 0;
self.bonusSpeed = 0;
self.gainExperience = function (amount) {
self.experience += amount;
if (self.experience >= self.experienceToNextLevel) {
self.levelUp();
}
};
self.levelUp = function () {
self.level++;
self.experience = 0;
self.experienceToNextLevel = Math.floor(self.experienceToNextLevel * 1.5);
// Calculate stat bonuses based on level
self.bonusDamage = Math.floor((self.level - 1) * 5);
self.bonusRange = Math.floor((self.level - 1) * 25);
self.bonusSpeed = Math.floor((self.level - 1) * 0.1 * 10) / 10; // 0.1 increment
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFCC00 // Bright 8-bit yellow desert background
});
/****
* Game Code
****/
// Load persisted upgrades into game with validation
// Remove healButton if secret boss is defeated or game reset
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
if (storage.unitUpgrades && _typeof(storage.unitUpgrades) === 'object') {
// Validate structure exists before assignment
if (storage.unitUpgrades.archer && storage.unitUpgrades.spearman && storage.unitUpgrades.cavalry) {
unitUpgrades = storage.unitUpgrades;
}
}
if (storage.shopCoins && typeof storage.shopCoins === 'number' && storage.shopCoins >= 0) {
shopCoins = Math.floor(storage.shopCoins);
}
if ((!secretBossSpawned || gameOver || gameWon) && healButton) {
if (typeof healButton.destroy === "function") {
healButton.destroy();
}
healButton = null;
}
var gameState = 'menu'; // 'menu' or 'playing'
var menuElements = [];
// Scenario change variables
var scenarioCubes = [];
var lastCubeSpawn = 0;
var cubeSpawnInterval = 1200; // 20 seconds at 60fps
var currentScenario = 'desert'; // 'desert' or 'alternate'
var scenarioChangeActive = false;
// Nuclear Defense System variables
var nuclearDefenseActive = false;
var nuclearDefenseCubes = [];
var lastNuclearAttackTime = 0;
var nuclearAttackCooldown = 1800; // 30 seconds between nuclear attack waves
// Missile rain event variables
var missileRainActive = false;
var missileRainTimer = 0;
var missileRainInstance = null;
// Game variables
var fortress;
var defensiveUnits = [];
var enemies = [];
var projectiles = [];
var coins = 50;
var currentWave = 1;
var enemiesInWave = 5;
var enemiesSpawned = 0;
var waveDelay = 180; // 3 seconds at 60fps
var spawnDelay = 60; // 1 second between enemy spawns
var gameOver = false;
var enemiesKilled = 0;
var selectedUnitType = 'archer';
var enemiesEntered = 0; // Track enemies that entered fortress
var maxEnemiesAllowed = 10; // Game ends when 10 enemies enter
var bossSpawned = false;
var bossWave = 10; // Boss appears on wave 10
var gameWon = false;
var fortressMaxHealth = 200;
var fortressCurrentHealth = 200;
var shieldActive = false;
var shieldCooldown = 0;
var shieldMaxCooldown = 600; // 10 seconds at 60fps
var lastShieldTime = 0;
var trollfaceOverlay = null;
var trollfaceShown = false;
var lastAlarmTime = 0;
var alarmCooldown = 300; // 5 seconds at 60fps
// Combo system variables
var comboSystem = new UnitCombo();
var comboText = null;
// Secret boss tracking variables
var secretBossConditionMet = false;
var secretBossSpawned = false;
var archerCount = 0;
var spearmanCount = 0;
var cavalryCount = 0;
var enemiesKilledByTrackedUnits = 0;
var trackingActive = false;
// Second secret boss tracking variables
var secondSecretBossConditionMet = false;
var secondSecretBossSpawned = false;
var archerCount2 = 0;
var spearmanCount2 = 0;
var cavalryCount2 = 0;
var trackingActive2 = false;
// Healing button variables
var healButton = null;
var healCooldown = 0;
var healMaxCooldown = 360; // 6 seconds at 60fps
// Time Distortion variables
var timeDistortionButton = null;
var timeDistortionCooldown = 0;
var timeDistortionMaxCooldown = 1800; // 30 seconds at 60fps
var timeDistortionActive = false;
var timeDistortionInstance = null;
// Boss spawn tracking variables
var lastBossSpawnKillCount = 0;
var totalMiniBossesSpawned = 0;
var maxMiniBossesToSpawn = 20;
// Shop system variables - initialize persistent weapon upgrades
var shopState = 'menu'; // 'menu' or 'shop'
var unitUpgrades = {
archer: {
damage: 0,
power: 0
},
spearman: {
damage: 0,
power: 0
},
cavalry: {
damage: 0,
power: 0
}
};
var shopCoins = 50;
// Menu initialization function
function initMainMenu() {
gameState = 'menu';
shopState = 'menu';
// Clear any existing menu elements
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Set blue background for menu
game.setBackgroundColor(0x0000FF);
// Play menu music
LK.playMusic('Pantalladeinicio');
// Game title
var titleText = new Text2('DEFENSA DE FORTALEZA', {
size: 120,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
menuElements.push(titleText);
// Subtitle
var subtitleText = new Text2('Torre de Defensa Islámica', {
size: 80,
fill: 0xFFFF00
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 550;
game.addChild(subtitleText);
menuElements.push(subtitleText);
// Instructions
var instructionsText = new Text2('¡Defiende tu fortaleza!\nColoca unidades para detener enemigos\n¡No dejes que entren 10 enemigos!', {
size: 80,
fill: 0xFFFF00
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 900;
game.addChild(instructionsText);
menuElements.push(instructionsText);
// Shop button
var shopButton = new Text2('TIENDA', {
size: 90,
fill: 0xFFAA00
});
shopButton.anchor.set(0.5, 0.5);
shopButton.x = 600;
shopButton.y = 1300;
game.addChild(shopButton);
menuElements.push(shopButton);
shopButton.down = function () {
initShop();
};
// Start button
var startButton = new Text2('JUGAR', {
size: 90,
fill: 0xFFFF00
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1450;
startButton.y = 1300;
game.addChild(startButton);
menuElements.push(startButton);
// Animate start button with continuous twinkling effect
function startTwinkle() {
if (gameState === 'menu' && shopState === 'menu') {
tween(startButton, {
alpha: 0.3,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu' && shopState === 'menu') {
tween(startButton, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
startTwinkle();
}
});
}
}
});
}
}
startTwinkle();
startButton.down = function () {
initCinematic();
};
}
// Shop initialization function
function initShop() {
shopState = 'shop';
// Clear menu elements
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Set shop background
game.setBackgroundColor(0x1a1a2e);
// Shop title
var shopTitle = new Text2('TIENDA DE MEJORAS', {
size: 100,
fill: 0xFFFF00
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 100;
game.addChild(shopTitle);
menuElements.push(shopTitle);
// Coins display
var coinsDisplay = new Text2('Monedas: ' + shopCoins, {
size: 60,
fill: 0xFFCC00
});
coinsDisplay.anchor.set(0.5, 0.5);
coinsDisplay.x = 1024;
coinsDisplay.y = 250;
game.addChild(coinsDisplay);
menuElements.push(coinsDisplay);
// Create upgrade cards for each unit type
var yOffset = 450;
var unitTypes = ['archer', 'spearman', 'cavalry'];
var unitNames = ['Arquero', 'Lancero', 'Caballería'];
var unitColors = [0x00FF00, 0x0080FF, 0xFF00FF];
for (var u = 0; u < unitTypes.length; u++) {
var unitType = unitTypes[u];
var unitName = unitNames[u];
var unitColor = unitColors[u];
// Unit card background
var cardBg = LK.getAsset('HealthBarBg', {
width: 900,
height: 280,
anchorX: 0.5,
anchorY: 0.5
});
cardBg.tint = 0x16213e;
cardBg.x = 1024;
cardBg.y = yOffset;
game.addChild(cardBg);
menuElements.push(cardBg);
// Unit name
var nameText = new Text2(unitName, {
size: 50,
fill: unitColor
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 700;
nameText.y = yOffset - 80;
game.addChild(nameText);
menuElements.push(nameText);
// Damage upgrade section
var damageLabel = new Text2('Daño: ' + unitUpgrades[unitType].damage, {
size: 40,
fill: 0xFFFFFF
});
damageLabel.anchor.set(0.5, 0.5);
damageLabel.x = 900;
damageLabel.y = yOffset - 20;
game.addChild(damageLabel);
menuElements.push(damageLabel);
// Damage upgrade button
var damageButton = new Text2('Mejorar\n(10 monedas)', {
size: 32,
fill: 0x00FF00
});
damageButton.anchor.set(0.5, 0.5);
damageButton.x = 1100;
damageButton.y = yOffset - 20;
game.addChild(damageButton);
menuElements.push(damageButton);
(function (type, damageBtn, damageLabel, coinsDisp) {
damageBtn.down = function () {
if (gameState === 'menu' && shopState === 'shop' && shopCoins >= 10 && unitUpgrades[type] && typeof unitUpgrades[type].damage === 'number') {
shopCoins = Math.max(0, shopCoins - 10);
unitUpgrades[type].damage = Math.max(0, unitUpgrades[type].damage + 1);
coinsDisp.setText('Monedas: ' + shopCoins);
damageLabel.setText('Daño: ' + unitUpgrades[type].damage);
storage.shopCoins = shopCoins;
storage.unitUpgrades = unitUpgrades;
LK.getSound('Poder').play();
tween(damageBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(damageBtn, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
})(unitType, damageButton, damageLabel, coinsDisplay);
// Power upgrade section
var powerLabel = new Text2('Poder: ' + unitUpgrades[unitType].power, {
size: 40,
fill: 0xFFFFFF
});
powerLabel.anchor.set(0.5, 0.5);
powerLabel.x = 900;
powerLabel.y = yOffset + 60;
game.addChild(powerLabel);
menuElements.push(powerLabel);
// Power upgrade button
var powerButton = new Text2('Mejorar\n(15 monedas)', {
size: 32,
fill: 0xFF00FF
});
powerButton.anchor.set(0.5, 0.5);
powerButton.x = 1100;
powerButton.y = yOffset + 60;
game.addChild(powerButton);
menuElements.push(powerButton);
(function (type, powerBtn, powerLabel, coinsDisp) {
powerBtn.down = function () {
if (gameState === 'menu' && shopState === 'shop' && shopCoins >= 15 && unitUpgrades[type] && typeof unitUpgrades[type].power === 'number') {
shopCoins = Math.max(0, shopCoins - 15);
unitUpgrades[type].power = Math.max(0, unitUpgrades[type].power + 1);
coinsDisp.setText('Monedas: ' + shopCoins);
powerLabel.setText('Poder: ' + unitUpgrades[type].power);
storage.shopCoins = shopCoins;
storage.unitUpgrades = unitUpgrades;
LK.getSound('Poder').play();
tween(powerBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(powerBtn, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
})(unitType, powerButton, powerLabel, coinsDisplay);
yOffset += 350;
}
// Back button to return to main menu
var backButton = new Text2('VOLVER AL MENÚ', {
size: 50,
fill: 0xFF6666
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 2550;
game.addChild(backButton);
menuElements.push(backButton);
backButton.down = function () {
if (gameState === 'menu' && shopState === 'shop') {
shopState = 'menu';
initMainMenu();
}
};
}
// Cinematic initialization function
function initCinematic() {
gameState = 'cinematic';
// Clear menu elements
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Set dark background for cinematic
game.setBackgroundColor(0x000020);
// Play cinematic music
LK.playMusic('cinematica');
// Create knight character on left
var knight = LK.getAsset('cavalry', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 2.0
});
knight.x = 400;
knight.y = 1800;
game.addChild(knight);
// Create spearman character on right
var spearman = LK.getAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 2.0
});
spearman.x = 1648;
spearman.y = 1800;
game.addChild(spearman);
// Create dialogue box background
var dialogueBox = LK.getAsset('HealthBarBg', {
width: 1800,
height: 300,
anchorX: 0.5,
anchorY: 0.5
});
dialogueBox.x = 1024;
dialogueBox.y = 2200;
dialogueBox.tint = 0x222222;
game.addChild(dialogueBox);
// Create dialogue text
var dialogueText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
dialogueText.anchor.set(0.5, 0.5);
dialogueText.x = 1024;
dialogueText.y = 2200;
game.addChild(dialogueText);
// Animate characters entering from sides
tween(knight, {
x: 600
}, {
duration: 1000,
easing: tween.easeOut
});
tween(spearman, {
x: 1448
}, {
duration: 1000,
easing: tween.easeOut
});
// Start dialogue sequence after characters are in position
LK.setTimeout(function () {
// Knight speaks first
dialogueText.setText('Caballero: "Está a punto de empezar el conflicto"');
// Play dialogue sound when knight speaks
LK.getSound('dialogo').play();
tween(knight, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(knight, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// After 3 seconds, spearman responds
LK.setTimeout(function () {
dialogueText.setText('Lancero: "Sí, en guardia"');
// Play dialogue sound when spearman speaks
LK.getSound('dialogo').play();
tween(spearman, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(spearman, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// After another 2 seconds, fade out and start game
LK.setTimeout(function () {
// Fade out all cinematic elements
tween(knight, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
tween(spearman, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
tween(dialogueBox, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
tween(dialogueText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Clean up cinematic elements
knight.destroy();
spearman.destroy();
dialogueBox.destroy();
dialogueText.destroy();
// Start the actual game
initGame();
}
});
}, 2000);
}, 3000);
}, 1500);
}
// Game initialization function
function initGame() {
gameState = 'playing';
// Reset background to game color
game.setBackgroundColor(0xFFCC00);
// Clear menu elements (already cleared in cinematic)
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Reset game variables
coins = 50;
currentWave = 1;
enemiesInWave = 5;
enemiesSpawned = 0;
waveDelay = 180;
spawnDelay = 60;
gameOver = false;
enemiesKilled = 0;
selectedUnitType = 'archer';
enemiesEntered = 0;
bossSpawned = false;
gameWon = false;
fortressCurrentHealth = 200;
shieldActive = false;
shieldCooldown = 0;
trollfaceShown = false;
if (trollfaceOverlay) {
trollfaceOverlay.destroy();
trollfaceOverlay = null;
}
// Reset secret boss tracking
secretBossConditionMet = false;
secretBossSpawned = false;
archerCount = 0;
spearmanCount = 0;
cavalryCount = 0;
enemiesKilledByTrackedUnits = 0;
trackingActive = false;
// Reset boss spawn tracking
lastBossSpawnKillCount = 0;
totalMiniBossesSpawned = 0;
// Reset alarm cooldown
lastAlarmTime = 0;
// Reset combo system
comboSystem.resetCombo();
// Reset healing button
healCooldown = 0;
if (healButton) {
if (typeof healButton.destroy === "function") {
healButton.destroy();
}
healButton = null;
}
// Reset time distortion
timeDistortionCooldown = 0;
timeDistortionActive = false;
if (timeDistortionInstance) {
if (typeof timeDistortionInstance.destroy === "function") {
timeDistortionInstance.destroy();
}
timeDistortionInstance = null;
}
if (timeDistortionButton) {
if (typeof timeDistortionButton.destroy === "function") {
timeDistortionButton.destroy();
}
timeDistortionButton = null;
}
// Reset nuclear defense system
nuclearDefenseActive = false;
for (var reset_i = 0; reset_i < nuclearDefenseCubes.length; reset_i++) {
if (nuclearDefenseCubes[reset_i] && typeof nuclearDefenseCubes[reset_i].destroy === "function") {
nuclearDefenseCubes[reset_i].destroy();
}
}
nuclearDefenseCubes = [];
lastNuclearAttackTime = 0;
// Healing button will be created only when secret boss spawns
// Clear arrays
defensiveUnits = [];
enemies = [];
projectiles = [];
scenarioCubes = [];
// Reset scenario variables
currentScenario = 'desert';
scenarioChangeActive = false;
lastCubeSpawn = 0;
cubeSpawnInterval = 1200;
// Initialize fortress at center
fortress = new Fortress();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
// Start background music for gameplay
LK.playMusic('background');
}
// Scenario change function
function changeScenario() {
if (scenarioChangeActive) return;
scenarioChangeActive = true;
// Clear all existing enemies except the fortress
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
// Clear all projectiles
for (var j = projectiles.length - 1; j >= 0; j--) {
projectiles[j].destroy();
projectiles.splice(j, 1);
}
// Clear all defensive units placed by player to leave clean scenario
for (var k = defensiveUnits.length - 1; k >= 0; k--) {
var unit = defensiveUnits[k];
unit.destroy();
defensiveUnits.splice(k, 1);
}
// Change scenario
if (currentScenario === 'desert') {
currentScenario = 'alternate';
game.setBackgroundColor(0x808080); // Gray environment
// Change fortress to fortress2
fortress.destroy();
fortress = new Fortress2();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
} else {
currentScenario = 'desert';
game.setBackgroundColor(0xFFCC00); // Desert yellow
// Reset fortress to normal
fortress.destroy();
fortress = new Fortress();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
}
// Spawn only airplane in alternate scenario, strongEnemy in desert
if (currentScenario === 'alternate') {
var airplane = new Avion();
enemies.push(airplane);
game.addChild(airplane);
} else {
var singleEnemy = new Enemy('strongEnemy');
singleEnemy.x = Math.random() * 1500 + 274; // Random position not too close to edges
singleEnemy.y = Math.random() * 1000 + 200;
enemies.push(singleEnemy);
game.addChild(singleEnemy);
}
// Visual effect for scenario change
LK.effects.flashScreen(0xFFFFFF, 1000);
// Reset scenario change flag after a delay
LK.setTimeout(function () {
scenarioChangeActive = false;
}, 2000);
}
// Start with main menu
initMainMenu();
// UI Elements
var coinsText = new Text2('Monedas: ' + coins, {
size: 32,
fill: 0xFFFF00
});
coinsText.anchor.set(0, 0);
coinsText.x = 120;
coinsText.y = 50;
LK.gui.topLeft.addChild(coinsText);
var waveText = new Text2('Oleada: ' + currentWave, {
size: 32,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.x = 0;
waveText.y = 50;
LK.gui.top.addChild(waveText);
var healthText = new Text2('Fortaleza: ' + fortressCurrentHealth + '/' + fortressMaxHealth, {
size: 32,
fill: 0xFF0000
});
healthText.anchor.set(1, 0);
healthText.x = -20;
healthText.y = 50;
LK.gui.topRight.addChild(healthText);
// Add health bar
var healthBarBg = LK.getAsset('fortress', {
width: 200,
height: 20,
anchorX: 0.5,
anchorY: 0
});
healthBarBg.tint = 0x666666;
healthBarBg.x = 0;
healthBarBg.y = 90;
LK.gui.top.addChild(healthBarBg);
var healthBarFill = LK.getAsset('fortress', {
width: 200,
height: 20,
anchorX: 0,
anchorY: 0
});
healthBarFill.tint = 0x00FF00;
healthBarFill.x = -100;
healthBarFill.y = 90;
LK.gui.top.addChild(healthBarFill);
// Add combo display text
comboText = new Text2('COMBO: 0x1.0', {
size: 32,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
comboText.x = 0;
comboText.y = 130;
LK.gui.top.addChild(comboText);
// Add shield button
var shieldButton = new Text2('Escudo (Listo)', {
size: 24,
fill: 0x00FFFF
});
shieldButton.anchor.set(0.5, 1);
shieldButton.x = 0;
shieldButton.y = -100;
LK.gui.bottom.addChild(shieldButton);
shieldButton.down = function () {
fortress.activateShield();
};
var enemiesEnteredText = new Text2('Enemigos Entraron: ' + enemiesEntered + '/' + maxEnemiesAllowed, {
size: 28,
fill: 0xFFFFFF
});
enemiesEnteredText.anchor.set(0.5, 0);
enemiesEnteredText.x = 0;
enemiesEnteredText.y = 100;
LK.gui.top.addChild(enemiesEnteredText);
// Unit selection buttons
var archerButton = new Text2('Arquero (10)', {
size: 28,
fill: 0x00FF00
});
archerButton.anchor.set(0.5, 1);
archerButton.x = -200;
archerButton.y = -50;
LK.gui.bottom.addChild(archerButton);
var spearmanButton = new Text2('Lancero (15)', {
size: 28,
fill: 0x0080FF
});
spearmanButton.anchor.set(0.5, 1);
spearmanButton.x = 0;
spearmanButton.y = -50;
LK.gui.bottom.addChild(spearmanButton);
var cavalryButton = new Text2('Caballería (25)', {
size: 28,
fill: 0xFF00FF
});
cavalryButton.anchor.set(0.5, 1);
cavalryButton.x = 200;
cavalryButton.y = -50;
LK.gui.bottom.addChild(cavalryButton);
// Button press handlers
archerButton.down = function () {
selectedUnitType = 'archer';
updateButtonColors();
};
spearmanButton.down = function () {
selectedUnitType = 'spearman';
updateButtonColors();
};
cavalryButton.down = function () {
selectedUnitType = 'cavalry';
updateButtonColors();
};
function updateButtonColors() {
archerButton.fill = selectedUnitType === 'archer' ? 0xFFFFFF : 0x00FF00;
spearmanButton.fill = selectedUnitType === 'spearman' ? 0xFFFFFF : 0x0080FF;
cavalryButton.fill = selectedUnitType === 'cavalry' ? 0xFFFFFF : 0xFF00FF;
}
updateButtonColors();
// Game input
game.down = function (x, y, obj) {
// Allow buttons to work during menu state - don't block their down handlers
if (gameState === 'menu' || gameState === 'cinematic') {
// Block all game placement input during menu or cinematic, but buttons still work
return;
}
if (gameOver) return;
// Disable unit placement during nuclear Quick Time Event
if (nuclearDefenseActive) {
return;
}
var unitCost = 0;
if (selectedUnitType === 'archer') {
unitCost = 10;
} else if (selectedUnitType === 'spearman') {
unitCost = 15;
} else if (selectedUnitType === 'cavalry') {
unitCost = 25;
} else {
return; // Invalid unit type
}
if (coins >= unitCost && unitCost > 0) {
var newUnit = new DefensiveUnit(selectedUnitType);
newUnit.x = x;
newUnit.y = y;
defensiveUnits.push(newUnit);
game.addChild(newUnit);
coins = Math.max(0, coins - unitCost);
LK.getSound('deploy').play();
// Track unit counts for secret boss condition
if (selectedUnitType === 'archer') {
archerCount++;
archerCount2++;
} else if (selectedUnitType === 'spearman') {
spearmanCount++;
spearmanCount2++;
} else if (selectedUnitType === 'cavalry') {
cavalryCount++;
cavalryCount2++;
}
// Check if we have exactly 3 archers and 1 spearman with no cavalry (first condition)
if (archerCount === 3 && spearmanCount === 1 && cavalryCount === 0 && !trackingActive && !secretBossSpawned) {
trackingActive = true;
enemiesKilledByTrackedUnits = 0;
}
// Check if we have exactly 1 archer and 3 spearmen with no cavalry (second condition)
if (archerCount2 === 1 && spearmanCount2 === 3 && cavalryCount2 === 0 && !trackingActive2 && !secondSecretBossSpawned) {
trackingActive2 = true;
}
// If we deploy any other units or wrong counts, disable tracking for first condition
if (trackingActive && (archerCount !== 3 || spearmanCount !== 1 || cavalryCount > 0)) {
trackingActive = false;
enemiesKilledByTrackedUnits = 0; // Reset kill count when tracking stops
}
// If we deploy any other units or wrong counts, disable tracking for second condition
if (trackingActive2 && (archerCount2 !== 1 || spearmanCount2 !== 3 || cavalryCount2 > 0)) {
trackingActive2 = false;
}
}
};
// Spawn enemies
function spawnEnemy() {
var enemy;
// Spawn boss on wave 10
if (currentWave >= bossWave && !bossSpawned) {
enemy = new Boss();
bossSpawned = true;
} else {
var enemyType = currentWave > 3 && Math.random() < 0.3 ? 'strongEnemy' : 'enemy';
enemy = new Enemy(enemyType);
}
// Spawn from random edge, avoiding fortress area
var side = Math.floor(Math.random() * 4);
if (side === 0) {
// Top - avoid fortress center area
enemy.x = Math.random() < 0.5 ? Math.random() * 700 : Math.random() * 700 + 1348;
enemy.y = -50;
} else if (side === 1) {
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
} else if (side === 2) {
// Bottom - avoid fortress center area
enemy.x = Math.random() < 0.5 ? Math.random() * 700 : Math.random() * 700 + 1348;
enemy.y = 2782;
} else {
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
}
// Initialize spawn time for auto-disappear functionality
enemy.spawnTime = LK.ticks;
enemies.push(enemy);
game.addChild(enemy);
}
game.update = function () {
// Don't run game logic when in menu or cinematic
if (gameState === 'menu' || gameState === 'cinematic') {
return;
}
// Check win condition - boss defeated
if (gameWon) {
LK.showYouWin();
return;
}
// Check lose conditions
if (gameOver || enemiesEntered >= maxEnemiesAllowed) {
LK.showGameOver();
return;
}
// Update shield cooldown
if (shieldCooldown > 0) {
shieldCooldown--;
}
// Update heal cooldown and button appearance
if (healCooldown > 0) {
healCooldown--;
}
// Update time distortion cooldown
if (timeDistortionCooldown > 0) {
timeDistortionCooldown--;
}
// Create Time Distortion button when player has 5+ archer units and at least wave 3
var archerUnitsCount = 0;
for (var archer_i = 0; archer_i < defensiveUnits.length; archer_i++) {
if (defensiveUnits[archer_i].unitType === 'archer') {
archerUnitsCount++;
}
}
if (archerUnitsCount >= 5 && currentWave >= 3 && !timeDistortionButton) {
timeDistortionButton = new Text2('DISTORSIÓN TEMPORAL', {
size: 20,
fill: 0x00FFFF
});
timeDistortionButton.anchor.set(0.5, 1);
timeDistortionButton.x = -200;
timeDistortionButton.y = -150;
LK.gui.bottom.addChild(timeDistortionButton);
timeDistortionButton.down = function () {
if (timeDistortionCooldown <= 0 && !timeDistortionActive) {
// Activate time distortion at fortress location
timeDistortionActive = true;
timeDistortionCooldown = timeDistortionMaxCooldown;
timeDistortionInstance = new TimeDistortion();
timeDistortionInstance.x = fortress.x;
timeDistortionInstance.y = fortress.y;
game.addChild(timeDistortionInstance);
// Visual and audio feedback
LK.effects.flashScreen(0x00FFFF, 500);
LK.getSound('Poder').play();
}
};
}
// Update time distortion button appearance
if (timeDistortionButton) {
if (timeDistortionCooldown > 0) {
var secondsLeft = Math.ceil(timeDistortionCooldown / 60);
timeDistortionButton.setText('DISTORSIÓN (' + secondsLeft + 's)');
timeDistortionButton.fill = 0x666666;
} else if (timeDistortionActive) {
timeDistortionButton.setText('DISTORSIÓN ACTIVA');
timeDistortionButton.fill = 0x00FF00;
} else {
timeDistortionButton.setText('DISTORSIÓN TEMPORAL');
timeDistortionButton.fill = 0x00FFFF;
}
}
// Update time distortion instance
if (timeDistortionActive && timeDistortionInstance) {
timeDistortionInstance.update();
if (!timeDistortionInstance.active) {
timeDistortionActive = false;
if (typeof timeDistortionInstance.destroy === "function") {
timeDistortionInstance.destroy();
}
timeDistortionInstance = null;
}
}
// Remove time distortion button if archer count drops below 5
if (timeDistortionButton && archerUnitsCount < 5) {
timeDistortionButton.destroy();
timeDistortionButton = null;
}
// Update healing button if it exists (only when secret boss is active)
if (healButton && secretBossSpawned) {
if (healCooldown > 0) {
var secondsLeft = Math.ceil(healCooldown / 60);
if (typeof healButton.setText === "function") {
healButton.setText('CURAR (' + secondsLeft + 's)');
healButton.fill = 0x666666;
} else if (healButton.healText && typeof healButton.healText.setText === "function") {
healButton.healText.setText('CURAR (' + secondsLeft + 's)');
healButton.healText.fill = 0x666666;
}
} else {
if (typeof healButton.setText === "function") {
healButton.setText('CURAR');
healButton.fill = 0xFFFFFF;
} else if (healButton.healText && typeof healButton.healText.setText === "function") {
healButton.healText.setText('CURAR');
healButton.healText.fill = 0xFFFFFF;
}
}
}
// Remove healButton if secret boss is defeated or game is reset
if ((!secretBossSpawned || gameOver || gameWon) && healButton) {
healButton.destroy();
healButton = null;
}
// Update combo system
comboSystem.update();
// Update UI
coinsText.setText('Monedas: ' + coins);
waveText.setText('Oleada: ' + currentWave);
healthText.setText('Fortaleza: ' + fortressCurrentHealth + '/' + fortressMaxHealth);
enemiesEnteredText.setText('Enemigos Entraron: ' + enemiesEntered + '/' + maxEnemiesAllowed);
LK.setScore(enemiesKilled);
// Update combo display
if (comboText) {
if (comboSystem.comboLevel > 0) {
comboText.setText('COMBO: ' + comboSystem.comboCount + 'x' + comboSystem.comboMultiplier.toFixed(1));
// Color intensity increases with combo level
var comboColors = [0xFFFF00, 0xFFCC00, 0xFF9900, 0xFF6600, 0xFF3300, 0xFF0000];
comboText.fill = comboColors[Math.min(comboSystem.comboLevel - 1, 5)];
// Scale effect based on combo
tween(comboText, {
scaleX: 1.0 + comboSystem.comboLevel * 0.1,
scaleY: 1.0 + comboSystem.comboLevel * 0.1
}, {
duration: 100,
easing: tween.easeOut
});
} else {
comboText.setText('COMBO: 0x1.0');
comboText.fill = 0xFFFF00;
comboText.scaleX = 1.0;
comboText.scaleY = 1.0;
}
}
// Update health bar
var healthPercent = fortressCurrentHealth / fortressMaxHealth;
healthBarFill.width = 200 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Update shield button
if (shieldCooldown > 0) {
var secondsLeft = Math.ceil(shieldCooldown / 60);
shieldButton.setText('Escudo (' + secondsLeft + 's)');
shieldButton.fill = 0x666666;
} else {
shieldButton.setText('Escudo (Listo)');
shieldButton.fill = 0x00FFFF;
}
// Spawn enemies for current wave - only if in desert scenario
if (currentScenario === 'desert' && enemiesSpawned < enemiesInWave && LK.ticks % spawnDelay === 0) {
spawnEnemy();
enemiesSpawned++;
}
// Check if wave is complete
if (enemiesSpawned >= enemiesInWave && enemies.length === 0) {
if (waveDelay > 0) {
waveDelay--;
} else {
// Start next wave
currentWave++;
enemiesInWave = Math.min(5 + currentWave * 2, 20);
enemiesSpawned = 0;
waveDelay = 180;
spawnDelay = Math.max(30, 60 - currentWave * 2);
}
}
// Update defensive units
for (var i = 0; i < defensiveUnits.length; i++) {
defensiveUnits[i].update();
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Auto-disappear enemies after they've been alive for too long (except bosses)
if (!enemy.isBoss && !enemy.isMiniBoss && !enemy.isSecretBoss) {
// Initialize spawn time if not set
if (enemy.spawnTime === undefined) {
enemy.spawnTime = LK.ticks;
}
// Auto-disappear after 30 seconds (1800 ticks at 60fps)
if (LK.ticks - enemy.spawnTime > 1800) {
enemy.markForDestroy = true;
}
}
if (enemy.markForDestroy) {
enemy.destroy();
enemies.splice(i, 1);
}
}
// Check if we're in boss mode (any boss or mini boss exists)
var inBossMode = false;
for (var j = 0; j < enemies.length; j++) {
if (enemies[j].isBoss || enemies[j].isMiniBoss || enemies[j].isSecretBoss) {
inBossMode = true;
break;
}
}
// Check for alerta warning when 5 enemies enter fortress (only if not in boss mode)
if (enemiesEntered === 5 && LK.ticks - lastAlarmTime >= alarmCooldown && !inBossMode) {
lastAlarmTime = LK.ticks;
// Play danger sound effect
LK.getSound('alerta').play();
// Create alerta overlay that appears quickly and fades out - same size as trollface
var alertaOverlay = LK.getAsset('Alerta', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 15
});
alertaOverlay.x = 1024;
alertaOverlay.y = 1366;
game.addChild(alertaOverlay);
// Quick flash effect - instantly visible then fade out
alertaOverlay.alpha = 1.0;
tween(alertaOverlay, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
alertaOverlay.destroy();
}
});
}
// Check for trollface trigger every 10 enemies killed (10, 20, 30, etc.) (only if not in boss mode)
if (enemiesKilled > 0 && enemiesKilled % 10 === 0 && !trollfaceShown && !inBossMode) {
trollfaceShown = true;
// Create trollface overlay that starts small and expands
trollfaceOverlay = LK.getAsset('Trollface', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
trollfaceOverlay.x = 1024;
trollfaceOverlay.y = 1366;
game.addChild(trollfaceOverlay);
// Play trollface sound effect when appearing
LK.getSound('trollface').play();
// Tornado expanding effect when appearing
tween(trollfaceOverlay, {
scaleX: 20,
scaleY: 15,
rotation: Math.PI * 2
}, {
duration: 1000,
easing: tween.easeOut
});
// Show trollface for 1 second to match shorter sound, then animate it away
LK.setTimeout(function () {
if (trollfaceOverlay) {
// Play trollface sound effect when disappearing
LK.getSound('trollface').play();
// Swirling and shrinking animation - shorter to match sound
tween(trollfaceOverlay, {
rotation: Math.PI * 4,
// 2 full rotations for faster animation
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (trollfaceOverlay) {
trollfaceOverlay.destroy();
trollfaceOverlay = null;
}
}
});
}
}, 1000);
}
// Check for boss spawn every 10 enemies killed
if (enemiesKilled >= lastBossSpawnKillCount + 10) {
lastBossSpawnKillCount = enemiesKilled;
// Spawn main boss only once (first time reaching 10 kills) and only if not already spawned
if (enemiesKilled === 10 && !bossSpawned) {
var mainBoss = new Boss();
mainBoss.x = 1024; // Center top
mainBoss.y = -100;
// Start with dramatic entrance - completely invisible and small
mainBoss.scaleX = 0.0;
mainBoss.scaleY = 0.0;
mainBoss.alpha = 0.0;
enemies.push(mainBoss);
game.addChild(mainBoss);
bossSpawned = true; // Mark boss as spawned
// ============================================================================
// COMPREHENSIVE BOSS ENTRANCE ANIMATION SYSTEM
// This section creates an epic, multi-layered entrance sequence for the ogre boss
// featuring dramatic visual effects, sound design, and choreographed animations
// that build tension and create an memorable boss encounter experience.
// ============================================================================
// PHASE 1: DRAMATIC WARNING AND ENVIRONMENT PREPARATION
// Create ominous atmosphere with dark screen overlay and warning text
LK.effects.flashScreen(0x000000, 800); // Extended dark flash for dramatic buildup
var warningText = new Text2('¡EL JEFE OGRO SE ACERCA!', {
size: 120,
fill: 0xFF0000
});
warningText.anchor.set(0.5, 0.5);
warningText.x = 1024;
warningText.y = 1366;
warningText.alpha = 0;
game.addChild(warningText);
// Dramatic warning text entrance with pulsing intensity animation
tween(warningText, {
alpha: 1.0,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Secondary pulse for emphasis
tween(warningText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Third pulse with red flash
LK.effects.flashObject(warningText, 0xFF0000, 500);
}
});
}
});
// PHASE 2: ENVIRONMENTAL DISTURBANCE AND MUSIC TRANSITION
LK.setTimeout(function () {
// Warning text dramatic exit with shrinking and fading
tween(warningText, {
alpha: 0,
scaleX: 0,
scaleY: 0,
rotation: Math.PI
}, {
duration: 600,
easing: tween.easeIn,
onFinish: function onFinish() {
warningText.destroy();
}
});
// Dramatic boss music entrance with fade-in crescendo
LK.playMusic('Bossogro', {
fade: {
start: 0,
end: 1,
duration: 1200
}
});
// PHASE 3: GROUND TREMORS AND SEISMIC ACTIVITY SIMULATION
LK.setTimeout(function () {
// Intensifying ground shake sequence with multiple impact waves
for (var shake = 0; shake < 5; shake++) {
LK.setTimeout(function () {
// Varied screen flash colors for realistic ground disturbance
var shakeColors = [0x8B4513, 0x654321, 0x3E2723, 0x5D4037];
LK.effects.flashScreen(shakeColors[Math.floor(Math.random() * shakeColors.length)], 250);
// Multiple ground impact explosion effects with randomized positioning
for (var impact = 0; impact < 7; impact++) {
var groundShake = new Explosion(300 + Math.random() * 1448,
// Wider spread across entire screen
fortress.y + (Math.random() - 0.5) * 400 // Varied vertical positioning around fortress
);
// Vary explosion sizes for realistic seismic effect
tween(groundShake, {
scaleX: 0.8 + Math.random() * 1.4,
scaleY: 0.8 + Math.random() * 1.4
}, {
duration: 400,
easing: tween.easeOut
});
game.addChild(groundShake);
}
}, shake * 200); // Faster succession for building intensity
}
// PHASE 4: EPIC BOSS MATERIALIZATION SEQUENCE
LK.setTimeout(function () {
// Boss visibility activation with mystical appearance effect
mainBoss.alpha = 1.0;
// MULTI-STAGE DRAMATIC ENTRANCE CHOREOGRAPHY
// Stage 1: Emergence from underground with elastic bounce
tween(mainBoss, {
scaleX: 2.2,
scaleY: 2.2,
y: mainBoss.y + 80,
rotation: 0.1
}, {
duration: 1500,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Stage 2: Intimidation roar with massive size increase
tween(mainBoss, {
scaleX: 3.2,
scaleY: 3.2,
rotation: -0.1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Stage 3: Final settling with authoritative presence
tween(mainBoss, {
scaleX: 2.0,
scaleY: 2.0,
rotation: 0,
y: mainBoss.y - 30
}, {
duration: 800,
easing: tween.bounceOut
});
// Create intimidation aura effect around boss
for (var aura = 0; aura < 6; aura++) {
var auraRing = new Explosion(mainBoss.x, mainBoss.y);
tween(auraRing, {
scaleX: 2.5 + aura * 0.5,
scaleY: 2.5 + aura * 0.5,
alpha: 0.3
}, {
duration: 1000 + aura * 200,
easing: tween.easeOut
});
game.addChild(auraRing);
}
}
});
}
});
// PHASE 5: ENVIRONMENTAL DEVASTATION EFFECTS
// Massive ground impact at boss epicenter
var massiveImpact = new Explosion(mainBoss.x, mainBoss.y + 200);
tween(massiveImpact, {
scaleX: 5.0,
scaleY: 5.0
}, {
duration: 1000,
easing: tween.easeOut
});
game.addChild(massiveImpact);
// PHASE 6: CHROMATIC LIGHT SHOW AND SCREEN EFFECTS
// Cascading color explosion sequence
var flashColors = [0xFF0000, 0xFF4500, 0xFFAA00, 0xFF8800, 0xFF6600];
for (var colorFlash = 0; colorFlash < flashColors.length; colorFlash++) {
(function (color, delay) {
LK.setTimeout(function () {
LK.effects.flashScreen(color, 600);
}, delay);
})(flashColors[colorFlash], colorFlash * 300);
}
// PHASE 7: DEBRIS FIELD AND ATMOSPHERIC PARTICLES
// Extended debris cloud system with varied timing
for (var debris = 0; debris < 12; debris++) {
LK.setTimeout(function () {
var debrisEffect = new Explosion(mainBoss.x + (Math.random() - 0.5) * 600,
// Wider debris field
mainBoss.y + Math.random() * 400);
// Randomized debris characteristics
tween(debrisEffect, {
scaleX: 0.5 + Math.random() * 1.5,
scaleY: 0.5 + Math.random() * 1.5,
rotation: Math.random() * Math.PI * 2
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut
});
game.addChild(debrisEffect);
}, debris * 80 + Math.random() * 120); // Staggered timing with randomization
}
// PHASE 8: AUDIO CLIMAX AND FINAL PRESENCE ESTABLISHMENT
// Boss entrance roar with echo effect simulation
LK.getSound('jefesecreto').play();
LK.setTimeout(function () {
// Secondary roar for echo effect
LK.getSound('jefesecreto').play();
}, 400);
}, 1200); // Synchronized timing with ground tremors
}, 1000); // Coordinated with music transition
}, 1400); // Allows warning text full dramatic impact
// ============================================================================
// END OF COMPREHENSIVE BOSS ENTRANCE ANIMATION SYSTEM
// ============================================================================
}
// Only spawn mini bosses if we haven't reached the limit of 20
if (totalMiniBossesSpawned < maxMiniBossesToSpawn) {
// Spawn horde of mini bosses (4 mini bosses each time)
// Spawn mini boss from top left
var miniBoss1 = new MiniBoss();
miniBoss1.x = 200;
miniBoss1.y = -50;
enemies.push(miniBoss1);
game.addChild(miniBoss1);
totalMiniBossesSpawned++;
// Spawn mini boss from top right
var miniBoss2 = new MiniBoss2();
miniBoss2.x = 1800;
miniBoss2.y = -50;
enemies.push(miniBoss2);
game.addChild(miniBoss2);
totalMiniBossesSpawned++;
// Spawn mini boss from left side
var miniBoss3 = new MiniBoss();
miniBoss3.x = -50;
miniBoss3.y = Math.random() * 1000 + 500;
enemies.push(miniBoss3);
game.addChild(miniBoss3);
totalMiniBossesSpawned++;
// Spawn mini boss from right side
var miniBoss4 = new MiniBoss2();
miniBoss4.x = 2098;
miniBoss4.y = Math.random() * 1000 + 500;
enemies.push(miniBoss4);
game.addChild(miniBoss4);
totalMiniBossesSpawned++;
// Play special sound effect
LK.getSound('jefesecreto').play();
// Visual effect for boss appearance
LK.effects.flashScreen(0xFF8800, 800);
}
}
// Check for second secret boss spawn condition (3 spearmen + 1 archer)
if (secondSecretBossConditionMet && !secondSecretBossSpawned) {
secondSecretBossSpawned = true;
trackingActive2 = false; // Stop tracking once boss spawns
// Clear all existing enemies when secret boss appears
for (var j = enemies.length - 1; j >= 0; j--) {
var existingEnemy = enemies[j];
existingEnemy.destroy();
enemies.splice(j, 1);
}
// Create 3 spearmen and 1 archer as the second secret boss encounter
// Spearman 1
var spearman1 = new Enemy('strongEnemy');
spearman1.x = 500;
spearman1.y = -100;
enemies.push(spearman1);
game.addChild(spearman1);
// Spearman 2
var spearman2 = new Enemy('strongEnemy');
spearman2.x = 1024;
spearman2.y = -100;
enemies.push(spearman2);
game.addChild(spearman2);
// Spearman 3
var spearman3 = new Enemy('strongEnemy');
spearman3.x = 1548;
spearman3.y = -100;
enemies.push(spearman3);
game.addChild(spearman3);
// Archer (positioned slightly back)
var archerBoss = new Enemy('strongEnemy');
archerBoss.x = 1024;
archerBoss.y = -250;
enemies.push(archerBoss);
game.addChild(archerBoss);
// Play special entrance effects
LK.effects.flashScreen(0x00FF00, 1000);
LK.getSound('jefesecreto').play();
// Visual effects on all boss units
for (var boss_i = 0; boss_i < 4; boss_i++) {
(function (enemyUnit, index) {
LK.setTimeout(function () {
tween(enemyUnit, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyUnit, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(enemyUnit, 0x00FF00, 300);
}, index * 150);
})([spearman1, spearman2, spearman3, archerBoss][boss_i], boss_i);
}
}
// Check for secret boss spawn condition
if (secretBossConditionMet && !secretBossSpawned) {
// Function to create one zigzag segment
var _performZigzag = function performZigzag() {
if (zigzagCount >= maxZigzags) {
// Final thrust attack
tween(attackCharacter, {
x: bossTargetX,
y: bossTargetY - 50
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
// Thrust impact effect
LK.effects.flashObject(secretBoss, 0xFFFFFF, 300);
LK.getSound('Espada').play();
// Shrinking animation for character after attack
tween(attackCharacter, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
attackCharacter.destroy();
}
});
}
});
return;
}
// Calculate zigzag offset
var progressRatio = zigzagCount / maxZigzags;
var baseX = zigzagStartX + (bossTargetX - zigzagStartX) * progressRatio;
var baseY = zigzagStartY + (bossTargetY - zigzagStartY) * progressRatio;
var zigzagOffset = (zigzagCount % 2 === 0 ? 1 : -1) * 150;
// Move to zigzag position
tween(attackCharacter, {
x: baseX + zigzagOffset,
y: baseY
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
zigzagCount++;
_performZigzag();
}
});
};
secretBossSpawned = true;
trackingActive = false; // Stop tracking once boss spawns
// Clear all existing enemies when secret boss appears
for (var j = enemies.length - 1; j >= 0; j--) {
var existingEnemy = enemies[j];
existingEnemy.destroy();
enemies.splice(j, 1);
}
// Spawn secret boss
var secretBoss = new SecretBoss();
// Spawn from a dramatic location (top center)
secretBoss.x = 1024;
secretBoss.y = -100;
enemies.push(secretBoss);
game.addChild(secretBoss);
// Create character for zigzag attack animation
var attackCharacter = LK.getAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
scaleY: 1.5
});
attackCharacter.x = fortress.x;
attackCharacter.y = fortress.y;
game.addChild(attackCharacter);
// Zigzag attack animation sequence
var zigzagStartX = fortress.x;
var zigzagStartY = fortress.y;
var bossTargetX = secretBoss.x;
var bossTargetY = secretBoss.y + 100;
var totalDistance = Math.sqrt(Math.pow(bossTargetX - zigzagStartX, 2) + Math.pow(bossTargetY - zigzagStartY, 2));
var zigzagCount = 0;
var maxZigzags = 6;
_performZigzag();
// Create healing button when secret boss appears - same design as shield button
healButton = new Text2('CURAR (Listo)', {
size: 24,
fill: 0x00FF00
});
healButton.anchor.set(0.5, 1);
healButton.x = 200; // Position next to shield button
healButton.y = -100;
LK.gui.bottom.addChild(healButton);
// Add healing button functionality
healButton.down = function () {
if (healCooldown <= 0) {
// Heal fortress for 11 points
var healAmount = 11;
fortressCurrentHealth = Math.min(fortressCurrentHealth + healAmount, fortressMaxHealth);
fortress.health = fortressCurrentHealth;
// Set cooldown
healCooldown = healMaxCooldown;
// Play heal sound
LK.getSound('heal').play();
// Visual effect on fortress
LK.effects.flashObject(fortress, 0x00FF00, 800);
// Visual effect on button
tween(healButton, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(healButton, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
// Play special sound effect
LK.getSound('jefesecreto').play();
// Start secret boss music
LK.playMusic('Secretboos');
// Visual effect for secret boss appearance
LK.effects.flashScreen(0xFF00FF, 1000);
}
// Spawn scenario cube every 20 seconds
if (LK.ticks - lastCubeSpawn >= cubeSpawnInterval && !scenarioChangeActive) {
lastCubeSpawn = LK.ticks;
cubeSpawnInterval = 1200; // 20 seconds at 60fps
var cube = new ScenarioCube();
// Spawn in safe area away from fortress and away from top left UI
var spawnX,
spawnY,
attempts = 0;
do {
spawnX = Math.random() * 1500 + 274;
spawnY = Math.random() * 1500 + 300;
attempts++;
// Avoid top left 0-200 x 0-200 area (UI), and avoid fortress
} while ((Math.sqrt(Math.pow(spawnX - fortress.x, 2) + Math.pow(spawnY - fortress.y, 2)) < 300 || spawnX < 200 && spawnY < 200) && attempts < 20);
cube.x = spawnX;
cube.y = spawnY;
scenarioCubes.push(cube);
game.addChild(cube);
}
// Update scenario cubes
for (var i = scenarioCubes.length - 1; i >= 0; i--) {
var cube = scenarioCubes[i];
cube.update();
if (cube.markForDestroy) {
cube.destroy();
scenarioCubes.splice(i, 1);
}
}
// Spawn airplane occasionally in alternate scenario only - only if no airplane exists
if (currentScenario === 'alternate') {
var airplaneExists = false;
for (var k = 0; k < enemies.length; k++) {
if (enemies[k].speed !== undefined && enemies[k].direction !== undefined) {
airplaneExists = true;
break;
}
}
if (!airplaneExists && LK.ticks % (Math.floor(Math.random() * 300) + 300) === 0) {
var airplane = new Avion();
game.addChild(airplane);
// Store airplanes in enemies array for easy cleanup
enemies.push(airplane);
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
projectile.update();
if (projectile.markForDestroy) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
// Nuclear Defense System and Missile rain variables are now defined at the top of Game Code section
// Trigger Nuclear Defense System (every 30 seconds after wave 5, not during boss fights)
if (!nuclearDefenseActive && currentWave >= 5 && !inBossMode && !secretBossSpawned && LK.ticks - lastNuclearAttackTime >= nuclearAttackCooldown && gameState === 'playing') {
lastNuclearAttackTime = LK.ticks;
nuclearDefenseActive = true;
// Clear any existing cubes
for (var ndc_i = nuclearDefenseCubes.length - 1; ndc_i >= 0; ndc_i--) {
if (nuclearDefenseCubes[ndc_i] && typeof nuclearDefenseCubes[ndc_i].destroy === "function") {
nuclearDefenseCubes[ndc_i].destroy();
}
nuclearDefenseCubes.splice(ndc_i, 1);
}
// Spawn 4 nuclear defense cubes in different corners/areas
var cubePositions = [{
x: 300,
y: 300
},
// Top left area
{
x: 1748,
y: 300
},
// Top right area
{
x: 300,
y: 2000
},
// Bottom left area
{
x: 1748,
y: 2000
} // Bottom right area
];
for (var pos_i = 0; pos_i < cubePositions.length; pos_i++) {
var nuclearCube = new NuclearDefenseCube();
nuclearCube.x = cubePositions[pos_i].x;
nuclearCube.y = cubePositions[pos_i].y;
nuclearDefenseCubes.push(nuclearCube);
game.addChild(nuclearCube);
}
// Play warning sound
LK.getSound('alerta').play();
// Show warning message
var warningText = new Text2('¡ALERTA NUCLEAR! ¡TOCA LOS CUADROS!', {
size: 80,
fill: 0xFF0000
});
warningText.anchor.set(0.5, 0.5);
warningText.x = 1024;
warningText.y = 200;
game.addChild(warningText);
// Animate warning text
tween(warningText, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
warningText.destroy();
}
});
}
// Update nuclear defense cubes
for (var ndc_j = nuclearDefenseCubes.length - 1; ndc_j >= 0; ndc_j--) {
var nuclearCube = nuclearDefenseCubes[ndc_j];
if (nuclearCube) {
nuclearCube.update();
if (nuclearCube.markForDestroy) {
nuclearCube.destroy();
nuclearDefenseCubes.splice(ndc_j, 1);
}
}
}
// Check if all cubes are cleared to end nuclear defense phase
if (nuclearDefenseActive && nuclearDefenseCubes.length === 0) {
nuclearDefenseActive = false;
}
// Randomly trigger missile rain (1/1200 chance per frame, not during boss/secret boss)
if (!missileRainActive && !inBossMode && !secretBossSpawned && Math.random() < 1 / 1200 && gameState === 'playing') {
missileRainActive = true;
missileRainTimer = 0;
if (missileRainInstance && typeof missileRainInstance.destroy === "function") {
missileRainInstance.destroy();
}
missileRainInstance = new MissileRain();
game.addChild(missileRainInstance);
}
// Update missile rain if active
if (missileRainActive && missileRainInstance) {
missileRainInstance.update();
if (!missileRainInstance.active) {
missileRainActive = false;
if (typeof missileRainInstance.destroy === "function") {
missileRainInstance.destroy();
}
missileRainInstance = null;
}
}
// Update explosions - find and update all explosion objects
var allChildren = game.children.slice();
for (var i = allChildren.length - 1; i >= 0; i--) {
var child = allChildren[i];
if (child.markForDestroy) {
child.destroy();
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
unitUpgrades: {
archer: {
damage: 0,
power: 0
},
spearman: {
damage: 0,
power: 0
},
cavalry: {
damage: 0,
power: 0
}
},
shopCoins: 50
});
/****
* Classes
****/
var Avion = Container.expand(function () {
var self = Container.call(this);
var avionGraphics = self.attachAsset('avion', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Flight properties
self.speed = 3;
self.altitude = Math.random() * 200 + 100; // Random altitude
self.direction = Math.random() < 0.5 ? 1 : -1; // Random direction
self.bobAmount = 0;
self.bobSpeed = 0.05;
// Set initial position based on direction
if (self.direction > 0) {
self.x = -250; // Start from left
} else {
self.x = 2298; // Start from right
}
self.y = Math.random() * 800 + 200;
// Flip airplane if moving right to left
if (self.direction < 0) {
avionGraphics.scaleX = -1.5;
}
self.update = function () {
// Move directly toward fortress instead of horizontally
if (fortress) {
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 250) {
// Move toward fortress
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
// Add bobbing motion for realistic flight
self.bobAmount += self.bobSpeed;
self.y += Math.sin(self.bobAmount) * 2;
// Slight rotation for banking effect
avionGraphics.rotation = Math.sin(self.bobAmount) * 0.1;
// Check collision with fortress
if (fortress) {
var fortressDist = Math.sqrt(Math.pow(fortress.x - self.x, 2) + Math.pow(fortress.y - self.y, 2));
if (fortressDist < 250) {
// Direct collision without cinematic
var explosion = new Explosion(self.x, self.y);
game.addChild(explosion);
// Play explosion sound
LK.getSound('explosion').play();
// Airplane crashed into fortress - hide all non-alternate enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Hide enemies that are not airplanes (check if enemy doesn't have airplane properties)
if (enemy.direction === undefined && enemy.speed === undefined) {
enemy.visible = false;
}
}
// Stop enemy spawning by setting current scenario permanently
currentScenario = 'alternate';
// Airplane crashed into fortress - game over
LK.effects.flashObject(fortress, 0xFF0000, 1000);
LK.effects.flashScreen(0xFF0000, 1000);
gameOver = true;
self.markForDestroy = true;
// Show game over immediately
LK.showGameOver();
return;
}
}
// Remove when off screen
if (self.x < -300 || self.x > 2348) {
self.markForDestroy = true;
}
};
return self;
});
var BigPoderProjectile = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 6; // Slower than regular Poder
// Calculate direction to target
var dx = target.x - startX;
var dy = target.y - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
// Set rotation to face target
projectileGraphics.rotation = Math.atan2(dy, dx);
// Start with growing animation
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 500,
easing: tween.easeOut
});
// Add pulsing effect
var spinSpeed = 0.08;
self.pulseDirection = 1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Spin and pulse the projectile
projectileGraphics.rotation += spinSpeed;
// Pulsing scale effect
if (self.scaleX >= 1.8) {
self.pulseDirection = -1;
} else if (self.scaleX <= 1.2) {
self.pulseDirection = 1;
}
self.scaleX += self.pulseDirection * 0.02;
self.scaleY += self.pulseDirection * 0.02;
// Check if hit fortress (secret boss attacks fortress directly)
if (fortress && fortress.takeDamage) {
var fortressDist = Math.sqrt(Math.pow(fortress.x - self.x, 2) + Math.pow(fortress.y - self.y, 2));
if (fortressDist < 250) {
// Ensure damage is exactly 2 points for reduced boss attack power
fortress.takeDamage(2);
// Big explosion effect
LK.effects.flashObject(fortress, 0x00FFFF, 800);
// Shrinking effect on hit
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
return;
}
}
// Remove if off screen
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.markForDestroy = true;
}
};
return self;
});
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('Bossogro', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
// Make boss twice as big
scaleY: 2.0
});
// Add boss details
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
eyesAsset.x = 0;
eyesAsset.y = -240; // Adjusted for larger size
eyesAsset.tint = 0xFF0000; // Red eyes for boss
// Add crown/spikes
var crownAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.6,
scaleY: 1.2
});
crownAsset.x = 0;
crownAsset.y = -320; // Adjusted for larger size
crownAsset.tint = 0xFFD700; // Gold crown
self.health = 500; // Same health as secret boss
self.maxHealth = 500;
self.speed = 0.5; // Slower than regular enemies
self.damage = 30; // More damage than regular enemies
self.coinValue = 50; // Lots of coins when defeated
self.enemyType = 'boss';
self.isBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 300; // 5 seconds at 60fps
self.update = function () {
// Add intimidating pulsing aura effect
if (LK.ticks % 120 === 0) {
// Every 2 seconds
LK.effects.flashObject(self, 0xFF4500, 300); // Orange intimidation flash
// Slight scale pulse for intimidation
tween(self, {
scaleX: 2.1,
scaleY: 2.1
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 150,
easing: tween.easeIn
});
}
});
}
// Enemy special power - speed boost when in range of units
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 300) {
// Within range of a unit
self.lastSpecialPower = LK.ticks;
// Temporary speed boost and visual effect
var originalSpeed = self.speed;
self.speed *= 2.5; // 2.5x speed boost
LK.effects.flashObject(self, 0xFF8800, 300); // Orange flash
// Special attack when in range but not at fortress yet
// Speed boost animation
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
easing: tween.easeOut
});
// Return to normal after 1 second
LK.setTimeout(function () {
self.speed = originalSpeed;
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 400 && distance > 215 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Boss special power - weakens fortress
LK.effects.flashObject(self, 0xFF00FF, 500);
fortress.takeDamage(15); // Special attack damage
tween(self, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut
});
tween(self, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
if (distance > 300) {
// Not at fortress yet - move closer
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Boss doesn't reach fortress - just damages it from distance and disappears
fortress.takeDamage(5);
enemiesEntered++; // Count boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when boss dies to allow repeat triggers
trollfaceShown = false;
gameWon = true; // Win when boss is defeated!
self.markForDestroy = true;
}
};
return self;
});
var DefensiveUnit = Container.expand(function (unitType) {
var self = Container.call(this);
var unitGraphics = self.attachAsset(unitType, {
anchorX: 0.5,
anchorY: 1.0
});
// Add character details for Mario-style appearance
var hatAsset, weaponAsset;
if (unitType === 'archer') {
// Add archer hat
hatAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.6
});
hatAsset.x = 0;
hatAsset.y = -140;
hatAsset.tint = 0x228B22;
} else if (unitType === 'spearman') {
// Add spearman helmet
hatAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.9,
scaleY: 0.7
});
hatAsset.x = 0;
hatAsset.y = -140;
hatAsset.tint = 0x4169E1;
} else if (unitType === 'cavalry') {
// Add cavalry plume
hatAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 0.8
});
hatAsset.x = 0;
hatAsset.y = -140;
hatAsset.tint = 0x9932CC;
}
if (unitType === 'archer') {
self.damage = 15;
self.range = 350;
self.attackSpeed = 45; // frames between attacks
self.cost = 10;
} else if (unitType === 'spearman') {
self.damage = 25;
self.range = 225;
self.attackSpeed = 30;
self.cost = 15;
} else if (unitType === 'cavalry') {
self.damage = 35;
self.range = 250;
self.attackSpeed = 20;
self.cost = 25;
}
// Apply shop power upgrades to range and attack speed with validation
var powerUpgrade = 0;
if (unitUpgrades[unitType] && typeof unitUpgrades[unitType].power === 'number') {
powerUpgrade = Math.max(0, unitUpgrades[unitType].power);
}
self.range += powerUpgrade * 25; // Each power level adds 25 range
self.attackSpeed = Math.max(5, self.attackSpeed - powerUpgrade * 3); // Each power level improves speed (lower = faster), minimum 5
self.unitType = unitType;
self.attackTimer = 0;
self.target = null;
self.battlesCount = 0; // Track number of battles
self.maxDamage = self.damage; // Store original damage for weakening calculation
self.fatigueLevel = 0; // Unit fatigue from battles
self.update = function () {
self.attackTimer--;
if (self.attackTimer <= 0) {
self.findTarget();
if (self.target && self.isInRange(self.target)) {
self.attack();
self.attackTimer = self.attackSpeed;
}
}
};
self.findTarget = function () {
var closestDistance = self.range;
self.target = null;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance < closestDistance) {
closestDistance = distance;
self.target = enemy;
}
}
};
self.isInRange = function (target) {
var distance = Math.sqrt(Math.pow(target.x - self.x, 2) + Math.pow(target.y - self.y, 2));
return distance <= self.range;
};
self.attack = function () {
if (self.target) {
// Apply battle fatigue - units get weaker with each battle
self.battlesCount++;
self.fatigueLevel = Math.min(self.battlesCount * 0.05, 0.4); // Max 40% damage reduction
var currentDamage = Math.max(1, Math.floor(self.maxDamage * (1 - self.fatigueLevel))); // Ensure minimum 1 damage
// Apply shop damage upgrades with validation
var damageUpgrade = 0;
if (unitUpgrades[self.unitType] && typeof unitUpgrades[self.unitType].damage === 'number') {
damageUpgrade = Math.max(0, unitUpgrades[self.unitType].damage);
}
currentDamage += damageUpgrade * 5;
// Apply combo multiplier to damage
if (comboSystem && comboSystem.comboMultiplier) {
currentDamage = Math.floor(currentDamage * comboSystem.comboMultiplier);
}
var projectile = new Projectile(self.unitType, self.x, self.y, self.target, currentDamage);
// Mark projectile with unit type for tracking
projectile.sourceUnitType = self.unitType;
projectiles.push(projectile);
game.addChild(projectile);
// Play general attack sound for all units
LK.getSound('Ataque').play();
if (self.unitType === 'cavalry') {
LK.getSound('Espada').play();
} else if (self.unitType === 'archer') {
LK.getSound('Arco').play();
}
// Visual feedback for tired units
if (self.fatigueLevel > 0.2) {
tween(self, {
alpha: 0.7
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
alpha: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
}
};
// Nuclear defense cubes are 100% invincible - no takeDamage method needed
return self;
});
var DialogueSound = Container.expand(function () {
var self = Container.call(this);
self.playDialogueSound = function () {
// Play dialogue sound effect when characters speak
LK.getSound('dialogo').play();
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
var enemyGraphics = self.attachAsset(enemyType, {
anchorX: 0.5,
anchorY: 1.0
});
// Add enemy details for Mario-style appearance
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
eyesAsset.x = 0;
eyesAsset.y = -120;
eyesAsset.tint = 0xFFFFFF;
if (enemyType === 'strongEnemy') {
// Add spikes for strong enemy
var spikesAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.6
});
spikesAsset.x = 0;
spikesAsset.y = -160;
spikesAsset.tint = 0x000000;
}
if (enemyType === 'enemy') {
self.health = 30;
self.maxHealth = 30;
self.speed = 1;
self.damage = 10;
self.coinValue = 2;
} else if (enemyType === 'strongEnemy') {
self.health = 60;
self.maxHealth = 60;
self.speed = 0.8;
self.damage = 20;
self.coinValue = 5;
}
self.enemyType = enemyType;
self.lastSpecialPower = 0;
self.specialPowerCooldown = 120; // 2 seconds at 60fps (shorter than other units)
self.canUseSpecialPower = true;
self.update = function () {
// Enemy special power - launch Poder attack when in range of units
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 300) {
// Within range of a unit - launch Poder attack
self.lastSpecialPower = LK.ticks;
var poderProjectile = new PoderProjectile(self.x, self.y, unit, self.damage * 0.5);
projectiles.push(poderProjectile);
game.addChild(poderProjectile);
// Play power sound effect
LK.getSound('Poder').play();
// Visual effect for enemy using power
LK.effects.flashObject(self, 0x00FFFF, 300);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 215) {
// Not at fortress yet
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Attack fortress
fortress.takeDamage(self.damage);
enemiesEntered++; // Count enemy entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when enemy dies to allow repeat triggers
trollfaceShown = false;
self.markForDestroy = true;
}
};
return self;
});
var Explosion = Container.expand(function (x, y) {
var self = Container.call(this);
// Create multiple explosion particles with more variety
var particles = [];
var particleCount = 12 + Math.floor(Math.random() * 6); // 12-18 particles
for (var i = 0; i < particleCount; i++) {
var particle = self.attachAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2 + Math.random() * 0.6,
scaleY: 0.2 + Math.random() * 0.6
});
// Random position offset in circular pattern
var angle = Math.PI * 2 * i / particleCount + Math.random() * 0.5;
var distance = Math.random() * 80;
particle.x = Math.cos(angle) * distance;
particle.y = Math.sin(angle) * distance;
// Enhanced color variety with bright explosion colors
var colors = [0xFF0000, 0xFF4500, 0xFF6600, 0xFF8800, 0xFFAA00, 0xFFCC00, 0xFFFFFF];
particle.tint = colors[Math.floor(Math.random() * colors.length)];
// Random rotation for variety
particle.rotation = Math.random() * Math.PI * 2;
particles.push(particle);
}
self.x = x;
self.y = y;
// Create central flash effect
var centralFlash = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
centralFlash.tint = 0xFFFFFF;
centralFlash.alpha = 1.0;
// Central flash expansion and fade
tween(centralFlash, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut
});
// Animate explosion particles with improved effects
for (var j = 0; j < particles.length; j++) {
var particle = particles[j];
var finalDistance = 150 + Math.random() * 100;
var particleAngle = Math.atan2(particle.y, particle.x);
var finalX = particle.x + Math.cos(particleAngle) * finalDistance;
var finalY = particle.y + Math.sin(particleAngle) * finalDistance;
// Expanding animation with rotation
tween(particle, {
x: finalX,
y: finalY,
scaleX: particle.scaleX * 1.5,
scaleY: particle.scaleY * 1.5,
rotation: particle.rotation + Math.PI * 3,
alpha: 0
}, {
duration: 600 + Math.random() * 400,
// Varied duration
easing: tween.easeOut
});
}
// Screen shake effect for big explosions
if (Math.random() < 0.3) {
// 30% chance for screen shake
LK.effects.flashScreen(0xFFAA00, 300);
}
// Auto-destroy after animation
LK.setTimeout(function () {
self.markForDestroy = true;
}, 1200);
return self;
});
var Fortress = Container.expand(function () {
var self = Container.call(this);
var fortressGraphics = self.attachAsset('fortress', {
anchorX: 0.5,
anchorY: 1.0
});
// Add castle tower details
var leftTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
leftTower.x = -125;
leftTower.y = -75;
leftTower.tint = 0x696969;
var rightTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
rightTower.x = 125;
rightTower.y = -75;
rightTower.tint = 0x696969;
// Add flag
var flag = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.5
});
flag.x = 0;
flag.y = -350;
flag.tint = 0xFF4500;
// Add shield visual effect
var shieldGraphics = self.attachAsset('fortress', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
shieldGraphics.alpha = 0;
shieldGraphics.tint = 0x00FFFF;
self.health = fortressMaxHealth;
self.maxHealth = fortressMaxHealth;
self.activateShield = function () {
if (shieldCooldown <= 0) {
shieldActive = true;
shieldCooldown = shieldMaxCooldown;
shieldGraphics.alpha = 0.5;
tween(shieldGraphics, {
alpha: 0.8
}, {
duration: 500,
easing: tween.easeInOut
});
LK.setTimeout(function () {
shieldActive = false;
tween(shieldGraphics, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000); // Shield lasts 3 seconds
}
};
self.takeDamage = function (damage) {
if (!shieldActive) {
fortressCurrentHealth -= damage;
self.health = fortressCurrentHealth;
LK.effects.flashObject(self, 0xFF0000, 300);
if (fortressCurrentHealth <= 0) {
gameOver = true;
}
} else {
// Shield blocks damage
LK.effects.flashObject(shieldGraphics, 0xFFFFFF, 200);
}
};
return self;
});
var Fortress2 = Container.expand(function () {
var self = Container.call(this);
var fortressGraphics = self.attachAsset('fortress2', {
anchorX: 0.5,
anchorY: 1.0
});
// Add castle tower details
var leftTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
leftTower.x = -125;
leftTower.y = -75;
leftTower.tint = 0x696969;
var rightTower = self.attachAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.6,
scaleY: 0.8
});
rightTower.x = 125;
rightTower.y = -75;
rightTower.tint = 0x696969;
// Add flag
var flag = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.8,
scaleY: 0.5
});
flag.x = 0;
flag.y = -350;
flag.tint = 0xFF4500;
// Add shield visual effect
var shieldGraphics = self.attachAsset('fortress2', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.2
});
shieldGraphics.alpha = 0;
shieldGraphics.tint = 0x00FFFF;
self.health = fortressMaxHealth;
self.maxHealth = fortressMaxHealth;
self.activateShield = function () {
if (shieldCooldown <= 0) {
shieldActive = true;
shieldCooldown = shieldMaxCooldown;
shieldGraphics.alpha = 0.5;
tween(shieldGraphics, {
alpha: 0.8
}, {
duration: 500,
easing: tween.easeInOut
});
LK.setTimeout(function () {
shieldActive = false;
tween(shieldGraphics, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
}, 3000); // Shield lasts 3 seconds
}
};
self.takeDamage = function (damage) {
if (!shieldActive) {
fortressCurrentHealth -= damage;
self.health = fortressCurrentHealth;
LK.effects.flashObject(self, 0xFF0000, 300);
if (fortressCurrentHealth <= 0) {
gameOver = true;
}
} else {
// Shield blocks damage
LK.effects.flashObject(shieldGraphics, 0xFFFFFF, 200);
}
};
return self;
});
var MiniBoss = Container.expand(function () {
var self = Container.call(this);
var miniBossGraphics = self.attachAsset('miniboos1', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
// Smaller than regular boss but bigger than normal enemy
scaleY: 1.5
});
// Add mini boss details
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
eyesAsset.x = 0;
eyesAsset.y = -180; // Adjusted for smaller size
eyesAsset.tint = 0xFFFF00; // Yellow eyes for mini boss
// Add small crown/spikes
var crownAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.0
});
crownAsset.x = 0;
crownAsset.y = -240; // Adjusted for smaller size
crownAsset.tint = 0xC0C0C0; // Silver crown
self.health = 150; // More health than enemies but less than boss
self.maxHealth = 150;
self.speed = 0.8; // Faster than regular boss but slower than normal enemies
self.damage = 20; // Less damage than regular boss
self.coinValue = 25; // Medium reward
self.enemyType = 'miniboss';
self.isMiniBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 240; // 4 seconds at 60fps
self.lastSpecialPower = 0;
self.specialPowerCooldown = 120; // 2 seconds at 60fps
self.canUseSpecialPower = true;
self.update = function () {
// Mini boss special power - similar to regular boss but weaker
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 250) {
// Shorter range than regular boss
// Within range of a unit
self.lastSpecialPower = LK.ticks;
// Temporary speed boost and visual effect
var originalSpeed = self.speed;
self.speed *= 2.0; // 2x speed boost (less than regular boss)
LK.effects.flashObject(self, 0xFFFF00, 300); // Yellow flash
// Speed boost animation
tween(self, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 300,
easing: tween.easeOut
});
// Return to normal after 1 second
LK.setTimeout(function () {
self.speed = originalSpeed;
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 350 && distance > 215 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Mini boss special power - weakens fortress but less than regular boss
LK.effects.flashObject(self, 0xFFFF00, 400);
fortress.takeDamage(10); // Less special attack damage than regular boss
tween(self, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
if (distance > 300) {
// Not at fortress yet - move closer
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Mini boss doesn't reach fortress - just damages it from distance and disappears
fortress.takeDamage(5);
enemiesEntered++; // Count mini boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when mini boss dies to allow repeat triggers
trollfaceShown = false;
self.markForDestroy = true;
}
};
return self;
});
var MiniBoss2 = Container.expand(function () {
var self = Container.call(this);
var miniBossGraphics = self.attachAsset('Miniboos2', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
// Smaller than regular boss but bigger than normal enemy
scaleY: 1.5
});
// Add mini boss details
var eyesAsset = self.attachAsset('spear', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
eyesAsset.x = 0;
eyesAsset.y = -180; // Adjusted for smaller size
eyesAsset.tint = 0xFFFF00; // Yellow eyes for mini boss
// Add small crown/spikes
var crownAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.2,
scaleY: 1.0
});
crownAsset.x = 0;
crownAsset.y = -240; // Adjusted for smaller size
crownAsset.tint = 0xC0C0C0; // Silver crown
self.health = 150; // More health than enemies but less than boss
self.maxHealth = 150;
self.speed = 0.8; // Faster than regular boss but slower than normal enemies
self.damage = 20; // Less damage than regular boss
self.coinValue = 25; // Medium reward
self.enemyType = 'miniboss';
self.isMiniBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 240; // 4 seconds at 60fps
self.lastSpecialPower = 0;
self.specialPowerCooldown = 120; // 2 seconds at 60fps
self.canUseSpecialPower = true;
self.update = function () {
// Mini boss special power - similar to regular boss but weaker
if (LK.ticks - self.lastSpecialPower >= self.specialPowerCooldown && self.canUseSpecialPower) {
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var unitDistance = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (unitDistance <= 250) {
// Shorter range than regular boss
// Within range of a unit
self.lastSpecialPower = LK.ticks;
// Temporary speed boost and visual effect
var originalSpeed = self.speed;
self.speed *= 2.0; // 2x speed boost (less than regular boss)
LK.effects.flashObject(self, 0xFFFF00, 300); // Yellow flash
// Speed boost animation
tween(self, {
scaleX: 1.7,
scaleY: 1.7
}, {
duration: 300,
easing: tween.easeOut
});
// Return to normal after 1 second
LK.setTimeout(function () {
self.speed = originalSpeed;
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeIn
});
}, 1000);
break; // Only use power once per cooldown
}
}
}
// Move toward fortress
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 350 && distance > 215 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Mini boss special power - weakens fortress but less than regular boss
LK.effects.flashObject(self, 0xFFFF00, 400);
fortress.takeDamage(10); // Less special attack damage than regular boss
tween(self, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 200,
easing: tween.easeOut
});
tween(self, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
if (distance > 300) {
// Not at fortress yet - move closer
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Mini boss doesn't reach fortress - just damages it from distance and disappears
fortress.takeDamage(5);
enemiesEntered++; // Count mini boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
// Reset trollface flag when mini boss dies to allow repeat triggers
trollfaceShown = false;
self.markForDestroy = true;
}
};
return self;
});
// Missile class for missile rain events
var Missile = Container.expand(function (x) {
var self = Container.call(this);
var missileAsset = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
self.x = x;
self.y = -60;
self.speed = 18 + Math.random() * 8;
self.hasDamaged = false;
self.update = function () {
self.y += self.speed;
// Check collision with fortress (centered)
if (!self.hasDamaged && fortress && Math.abs(self.x - fortress.x) < 180 && Math.abs(self.y - fortress.y) < 220) {
self.hasDamaged = true;
if (!shieldActive) {
fortressCurrentHealth = Math.max(0, fortressCurrentHealth - 5);
fortress.health = fortressCurrentHealth;
LK.effects.flashObject(fortress, 0xFF0000, 200);
} else {
LK.effects.flashObject(fortress, 0x00FFFF, 200);
}
// Explosion effect
var explosion = new Explosion(self.x, self.y);
game.addChild(explosion);
self.markForDestroy = true;
}
// Remove if off screen
if (self.y > 2800) {
self.markForDestroy = true;
}
};
return self;
});
// MissileRain class for random missile rain event
var MissileRain = Container.expand(function () {
var self = Container.call(this);
self.duration = 1200; // 20 seconds at 60fps
self.ticks = 0;
self.missiles = [];
self.active = true;
// Show warning overlay
var warningOverlay = LK.getAsset('Alerta', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 10,
scaleY: 7
});
warningOverlay.x = 1024;
warningOverlay.y = 600;
warningOverlay.alpha = 1.0;
self.addChild(warningOverlay);
// Animate warning overlay
tween(warningOverlay, {
alpha: 0
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
warningOverlay.destroy();
}
});
// Play alert sound
LK.getSound('alerta').play();
self.update = function () {
self.ticks++;
// Spawn missiles randomly across the screen every 8-16 ticks
if (self.ticks % (8 + Math.floor(Math.random() * 8)) === 0 && self.ticks < self.duration) {
var missileX = 100 + Math.random() * 1848;
var missile = new Missile(missileX);
self.missiles.push(missile);
game.addChild(missile);
}
// Update all missiles
for (var i = self.missiles.length - 1; i >= 0; i--) {
var m = self.missiles[i];
m.update();
if (m.markForDestroy) {
m.destroy();
self.missiles.splice(i, 1);
}
}
// End rain after duration
if (self.ticks >= self.duration && self.missiles.length === 0) {
self.active = false;
self.markForDestroy = true;
}
};
return self;
});
var NuclearDefenseCube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Show the green cube
cubeGraphics.visible = true;
// Add Peligro asset as danger indicator - larger size
var dangerGraphics = self.attachAsset('Peligro', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
dangerGraphics.x = 0;
dangerGraphics.y = 0;
// Visual warning indicators
self.warningLevel = 0;
self.maxWarningLevel = 240; // 4 seconds at 60fps
self.isDefused = false;
// Warning colors from green to red
var warningColors = [0x00FF00, 0x66FF00, 0xCCFF00, 0xFFFF00, 0xFFCC00, 0xFF8800, 0xFF4400, 0xFF0000];
self.update = function () {
if (!self.isDefused) {
self.warningLevel++;
// Change color based on warning level
var colorIndex = Math.min(Math.floor(self.warningLevel / 30), warningColors.length - 1);
dangerGraphics.tint = warningColors[colorIndex];
// Pulsing effect gets faster as time runs out
var pulseSpeed = 0.02 + self.warningLevel / self.maxWarningLevel * 0.08;
dangerGraphics.scaleX = 1.5 + Math.sin(LK.ticks * pulseSpeed) * 0.2;
dangerGraphics.scaleY = 1.5 + Math.sin(LK.ticks * pulseSpeed) * 0.2;
// Rotation effect
dangerGraphics.rotation += 0.05 + self.warningLevel / self.maxWarningLevel * 0.1;
// Check if time is up
if (self.warningLevel >= self.maxWarningLevel) {
// Nuclear bomb hits fortress
self.triggerNuclearBomb();
}
}
};
self.triggerNuclearBomb = function () {
// Massive damage to fortress
if (!shieldActive) {
fortressCurrentHealth = Math.max(0, fortressCurrentHealth - 50);
fortress.health = fortressCurrentHealth;
} else {
// Even with shield, some damage gets through
fortressCurrentHealth = Math.max(0, fortressCurrentHealth - 15);
fortress.health = fortressCurrentHealth;
}
// Massive explosion at fortress
var nuclearExplosion = new Explosion(fortress.x, fortress.y);
// Scale up the explosion
tween(nuclearExplosion, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 800,
easing: tween.easeOut
});
game.addChild(nuclearExplosion);
// Screen effects
LK.effects.flashScreen(0xFFFFFF, 1200);
LK.effects.flashObject(fortress, 0xFF0000, 1500);
// Play explosion sound
LK.getSound('explosion').play();
self.markForDestroy = true;
};
self.down = function (x, y, obj) {
if (!self.isDefused) {
// Successfully defused
self.isDefused = true;
// Visual feedback
dangerGraphics.tint = 0x00FF00;
LK.effects.flashObject(self, 0x00FF00, 500);
// Reward player with coins
coins += 5;
// Play success sound
LK.getSound('Poder').play();
// Shrinking animation
tween(self, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
}
};
return self;
});
var PoderProjectile = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
var projectileGraphics = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 8; // Slower than regular projectiles
// Calculate direction to target
var dx = target.x - startX;
var dy = target.y - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
// Set rotation to face target
projectileGraphics.rotation = Math.atan2(dy, dx);
// Add spinning effect
var spinSpeed = 0.1;
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Spin the projectile
projectileGraphics.rotation += spinSpeed;
// Check if hit target
if (self.target && self.target.takeDamage && Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2)) < 80) {
self.target.takeDamage(self.damage);
// Add special effect when hitting
LK.effects.flashObject(self.target, 0x00FFFF, 500);
self.markForDestroy = true;
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.markForDestroy = true;
}
};
return self;
});
var Projectile = Container.expand(function (unitType, startX, startY, target, damage) {
var self = Container.call(this);
var assetType = unitType === 'archer' ? 'arrow' : 'spear';
var projectileGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.x = startX;
self.y = startY;
self.target = target;
self.damage = damage;
self.speed = 12;
// Calculate direction to target
var dx = target.x - startX;
var dy = target.y - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
// Set rotation to face target
projectileGraphics.rotation = Math.atan2(dy, dx);
self.update = function () {
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Check if hit target
if (self.target && self.target.takeDamage && Math.sqrt(Math.pow(self.target.x - self.x, 2) + Math.pow(self.target.y - self.y, 2)) < 80) {
// Track health before damage for kill detection
var targetHealthBefore = self.target.health;
self.target.takeDamage(self.damage);
// Check if this projectile killed the target and we're tracking
if (trackingActive && targetHealthBefore > 0 && self.target.health <= 0) {
// Only count kills from archer or spearman projectiles
if (self.sourceUnitType === 'archer' || self.sourceUnitType === 'spearman') {
enemiesKilledByTrackedUnits++;
// Check if we've killed 5 enemies with only our tracked units
if (enemiesKilledByTrackedUnits >= 5 && !secretBossSpawned) {
secretBossConditionMet = true;
}
}
}
// Check if this projectile killed the target and we're tracking for second condition
if (trackingActive2 && targetHealthBefore > 0 && self.target.health <= 0) {
// Only count kills from spearman projectiles for second condition
if (self.sourceUnitType === 'spearman') {
if (enemiesKilledByTrackedUnits >= 5 && !secondSecretBossSpawned) {
secondSecretBossConditionMet = true;
}
}
}
// Trigger combo on kill
if (targetHealthBefore > 0 && self.target.health <= 0) {
// Find the source unit and add to combo
for (var cu = 0; cu < defensiveUnits.length; cu++) {
var checkUnit = defensiveUnits[cu];
if (checkUnit && checkUnit.unitType === self.sourceUnitType) {
comboSystem.addKill(checkUnit);
break;
}
}
}
self.markForDestroy = true;
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.markForDestroy = true;
}
};
return self;
});
var ScenarioCube = Container.expand(function () {
var self = Container.call(this);
var cubeGraphics = self.attachAsset('ScenarioCube', {
anchorX: 0.5,
anchorY: 0.5
});
// Add pulsing animation
self.pulseDirection = 1;
self.spawnTime = LK.ticks;
self.update = function () {
// Pulsing effect
if (cubeGraphics.scaleX >= 1.3) {
self.pulseDirection = -1;
} else if (cubeGraphics.scaleX <= 0.8) {
self.pulseDirection = 1;
}
cubeGraphics.scaleX += self.pulseDirection * 0.02;
cubeGraphics.scaleY += self.pulseDirection * 0.02;
// Rotation effect
cubeGraphics.rotation += 0.1;
// Auto-disappear after 10 seconds
if (LK.ticks - self.spawnTime > 600) {
self.markForDestroy = true;
}
};
self.down = function (x, y, obj) {
// Trigger scenario change
changeScenario();
self.markForDestroy = true;
};
return self;
});
var SecretBoss = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('Jefesecreto', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 3.0,
scaleY: 3.0
});
// Add mystical aura
var auraAsset = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
auraAsset.x = 0;
auraAsset.y = -200;
auraAsset.alpha = 0.3;
auraAsset.tint = 0xFF00FF;
// Add health bar background
var healthBarBg = self.attachAsset('HealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -350;
// Add health bar fill
var healthBarFill = self.attachAsset('HealthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -200;
healthBarFill.y = -350;
self.health = 500; // Even more health than regular boss
self.maxHealth = 500;
self.speed = 0.3; // Very slow but powerful
self.damage = 50; // Massive damage
self.coinValue = 100; // Huge reward
self.enemyType = 'secretboss';
self.isSecretBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 180; // 3 seconds
self.update = function () {
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 400 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Mystical aura spinning effect
auraAsset.rotation += 0.05;
// Check distance to fortress for BigPoder attack
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// BigPoder attack when in range but not too close
if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown && distance <= 600 && distance > 300) {
self.lastSpecialAttack = LK.ticks;
// Launch BigPoder projectile at fortress with reduced damage
var bigPoderProjectile = new BigPoderProjectile(self.x, self.y, fortress, 2);
projectiles.push(bigPoderProjectile);
game.addChild(bigPoderProjectile);
// Play power sound effect louder
LK.getSound('Poder').play();
// Visual effect with growing animation
LK.effects.flashObject(self, 0xFF00FF, 500);
tween(self, {
scaleX: 3.5,
scaleY: 3.5
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 400,
easing: tween.easeIn
});
}
});
} else if (distance > 600) {
// Move toward fortress when far away
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else if (distance <= 600) {
// Stay in attack range, don't get too close
// Do nothing, just stay and attack
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
LK.getSound('jefesecreto').play();
// Switch back to normal background music when secret boss is defeated
LK.playMusic('background');
gameWon = true; // Win when secret boss is defeated!
self.markForDestroy = true;
}
};
return self;
});
var SecretBoss2 = Container.expand(function () {
var self = Container.call(this);
var bossGraphics = self.attachAsset('Jefesecreto', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.5,
scaleY: 2.5
});
// Add mystical aura
var auraAsset = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
auraAsset.x = 0;
auraAsset.y = -150;
auraAsset.alpha = 0.3;
auraAsset.tint = 0x00FF00;
// Add health bar background
var healthBarBg = self.attachAsset('HealthBarBg', {
anchorX: 0.5,
anchorY: 0.5
});
healthBarBg.x = 0;
healthBarBg.y = -280;
// Add health bar fill
var healthBarFill = self.attachAsset('HealthBarFill', {
anchorX: 0,
anchorY: 0.5
});
healthBarFill.x = -200;
healthBarFill.y = -280;
self.health = 400; // Slightly less than first secret boss
self.maxHealth = 400;
self.speed = 0.35; // Slightly faster than first secret boss
self.damage = 45; // Less damage than first secret boss
self.coinValue = 80; // Good reward
self.enemyType = 'secretboss2';
self.isSecretBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 200; // 3.3 seconds
self.update = function () {
// Update health bar
var healthPercent = self.health / self.maxHealth;
healthBarFill.width = 400 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Mystical aura spinning effect
auraAsset.rotation += 0.08;
// Check distance to fortress for BigPoder attack
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// BigPoder attack when in range but not too close
if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown && distance <= 550 && distance > 300) {
self.lastSpecialAttack = LK.ticks;
// Launch BigPoder projectile at fortress with reduced damage
var bigPoderProjectile = new BigPoderProjectile(self.x, self.y, fortress, 2);
projectiles.push(bigPoderProjectile);
game.addChild(bigPoderProjectile);
// Play power sound effect
LK.getSound('Poder').play();
// Visual effect
LK.effects.flashObject(self, 0x00FF00, 400);
tween(self, {
scaleX: 2.8,
scaleY: 2.8
}, {
duration: 350,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 350,
easing: tween.easeIn
});
}
});
} else if (distance > 550) {
// Move toward fortress when far away
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else if (distance <= 550) {
// Stay in attack range
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xFFFFFF, 200);
if (self.health <= 0) {
coins += self.coinValue;
enemiesKilled++;
LK.getSound('jefesecreto').play();
// Switch back to normal background music when secret boss is defeated
LK.playMusic('background');
gameWon = true; // Win when secret boss is defeated!
self.markForDestroy = true;
}
};
return self;
});
var TimeDistortion = Container.expand(function () {
var self = Container.call(this);
// Visual effect circle that expands outward
var distortionRing = self.attachAsset('BigPoder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
distortionRing.tint = 0x00FFFF;
distortionRing.alpha = 0.6;
// Inner energy core
var energyCore = self.attachAsset('Poder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
energyCore.tint = 0xFFFFFF;
energyCore.alpha = 0.8;
self.duration = 600; // 10 seconds
self.ticks = 0;
self.active = true;
self.affectedEnemies = [];
self.affectedProjectiles = [];
// Expansion animation for ring
tween(distortionRing, {
scaleX: 15,
scaleY: 15,
alpha: 0.2
}, {
duration: 1000,
easing: tween.easeOut
});
// Pulsing animation for core
function pulseCore() {
if (self.active) {
tween(energyCore, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1.0
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.active) {
tween(energyCore, {
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.6
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
pulseCore();
}
});
}
}
});
}
}
pulseCore();
self.update = function () {
self.ticks++;
energyCore.rotation += 0.15; // Spinning energy core
distortionRing.rotation -= 0.05; // Counter-rotating ring
// Apply time distortion effects to all enemies and projectiles within range
var distortionRadius = 800;
// Slow down enemies within range
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= distortionRadius) {
// Mark enemy as affected if not already
if (self.affectedEnemies.indexOf(enemy) === -1) {
self.affectedEnemies.push(enemy);
enemy.originalSpeed = enemy.speed;
enemy.speed *= 0.3; // Slow to 30% speed
// Visual effect - blue tint
tween(enemy, {
tint: 0x0088FF
}, {
duration: 300,
easing: tween.easeOut
});
}
}
}
// Slow down enemy projectiles within range
for (var j = 0; j < projectiles.length; j++) {
var proj = projectiles[j];
if (proj.target && proj.target === fortress) {
// Only affect enemy projectiles (those targeting fortress)
var projDistance = Math.sqrt(Math.pow(proj.x - self.x, 2) + Math.pow(proj.y - self.y, 2));
if (projDistance <= distortionRadius) {
if (self.affectedProjectiles.indexOf(proj) === -1) {
self.affectedProjectiles.push(proj);
proj.originalSpeed = proj.speed;
proj.speed *= 0.2; // Slow projectiles even more
// Visual trail effect
tween(proj, {
tint: 0x00DDFF
}, {
duration: 200,
easing: tween.easeOut
});
}
}
}
}
// End distortion after duration
if (self.ticks >= self.duration) {
self.active = false;
// Restore original speeds and remove effects
for (var k = 0; k < self.affectedEnemies.length; k++) {
var affectedEnemy = self.affectedEnemies[k];
if (affectedEnemy.originalSpeed !== undefined) {
affectedEnemy.speed = affectedEnemy.originalSpeed;
// Remove tint
tween(affectedEnemy, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeOut
});
}
}
for (var l = 0; l < self.affectedProjectiles.length; l++) {
var affectedProj = self.affectedProjectiles[l];
if (affectedProj.originalSpeed !== undefined) {
affectedProj.speed = affectedProj.originalSpeed;
// Remove tint
tween(affectedProj, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Fade out effect
tween(self, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
}
};
return self;
});
// Unit Leveling and Combo System
var UnitCombo = Container.expand(function () {
var self = Container.call(this);
self.comboCount = 0;
self.comboTimer = 0;
self.maxComboTimer = 300; // 5 seconds at 60fps
self.comboMultiplier = 1.0;
self.comboLevel = 0; // 0-5 levels
self.participatingUnits = [];
self.update = function () {
if (self.comboTimer > 0) {
self.comboTimer--;
} else if (self.comboCount > 0) {
// Combo expired
self.resetCombo();
}
};
self.addKill = function (unit) {
self.comboCount++;
self.comboTimer = self.maxComboTimer;
// Update combo multiplier based on count
if (self.comboCount >= 5) {
self.comboLevel = 5;
self.comboMultiplier = 3.0;
} else if (self.comboCount >= 4) {
self.comboLevel = 4;
self.comboMultiplier = 2.5;
} else if (self.comboCount >= 3) {
self.comboLevel = 3;
self.comboMultiplier = 2.0;
} else if (self.comboCount >= 2) {
self.comboLevel = 2;
self.comboMultiplier = 1.5;
} else {
self.comboLevel = 1;
self.comboMultiplier = 1.2;
}
// Track participating unit
if (self.participatingUnits.indexOf(unit) === -1) {
self.participatingUnits.push(unit);
}
// Bonus coins for combo
var bonusCoins = Math.floor(self.comboCount * 2);
coins += bonusCoins;
// Visual feedback
LK.effects.flashScreen(0xFFFF00, 200);
};
self.resetCombo = function () {
self.comboCount = 0;
self.comboLevel = 0;
self.comboMultiplier = 1.0;
self.participatingUnits = [];
self.comboTimer = 0;
};
return self;
});
var UnitLevel = Container.expand(function () {
var self = Container.call(this);
self.level = 1;
self.experience = 0;
self.experienceToNextLevel = 100;
self.bonusDamage = 0;
self.bonusRange = 0;
self.bonusSpeed = 0;
self.gainExperience = function (amount) {
self.experience += amount;
if (self.experience >= self.experienceToNextLevel) {
self.levelUp();
}
};
self.levelUp = function () {
self.level++;
self.experience = 0;
self.experienceToNextLevel = Math.floor(self.experienceToNextLevel * 1.5);
// Calculate stat bonuses based on level
self.bonusDamage = Math.floor((self.level - 1) * 5);
self.bonusRange = Math.floor((self.level - 1) * 25);
self.bonusSpeed = Math.floor((self.level - 1) * 0.1 * 10) / 10; // 0.1 increment
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xFFCC00 // Bright 8-bit yellow desert background
});
/****
* Game Code
****/
// Load persisted upgrades into game with validation
// Remove healButton if secret boss is defeated or game reset
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
if (storage.unitUpgrades && _typeof(storage.unitUpgrades) === 'object') {
// Validate structure exists before assignment
if (storage.unitUpgrades.archer && storage.unitUpgrades.spearman && storage.unitUpgrades.cavalry) {
unitUpgrades = storage.unitUpgrades;
}
}
if (storage.shopCoins && typeof storage.shopCoins === 'number' && storage.shopCoins >= 0) {
shopCoins = Math.floor(storage.shopCoins);
}
if ((!secretBossSpawned || gameOver || gameWon) && healButton) {
if (typeof healButton.destroy === "function") {
healButton.destroy();
}
healButton = null;
}
var gameState = 'menu'; // 'menu' or 'playing'
var menuElements = [];
// Scenario change variables
var scenarioCubes = [];
var lastCubeSpawn = 0;
var cubeSpawnInterval = 1200; // 20 seconds at 60fps
var currentScenario = 'desert'; // 'desert' or 'alternate'
var scenarioChangeActive = false;
// Nuclear Defense System variables
var nuclearDefenseActive = false;
var nuclearDefenseCubes = [];
var lastNuclearAttackTime = 0;
var nuclearAttackCooldown = 1800; // 30 seconds between nuclear attack waves
// Missile rain event variables
var missileRainActive = false;
var missileRainTimer = 0;
var missileRainInstance = null;
// Game variables
var fortress;
var defensiveUnits = [];
var enemies = [];
var projectiles = [];
var coins = 50;
var currentWave = 1;
var enemiesInWave = 5;
var enemiesSpawned = 0;
var waveDelay = 180; // 3 seconds at 60fps
var spawnDelay = 60; // 1 second between enemy spawns
var gameOver = false;
var enemiesKilled = 0;
var selectedUnitType = 'archer';
var enemiesEntered = 0; // Track enemies that entered fortress
var maxEnemiesAllowed = 10; // Game ends when 10 enemies enter
var bossSpawned = false;
var bossWave = 10; // Boss appears on wave 10
var gameWon = false;
var fortressMaxHealth = 200;
var fortressCurrentHealth = 200;
var shieldActive = false;
var shieldCooldown = 0;
var shieldMaxCooldown = 600; // 10 seconds at 60fps
var lastShieldTime = 0;
var trollfaceOverlay = null;
var trollfaceShown = false;
var lastAlarmTime = 0;
var alarmCooldown = 300; // 5 seconds at 60fps
// Combo system variables
var comboSystem = new UnitCombo();
var comboText = null;
// Secret boss tracking variables
var secretBossConditionMet = false;
var secretBossSpawned = false;
var archerCount = 0;
var spearmanCount = 0;
var cavalryCount = 0;
var enemiesKilledByTrackedUnits = 0;
var trackingActive = false;
// Second secret boss tracking variables
var secondSecretBossConditionMet = false;
var secondSecretBossSpawned = false;
var archerCount2 = 0;
var spearmanCount2 = 0;
var cavalryCount2 = 0;
var trackingActive2 = false;
// Healing button variables
var healButton = null;
var healCooldown = 0;
var healMaxCooldown = 360; // 6 seconds at 60fps
// Time Distortion variables
var timeDistortionButton = null;
var timeDistortionCooldown = 0;
var timeDistortionMaxCooldown = 1800; // 30 seconds at 60fps
var timeDistortionActive = false;
var timeDistortionInstance = null;
// Boss spawn tracking variables
var lastBossSpawnKillCount = 0;
var totalMiniBossesSpawned = 0;
var maxMiniBossesToSpawn = 20;
// Shop system variables - initialize persistent weapon upgrades
var shopState = 'menu'; // 'menu' or 'shop'
var unitUpgrades = {
archer: {
damage: 0,
power: 0
},
spearman: {
damage: 0,
power: 0
},
cavalry: {
damage: 0,
power: 0
}
};
var shopCoins = 50;
// Menu initialization function
function initMainMenu() {
gameState = 'menu';
shopState = 'menu';
// Clear any existing menu elements
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Set blue background for menu
game.setBackgroundColor(0x0000FF);
// Play menu music
LK.playMusic('Pantalladeinicio');
// Game title
var titleText = new Text2('DEFENSA DE FORTALEZA', {
size: 120,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
game.addChild(titleText);
menuElements.push(titleText);
// Subtitle
var subtitleText = new Text2('Torre de Defensa Islámica', {
size: 80,
fill: 0xFFFF00
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 550;
game.addChild(subtitleText);
menuElements.push(subtitleText);
// Instructions
var instructionsText = new Text2('¡Defiende tu fortaleza!\nColoca unidades para detener enemigos\n¡No dejes que entren 10 enemigos!', {
size: 80,
fill: 0xFFFF00
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.x = 1024;
instructionsText.y = 900;
game.addChild(instructionsText);
menuElements.push(instructionsText);
// Shop button
var shopButton = new Text2('TIENDA', {
size: 90,
fill: 0xFFAA00
});
shopButton.anchor.set(0.5, 0.5);
shopButton.x = 600;
shopButton.y = 1300;
game.addChild(shopButton);
menuElements.push(shopButton);
shopButton.down = function () {
initShop();
};
// Start button
var startButton = new Text2('JUGAR', {
size: 90,
fill: 0xFFFF00
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1450;
startButton.y = 1300;
game.addChild(startButton);
menuElements.push(startButton);
// Animate start button with continuous twinkling effect
function startTwinkle() {
if (gameState === 'menu' && shopState === 'menu') {
tween(startButton, {
alpha: 0.3,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (gameState === 'menu' && shopState === 'menu') {
tween(startButton, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
startTwinkle();
}
});
}
}
});
}
}
startTwinkle();
startButton.down = function () {
initCinematic();
};
}
// Shop initialization function
function initShop() {
shopState = 'shop';
// Clear menu elements
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Set shop background
game.setBackgroundColor(0x1a1a2e);
// Shop title
var shopTitle = new Text2('TIENDA DE MEJORAS', {
size: 100,
fill: 0xFFFF00
});
shopTitle.anchor.set(0.5, 0.5);
shopTitle.x = 1024;
shopTitle.y = 100;
game.addChild(shopTitle);
menuElements.push(shopTitle);
// Coins display
var coinsDisplay = new Text2('Monedas: ' + shopCoins, {
size: 60,
fill: 0xFFCC00
});
coinsDisplay.anchor.set(0.5, 0.5);
coinsDisplay.x = 1024;
coinsDisplay.y = 250;
game.addChild(coinsDisplay);
menuElements.push(coinsDisplay);
// Create upgrade cards for each unit type
var yOffset = 450;
var unitTypes = ['archer', 'spearman', 'cavalry'];
var unitNames = ['Arquero', 'Lancero', 'Caballería'];
var unitColors = [0x00FF00, 0x0080FF, 0xFF00FF];
for (var u = 0; u < unitTypes.length; u++) {
var unitType = unitTypes[u];
var unitName = unitNames[u];
var unitColor = unitColors[u];
// Unit card background
var cardBg = LK.getAsset('HealthBarBg', {
width: 900,
height: 280,
anchorX: 0.5,
anchorY: 0.5
});
cardBg.tint = 0x16213e;
cardBg.x = 1024;
cardBg.y = yOffset;
game.addChild(cardBg);
menuElements.push(cardBg);
// Unit name
var nameText = new Text2(unitName, {
size: 50,
fill: unitColor
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 700;
nameText.y = yOffset - 80;
game.addChild(nameText);
menuElements.push(nameText);
// Damage upgrade section
var damageLabel = new Text2('Daño: ' + unitUpgrades[unitType].damage, {
size: 40,
fill: 0xFFFFFF
});
damageLabel.anchor.set(0.5, 0.5);
damageLabel.x = 900;
damageLabel.y = yOffset - 20;
game.addChild(damageLabel);
menuElements.push(damageLabel);
// Damage upgrade button
var damageButton = new Text2('Mejorar\n(10 monedas)', {
size: 32,
fill: 0x00FF00
});
damageButton.anchor.set(0.5, 0.5);
damageButton.x = 1100;
damageButton.y = yOffset - 20;
game.addChild(damageButton);
menuElements.push(damageButton);
(function (type, damageBtn, damageLabel, coinsDisp) {
damageBtn.down = function () {
if (gameState === 'menu' && shopState === 'shop' && shopCoins >= 10 && unitUpgrades[type] && typeof unitUpgrades[type].damage === 'number') {
shopCoins = Math.max(0, shopCoins - 10);
unitUpgrades[type].damage = Math.max(0, unitUpgrades[type].damage + 1);
coinsDisp.setText('Monedas: ' + shopCoins);
damageLabel.setText('Daño: ' + unitUpgrades[type].damage);
storage.shopCoins = shopCoins;
storage.unitUpgrades = unitUpgrades;
LK.getSound('Poder').play();
tween(damageBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(damageBtn, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
})(unitType, damageButton, damageLabel, coinsDisplay);
// Power upgrade section
var powerLabel = new Text2('Poder: ' + unitUpgrades[unitType].power, {
size: 40,
fill: 0xFFFFFF
});
powerLabel.anchor.set(0.5, 0.5);
powerLabel.x = 900;
powerLabel.y = yOffset + 60;
game.addChild(powerLabel);
menuElements.push(powerLabel);
// Power upgrade button
var powerButton = new Text2('Mejorar\n(15 monedas)', {
size: 32,
fill: 0xFF00FF
});
powerButton.anchor.set(0.5, 0.5);
powerButton.x = 1100;
powerButton.y = yOffset + 60;
game.addChild(powerButton);
menuElements.push(powerButton);
(function (type, powerBtn, powerLabel, coinsDisp) {
powerBtn.down = function () {
if (gameState === 'menu' && shopState === 'shop' && shopCoins >= 15 && unitUpgrades[type] && typeof unitUpgrades[type].power === 'number') {
shopCoins = Math.max(0, shopCoins - 15);
unitUpgrades[type].power = Math.max(0, unitUpgrades[type].power + 1);
coinsDisp.setText('Monedas: ' + shopCoins);
powerLabel.setText('Poder: ' + unitUpgrades[type].power);
storage.shopCoins = shopCoins;
storage.unitUpgrades = unitUpgrades;
LK.getSound('Poder').play();
tween(powerBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(powerBtn, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
})(unitType, powerButton, powerLabel, coinsDisplay);
yOffset += 350;
}
// Back button to return to main menu
var backButton = new Text2('VOLVER AL MENÚ', {
size: 50,
fill: 0xFF6666
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 1024;
backButton.y = 2550;
game.addChild(backButton);
menuElements.push(backButton);
backButton.down = function () {
if (gameState === 'menu' && shopState === 'shop') {
shopState = 'menu';
initMainMenu();
}
};
}
// Cinematic initialization function
function initCinematic() {
gameState = 'cinematic';
// Clear menu elements
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Set dark background for cinematic
game.setBackgroundColor(0x000020);
// Play cinematic music
LK.playMusic('cinematica');
// Create knight character on left
var knight = LK.getAsset('cavalry', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 2.0
});
knight.x = 400;
knight.y = 1800;
game.addChild(knight);
// Create spearman character on right
var spearman = LK.getAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 2.0
});
spearman.x = 1648;
spearman.y = 1800;
game.addChild(spearman);
// Create dialogue box background
var dialogueBox = LK.getAsset('HealthBarBg', {
width: 1800,
height: 300,
anchorX: 0.5,
anchorY: 0.5
});
dialogueBox.x = 1024;
dialogueBox.y = 2200;
dialogueBox.tint = 0x222222;
game.addChild(dialogueBox);
// Create dialogue text
var dialogueText = new Text2('', {
size: 60,
fill: 0xFFFFFF
});
dialogueText.anchor.set(0.5, 0.5);
dialogueText.x = 1024;
dialogueText.y = 2200;
game.addChild(dialogueText);
// Animate characters entering from sides
tween(knight, {
x: 600
}, {
duration: 1000,
easing: tween.easeOut
});
tween(spearman, {
x: 1448
}, {
duration: 1000,
easing: tween.easeOut
});
// Start dialogue sequence after characters are in position
LK.setTimeout(function () {
// Knight speaks first
dialogueText.setText('Caballero: "Está a punto de empezar el conflicto"');
// Play dialogue sound when knight speaks
LK.getSound('dialogo').play();
tween(knight, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(knight, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// After 3 seconds, spearman responds
LK.setTimeout(function () {
dialogueText.setText('Lancero: "Sí, en guardia"');
// Play dialogue sound when spearman speaks
LK.getSound('dialogo').play();
tween(spearman, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(spearman, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
// After another 2 seconds, fade out and start game
LK.setTimeout(function () {
// Fade out all cinematic elements
tween(knight, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
tween(spearman, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
tween(dialogueBox, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut
});
tween(dialogueText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Clean up cinematic elements
knight.destroy();
spearman.destroy();
dialogueBox.destroy();
dialogueText.destroy();
// Start the actual game
initGame();
}
});
}, 2000);
}, 3000);
}, 1500);
}
// Game initialization function
function initGame() {
gameState = 'playing';
// Reset background to game color
game.setBackgroundColor(0xFFCC00);
// Clear menu elements (already cleared in cinematic)
for (var i = 0; i < menuElements.length; i++) {
menuElements[i].destroy();
}
menuElements = [];
// Reset game variables
coins = 50;
currentWave = 1;
enemiesInWave = 5;
enemiesSpawned = 0;
waveDelay = 180;
spawnDelay = 60;
gameOver = false;
enemiesKilled = 0;
selectedUnitType = 'archer';
enemiesEntered = 0;
bossSpawned = false;
gameWon = false;
fortressCurrentHealth = 200;
shieldActive = false;
shieldCooldown = 0;
trollfaceShown = false;
if (trollfaceOverlay) {
trollfaceOverlay.destroy();
trollfaceOverlay = null;
}
// Reset secret boss tracking
secretBossConditionMet = false;
secretBossSpawned = false;
archerCount = 0;
spearmanCount = 0;
cavalryCount = 0;
enemiesKilledByTrackedUnits = 0;
trackingActive = false;
// Reset boss spawn tracking
lastBossSpawnKillCount = 0;
totalMiniBossesSpawned = 0;
// Reset alarm cooldown
lastAlarmTime = 0;
// Reset combo system
comboSystem.resetCombo();
// Reset healing button
healCooldown = 0;
if (healButton) {
if (typeof healButton.destroy === "function") {
healButton.destroy();
}
healButton = null;
}
// Reset time distortion
timeDistortionCooldown = 0;
timeDistortionActive = false;
if (timeDistortionInstance) {
if (typeof timeDistortionInstance.destroy === "function") {
timeDistortionInstance.destroy();
}
timeDistortionInstance = null;
}
if (timeDistortionButton) {
if (typeof timeDistortionButton.destroy === "function") {
timeDistortionButton.destroy();
}
timeDistortionButton = null;
}
// Reset nuclear defense system
nuclearDefenseActive = false;
for (var reset_i = 0; reset_i < nuclearDefenseCubes.length; reset_i++) {
if (nuclearDefenseCubes[reset_i] && typeof nuclearDefenseCubes[reset_i].destroy === "function") {
nuclearDefenseCubes[reset_i].destroy();
}
}
nuclearDefenseCubes = [];
lastNuclearAttackTime = 0;
// Healing button will be created only when secret boss spawns
// Clear arrays
defensiveUnits = [];
enemies = [];
projectiles = [];
scenarioCubes = [];
// Reset scenario variables
currentScenario = 'desert';
scenarioChangeActive = false;
lastCubeSpawn = 0;
cubeSpawnInterval = 1200;
// Initialize fortress at center
fortress = new Fortress();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
// Start background music for gameplay
LK.playMusic('background');
}
// Scenario change function
function changeScenario() {
if (scenarioChangeActive) return;
scenarioChangeActive = true;
// Clear all existing enemies except the fortress
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
// Clear all projectiles
for (var j = projectiles.length - 1; j >= 0; j--) {
projectiles[j].destroy();
projectiles.splice(j, 1);
}
// Clear all defensive units placed by player to leave clean scenario
for (var k = defensiveUnits.length - 1; k >= 0; k--) {
var unit = defensiveUnits[k];
unit.destroy();
defensiveUnits.splice(k, 1);
}
// Change scenario
if (currentScenario === 'desert') {
currentScenario = 'alternate';
game.setBackgroundColor(0x808080); // Gray environment
// Change fortress to fortress2
fortress.destroy();
fortress = new Fortress2();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
} else {
currentScenario = 'desert';
game.setBackgroundColor(0xFFCC00); // Desert yellow
// Reset fortress to normal
fortress.destroy();
fortress = new Fortress();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
}
// Spawn only airplane in alternate scenario, strongEnemy in desert
if (currentScenario === 'alternate') {
var airplane = new Avion();
enemies.push(airplane);
game.addChild(airplane);
} else {
var singleEnemy = new Enemy('strongEnemy');
singleEnemy.x = Math.random() * 1500 + 274; // Random position not too close to edges
singleEnemy.y = Math.random() * 1000 + 200;
enemies.push(singleEnemy);
game.addChild(singleEnemy);
}
// Visual effect for scenario change
LK.effects.flashScreen(0xFFFFFF, 1000);
// Reset scenario change flag after a delay
LK.setTimeout(function () {
scenarioChangeActive = false;
}, 2000);
}
// Start with main menu
initMainMenu();
// UI Elements
var coinsText = new Text2('Monedas: ' + coins, {
size: 32,
fill: 0xFFFF00
});
coinsText.anchor.set(0, 0);
coinsText.x = 120;
coinsText.y = 50;
LK.gui.topLeft.addChild(coinsText);
var waveText = new Text2('Oleada: ' + currentWave, {
size: 32,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.x = 0;
waveText.y = 50;
LK.gui.top.addChild(waveText);
var healthText = new Text2('Fortaleza: ' + fortressCurrentHealth + '/' + fortressMaxHealth, {
size: 32,
fill: 0xFF0000
});
healthText.anchor.set(1, 0);
healthText.x = -20;
healthText.y = 50;
LK.gui.topRight.addChild(healthText);
// Add health bar
var healthBarBg = LK.getAsset('fortress', {
width: 200,
height: 20,
anchorX: 0.5,
anchorY: 0
});
healthBarBg.tint = 0x666666;
healthBarBg.x = 0;
healthBarBg.y = 90;
LK.gui.top.addChild(healthBarBg);
var healthBarFill = LK.getAsset('fortress', {
width: 200,
height: 20,
anchorX: 0,
anchorY: 0
});
healthBarFill.tint = 0x00FF00;
healthBarFill.x = -100;
healthBarFill.y = 90;
LK.gui.top.addChild(healthBarFill);
// Add combo display text
comboText = new Text2('COMBO: 0x1.0', {
size: 32,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
comboText.x = 0;
comboText.y = 130;
LK.gui.top.addChild(comboText);
// Add shield button
var shieldButton = new Text2('Escudo (Listo)', {
size: 24,
fill: 0x00FFFF
});
shieldButton.anchor.set(0.5, 1);
shieldButton.x = 0;
shieldButton.y = -100;
LK.gui.bottom.addChild(shieldButton);
shieldButton.down = function () {
fortress.activateShield();
};
var enemiesEnteredText = new Text2('Enemigos Entraron: ' + enemiesEntered + '/' + maxEnemiesAllowed, {
size: 28,
fill: 0xFFFFFF
});
enemiesEnteredText.anchor.set(0.5, 0);
enemiesEnteredText.x = 0;
enemiesEnteredText.y = 100;
LK.gui.top.addChild(enemiesEnteredText);
// Unit selection buttons
var archerButton = new Text2('Arquero (10)', {
size: 28,
fill: 0x00FF00
});
archerButton.anchor.set(0.5, 1);
archerButton.x = -200;
archerButton.y = -50;
LK.gui.bottom.addChild(archerButton);
var spearmanButton = new Text2('Lancero (15)', {
size: 28,
fill: 0x0080FF
});
spearmanButton.anchor.set(0.5, 1);
spearmanButton.x = 0;
spearmanButton.y = -50;
LK.gui.bottom.addChild(spearmanButton);
var cavalryButton = new Text2('Caballería (25)', {
size: 28,
fill: 0xFF00FF
});
cavalryButton.anchor.set(0.5, 1);
cavalryButton.x = 200;
cavalryButton.y = -50;
LK.gui.bottom.addChild(cavalryButton);
// Button press handlers
archerButton.down = function () {
selectedUnitType = 'archer';
updateButtonColors();
};
spearmanButton.down = function () {
selectedUnitType = 'spearman';
updateButtonColors();
};
cavalryButton.down = function () {
selectedUnitType = 'cavalry';
updateButtonColors();
};
function updateButtonColors() {
archerButton.fill = selectedUnitType === 'archer' ? 0xFFFFFF : 0x00FF00;
spearmanButton.fill = selectedUnitType === 'spearman' ? 0xFFFFFF : 0x0080FF;
cavalryButton.fill = selectedUnitType === 'cavalry' ? 0xFFFFFF : 0xFF00FF;
}
updateButtonColors();
// Game input
game.down = function (x, y, obj) {
// Allow buttons to work during menu state - don't block their down handlers
if (gameState === 'menu' || gameState === 'cinematic') {
// Block all game placement input during menu or cinematic, but buttons still work
return;
}
if (gameOver) return;
// Disable unit placement during nuclear Quick Time Event
if (nuclearDefenseActive) {
return;
}
var unitCost = 0;
if (selectedUnitType === 'archer') {
unitCost = 10;
} else if (selectedUnitType === 'spearman') {
unitCost = 15;
} else if (selectedUnitType === 'cavalry') {
unitCost = 25;
} else {
return; // Invalid unit type
}
if (coins >= unitCost && unitCost > 0) {
var newUnit = new DefensiveUnit(selectedUnitType);
newUnit.x = x;
newUnit.y = y;
defensiveUnits.push(newUnit);
game.addChild(newUnit);
coins = Math.max(0, coins - unitCost);
LK.getSound('deploy').play();
// Track unit counts for secret boss condition
if (selectedUnitType === 'archer') {
archerCount++;
archerCount2++;
} else if (selectedUnitType === 'spearman') {
spearmanCount++;
spearmanCount2++;
} else if (selectedUnitType === 'cavalry') {
cavalryCount++;
cavalryCount2++;
}
// Check if we have exactly 3 archers and 1 spearman with no cavalry (first condition)
if (archerCount === 3 && spearmanCount === 1 && cavalryCount === 0 && !trackingActive && !secretBossSpawned) {
trackingActive = true;
enemiesKilledByTrackedUnits = 0;
}
// Check if we have exactly 1 archer and 3 spearmen with no cavalry (second condition)
if (archerCount2 === 1 && spearmanCount2 === 3 && cavalryCount2 === 0 && !trackingActive2 && !secondSecretBossSpawned) {
trackingActive2 = true;
}
// If we deploy any other units or wrong counts, disable tracking for first condition
if (trackingActive && (archerCount !== 3 || spearmanCount !== 1 || cavalryCount > 0)) {
trackingActive = false;
enemiesKilledByTrackedUnits = 0; // Reset kill count when tracking stops
}
// If we deploy any other units or wrong counts, disable tracking for second condition
if (trackingActive2 && (archerCount2 !== 1 || spearmanCount2 !== 3 || cavalryCount2 > 0)) {
trackingActive2 = false;
}
}
};
// Spawn enemies
function spawnEnemy() {
var enemy;
// Spawn boss on wave 10
if (currentWave >= bossWave && !bossSpawned) {
enemy = new Boss();
bossSpawned = true;
} else {
var enemyType = currentWave > 3 && Math.random() < 0.3 ? 'strongEnemy' : 'enemy';
enemy = new Enemy(enemyType);
}
// Spawn from random edge, avoiding fortress area
var side = Math.floor(Math.random() * 4);
if (side === 0) {
// Top - avoid fortress center area
enemy.x = Math.random() < 0.5 ? Math.random() * 700 : Math.random() * 700 + 1348;
enemy.y = -50;
} else if (side === 1) {
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
} else if (side === 2) {
// Bottom - avoid fortress center area
enemy.x = Math.random() < 0.5 ? Math.random() * 700 : Math.random() * 700 + 1348;
enemy.y = 2782;
} else {
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
}
// Initialize spawn time for auto-disappear functionality
enemy.spawnTime = LK.ticks;
enemies.push(enemy);
game.addChild(enemy);
}
game.update = function () {
// Don't run game logic when in menu or cinematic
if (gameState === 'menu' || gameState === 'cinematic') {
return;
}
// Check win condition - boss defeated
if (gameWon) {
LK.showYouWin();
return;
}
// Check lose conditions
if (gameOver || enemiesEntered >= maxEnemiesAllowed) {
LK.showGameOver();
return;
}
// Update shield cooldown
if (shieldCooldown > 0) {
shieldCooldown--;
}
// Update heal cooldown and button appearance
if (healCooldown > 0) {
healCooldown--;
}
// Update time distortion cooldown
if (timeDistortionCooldown > 0) {
timeDistortionCooldown--;
}
// Create Time Distortion button when player has 5+ archer units and at least wave 3
var archerUnitsCount = 0;
for (var archer_i = 0; archer_i < defensiveUnits.length; archer_i++) {
if (defensiveUnits[archer_i].unitType === 'archer') {
archerUnitsCount++;
}
}
if (archerUnitsCount >= 5 && currentWave >= 3 && !timeDistortionButton) {
timeDistortionButton = new Text2('DISTORSIÓN TEMPORAL', {
size: 20,
fill: 0x00FFFF
});
timeDistortionButton.anchor.set(0.5, 1);
timeDistortionButton.x = -200;
timeDistortionButton.y = -150;
LK.gui.bottom.addChild(timeDistortionButton);
timeDistortionButton.down = function () {
if (timeDistortionCooldown <= 0 && !timeDistortionActive) {
// Activate time distortion at fortress location
timeDistortionActive = true;
timeDistortionCooldown = timeDistortionMaxCooldown;
timeDistortionInstance = new TimeDistortion();
timeDistortionInstance.x = fortress.x;
timeDistortionInstance.y = fortress.y;
game.addChild(timeDistortionInstance);
// Visual and audio feedback
LK.effects.flashScreen(0x00FFFF, 500);
LK.getSound('Poder').play();
}
};
}
// Update time distortion button appearance
if (timeDistortionButton) {
if (timeDistortionCooldown > 0) {
var secondsLeft = Math.ceil(timeDistortionCooldown / 60);
timeDistortionButton.setText('DISTORSIÓN (' + secondsLeft + 's)');
timeDistortionButton.fill = 0x666666;
} else if (timeDistortionActive) {
timeDistortionButton.setText('DISTORSIÓN ACTIVA');
timeDistortionButton.fill = 0x00FF00;
} else {
timeDistortionButton.setText('DISTORSIÓN TEMPORAL');
timeDistortionButton.fill = 0x00FFFF;
}
}
// Update time distortion instance
if (timeDistortionActive && timeDistortionInstance) {
timeDistortionInstance.update();
if (!timeDistortionInstance.active) {
timeDistortionActive = false;
if (typeof timeDistortionInstance.destroy === "function") {
timeDistortionInstance.destroy();
}
timeDistortionInstance = null;
}
}
// Remove time distortion button if archer count drops below 5
if (timeDistortionButton && archerUnitsCount < 5) {
timeDistortionButton.destroy();
timeDistortionButton = null;
}
// Update healing button if it exists (only when secret boss is active)
if (healButton && secretBossSpawned) {
if (healCooldown > 0) {
var secondsLeft = Math.ceil(healCooldown / 60);
if (typeof healButton.setText === "function") {
healButton.setText('CURAR (' + secondsLeft + 's)');
healButton.fill = 0x666666;
} else if (healButton.healText && typeof healButton.healText.setText === "function") {
healButton.healText.setText('CURAR (' + secondsLeft + 's)');
healButton.healText.fill = 0x666666;
}
} else {
if (typeof healButton.setText === "function") {
healButton.setText('CURAR');
healButton.fill = 0xFFFFFF;
} else if (healButton.healText && typeof healButton.healText.setText === "function") {
healButton.healText.setText('CURAR');
healButton.healText.fill = 0xFFFFFF;
}
}
}
// Remove healButton if secret boss is defeated or game is reset
if ((!secretBossSpawned || gameOver || gameWon) && healButton) {
healButton.destroy();
healButton = null;
}
// Update combo system
comboSystem.update();
// Update UI
coinsText.setText('Monedas: ' + coins);
waveText.setText('Oleada: ' + currentWave);
healthText.setText('Fortaleza: ' + fortressCurrentHealth + '/' + fortressMaxHealth);
enemiesEnteredText.setText('Enemigos Entraron: ' + enemiesEntered + '/' + maxEnemiesAllowed);
LK.setScore(enemiesKilled);
// Update combo display
if (comboText) {
if (comboSystem.comboLevel > 0) {
comboText.setText('COMBO: ' + comboSystem.comboCount + 'x' + comboSystem.comboMultiplier.toFixed(1));
// Color intensity increases with combo level
var comboColors = [0xFFFF00, 0xFFCC00, 0xFF9900, 0xFF6600, 0xFF3300, 0xFF0000];
comboText.fill = comboColors[Math.min(comboSystem.comboLevel - 1, 5)];
// Scale effect based on combo
tween(comboText, {
scaleX: 1.0 + comboSystem.comboLevel * 0.1,
scaleY: 1.0 + comboSystem.comboLevel * 0.1
}, {
duration: 100,
easing: tween.easeOut
});
} else {
comboText.setText('COMBO: 0x1.0');
comboText.fill = 0xFFFF00;
comboText.scaleX = 1.0;
comboText.scaleY = 1.0;
}
}
// Update health bar
var healthPercent = fortressCurrentHealth / fortressMaxHealth;
healthBarFill.width = 200 * healthPercent;
if (healthPercent > 0.6) {
healthBarFill.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBarFill.tint = 0xFFFF00;
} else {
healthBarFill.tint = 0xFF0000;
}
// Update shield button
if (shieldCooldown > 0) {
var secondsLeft = Math.ceil(shieldCooldown / 60);
shieldButton.setText('Escudo (' + secondsLeft + 's)');
shieldButton.fill = 0x666666;
} else {
shieldButton.setText('Escudo (Listo)');
shieldButton.fill = 0x00FFFF;
}
// Spawn enemies for current wave - only if in desert scenario
if (currentScenario === 'desert' && enemiesSpawned < enemiesInWave && LK.ticks % spawnDelay === 0) {
spawnEnemy();
enemiesSpawned++;
}
// Check if wave is complete
if (enemiesSpawned >= enemiesInWave && enemies.length === 0) {
if (waveDelay > 0) {
waveDelay--;
} else {
// Start next wave
currentWave++;
enemiesInWave = Math.min(5 + currentWave * 2, 20);
enemiesSpawned = 0;
waveDelay = 180;
spawnDelay = Math.max(30, 60 - currentWave * 2);
}
}
// Update defensive units
for (var i = 0; i < defensiveUnits.length; i++) {
defensiveUnits[i].update();
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
// Auto-disappear enemies after they've been alive for too long (except bosses)
if (!enemy.isBoss && !enemy.isMiniBoss && !enemy.isSecretBoss) {
// Initialize spawn time if not set
if (enemy.spawnTime === undefined) {
enemy.spawnTime = LK.ticks;
}
// Auto-disappear after 30 seconds (1800 ticks at 60fps)
if (LK.ticks - enemy.spawnTime > 1800) {
enemy.markForDestroy = true;
}
}
if (enemy.markForDestroy) {
enemy.destroy();
enemies.splice(i, 1);
}
}
// Check if we're in boss mode (any boss or mini boss exists)
var inBossMode = false;
for (var j = 0; j < enemies.length; j++) {
if (enemies[j].isBoss || enemies[j].isMiniBoss || enemies[j].isSecretBoss) {
inBossMode = true;
break;
}
}
// Check for alerta warning when 5 enemies enter fortress (only if not in boss mode)
if (enemiesEntered === 5 && LK.ticks - lastAlarmTime >= alarmCooldown && !inBossMode) {
lastAlarmTime = LK.ticks;
// Play danger sound effect
LK.getSound('alerta').play();
// Create alerta overlay that appears quickly and fades out - same size as trollface
var alertaOverlay = LK.getAsset('Alerta', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 20,
scaleY: 15
});
alertaOverlay.x = 1024;
alertaOverlay.y = 1366;
game.addChild(alertaOverlay);
// Quick flash effect - instantly visible then fade out
alertaOverlay.alpha = 1.0;
tween(alertaOverlay, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
alertaOverlay.destroy();
}
});
}
// Check for trollface trigger every 10 enemies killed (10, 20, 30, etc.) (only if not in boss mode)
if (enemiesKilled > 0 && enemiesKilled % 10 === 0 && !trollfaceShown && !inBossMode) {
trollfaceShown = true;
// Create trollface overlay that starts small and expands
trollfaceOverlay = LK.getAsset('Trollface', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0
});
trollfaceOverlay.x = 1024;
trollfaceOverlay.y = 1366;
game.addChild(trollfaceOverlay);
// Play trollface sound effect when appearing
LK.getSound('trollface').play();
// Tornado expanding effect when appearing
tween(trollfaceOverlay, {
scaleX: 20,
scaleY: 15,
rotation: Math.PI * 2
}, {
duration: 1000,
easing: tween.easeOut
});
// Show trollface for 1 second to match shorter sound, then animate it away
LK.setTimeout(function () {
if (trollfaceOverlay) {
// Play trollface sound effect when disappearing
LK.getSound('trollface').play();
// Swirling and shrinking animation - shorter to match sound
tween(trollfaceOverlay, {
rotation: Math.PI * 4,
// 2 full rotations for faster animation
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (trollfaceOverlay) {
trollfaceOverlay.destroy();
trollfaceOverlay = null;
}
}
});
}
}, 1000);
}
// Check for boss spawn every 10 enemies killed
if (enemiesKilled >= lastBossSpawnKillCount + 10) {
lastBossSpawnKillCount = enemiesKilled;
// Spawn main boss only once (first time reaching 10 kills) and only if not already spawned
if (enemiesKilled === 10 && !bossSpawned) {
var mainBoss = new Boss();
mainBoss.x = 1024; // Center top
mainBoss.y = -100;
// Start with dramatic entrance - completely invisible and small
mainBoss.scaleX = 0.0;
mainBoss.scaleY = 0.0;
mainBoss.alpha = 0.0;
enemies.push(mainBoss);
game.addChild(mainBoss);
bossSpawned = true; // Mark boss as spawned
// ============================================================================
// COMPREHENSIVE BOSS ENTRANCE ANIMATION SYSTEM
// This section creates an epic, multi-layered entrance sequence for the ogre boss
// featuring dramatic visual effects, sound design, and choreographed animations
// that build tension and create an memorable boss encounter experience.
// ============================================================================
// PHASE 1: DRAMATIC WARNING AND ENVIRONMENT PREPARATION
// Create ominous atmosphere with dark screen overlay and warning text
LK.effects.flashScreen(0x000000, 800); // Extended dark flash for dramatic buildup
var warningText = new Text2('¡EL JEFE OGRO SE ACERCA!', {
size: 120,
fill: 0xFF0000
});
warningText.anchor.set(0.5, 0.5);
warningText.x = 1024;
warningText.y = 1366;
warningText.alpha = 0;
game.addChild(warningText);
// Dramatic warning text entrance with pulsing intensity animation
tween(warningText, {
alpha: 1.0,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Secondary pulse for emphasis
tween(warningText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Third pulse with red flash
LK.effects.flashObject(warningText, 0xFF0000, 500);
}
});
}
});
// PHASE 2: ENVIRONMENTAL DISTURBANCE AND MUSIC TRANSITION
LK.setTimeout(function () {
// Warning text dramatic exit with shrinking and fading
tween(warningText, {
alpha: 0,
scaleX: 0,
scaleY: 0,
rotation: Math.PI
}, {
duration: 600,
easing: tween.easeIn,
onFinish: function onFinish() {
warningText.destroy();
}
});
// Dramatic boss music entrance with fade-in crescendo
LK.playMusic('Bossogro', {
fade: {
start: 0,
end: 1,
duration: 1200
}
});
// PHASE 3: GROUND TREMORS AND SEISMIC ACTIVITY SIMULATION
LK.setTimeout(function () {
// Intensifying ground shake sequence with multiple impact waves
for (var shake = 0; shake < 5; shake++) {
LK.setTimeout(function () {
// Varied screen flash colors for realistic ground disturbance
var shakeColors = [0x8B4513, 0x654321, 0x3E2723, 0x5D4037];
LK.effects.flashScreen(shakeColors[Math.floor(Math.random() * shakeColors.length)], 250);
// Multiple ground impact explosion effects with randomized positioning
for (var impact = 0; impact < 7; impact++) {
var groundShake = new Explosion(300 + Math.random() * 1448,
// Wider spread across entire screen
fortress.y + (Math.random() - 0.5) * 400 // Varied vertical positioning around fortress
);
// Vary explosion sizes for realistic seismic effect
tween(groundShake, {
scaleX: 0.8 + Math.random() * 1.4,
scaleY: 0.8 + Math.random() * 1.4
}, {
duration: 400,
easing: tween.easeOut
});
game.addChild(groundShake);
}
}, shake * 200); // Faster succession for building intensity
}
// PHASE 4: EPIC BOSS MATERIALIZATION SEQUENCE
LK.setTimeout(function () {
// Boss visibility activation with mystical appearance effect
mainBoss.alpha = 1.0;
// MULTI-STAGE DRAMATIC ENTRANCE CHOREOGRAPHY
// Stage 1: Emergence from underground with elastic bounce
tween(mainBoss, {
scaleX: 2.2,
scaleY: 2.2,
y: mainBoss.y + 80,
rotation: 0.1
}, {
duration: 1500,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Stage 2: Intimidation roar with massive size increase
tween(mainBoss, {
scaleX: 3.2,
scaleY: 3.2,
rotation: -0.1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Stage 3: Final settling with authoritative presence
tween(mainBoss, {
scaleX: 2.0,
scaleY: 2.0,
rotation: 0,
y: mainBoss.y - 30
}, {
duration: 800,
easing: tween.bounceOut
});
// Create intimidation aura effect around boss
for (var aura = 0; aura < 6; aura++) {
var auraRing = new Explosion(mainBoss.x, mainBoss.y);
tween(auraRing, {
scaleX: 2.5 + aura * 0.5,
scaleY: 2.5 + aura * 0.5,
alpha: 0.3
}, {
duration: 1000 + aura * 200,
easing: tween.easeOut
});
game.addChild(auraRing);
}
}
});
}
});
// PHASE 5: ENVIRONMENTAL DEVASTATION EFFECTS
// Massive ground impact at boss epicenter
var massiveImpact = new Explosion(mainBoss.x, mainBoss.y + 200);
tween(massiveImpact, {
scaleX: 5.0,
scaleY: 5.0
}, {
duration: 1000,
easing: tween.easeOut
});
game.addChild(massiveImpact);
// PHASE 6: CHROMATIC LIGHT SHOW AND SCREEN EFFECTS
// Cascading color explosion sequence
var flashColors = [0xFF0000, 0xFF4500, 0xFFAA00, 0xFF8800, 0xFF6600];
for (var colorFlash = 0; colorFlash < flashColors.length; colorFlash++) {
(function (color, delay) {
LK.setTimeout(function () {
LK.effects.flashScreen(color, 600);
}, delay);
})(flashColors[colorFlash], colorFlash * 300);
}
// PHASE 7: DEBRIS FIELD AND ATMOSPHERIC PARTICLES
// Extended debris cloud system with varied timing
for (var debris = 0; debris < 12; debris++) {
LK.setTimeout(function () {
var debrisEffect = new Explosion(mainBoss.x + (Math.random() - 0.5) * 600,
// Wider debris field
mainBoss.y + Math.random() * 400);
// Randomized debris characteristics
tween(debrisEffect, {
scaleX: 0.5 + Math.random() * 1.5,
scaleY: 0.5 + Math.random() * 1.5,
rotation: Math.random() * Math.PI * 2
}, {
duration: 600 + Math.random() * 400,
easing: tween.easeOut
});
game.addChild(debrisEffect);
}, debris * 80 + Math.random() * 120); // Staggered timing with randomization
}
// PHASE 8: AUDIO CLIMAX AND FINAL PRESENCE ESTABLISHMENT
// Boss entrance roar with echo effect simulation
LK.getSound('jefesecreto').play();
LK.setTimeout(function () {
// Secondary roar for echo effect
LK.getSound('jefesecreto').play();
}, 400);
}, 1200); // Synchronized timing with ground tremors
}, 1000); // Coordinated with music transition
}, 1400); // Allows warning text full dramatic impact
// ============================================================================
// END OF COMPREHENSIVE BOSS ENTRANCE ANIMATION SYSTEM
// ============================================================================
}
// Only spawn mini bosses if we haven't reached the limit of 20
if (totalMiniBossesSpawned < maxMiniBossesToSpawn) {
// Spawn horde of mini bosses (4 mini bosses each time)
// Spawn mini boss from top left
var miniBoss1 = new MiniBoss();
miniBoss1.x = 200;
miniBoss1.y = -50;
enemies.push(miniBoss1);
game.addChild(miniBoss1);
totalMiniBossesSpawned++;
// Spawn mini boss from top right
var miniBoss2 = new MiniBoss2();
miniBoss2.x = 1800;
miniBoss2.y = -50;
enemies.push(miniBoss2);
game.addChild(miniBoss2);
totalMiniBossesSpawned++;
// Spawn mini boss from left side
var miniBoss3 = new MiniBoss();
miniBoss3.x = -50;
miniBoss3.y = Math.random() * 1000 + 500;
enemies.push(miniBoss3);
game.addChild(miniBoss3);
totalMiniBossesSpawned++;
// Spawn mini boss from right side
var miniBoss4 = new MiniBoss2();
miniBoss4.x = 2098;
miniBoss4.y = Math.random() * 1000 + 500;
enemies.push(miniBoss4);
game.addChild(miniBoss4);
totalMiniBossesSpawned++;
// Play special sound effect
LK.getSound('jefesecreto').play();
// Visual effect for boss appearance
LK.effects.flashScreen(0xFF8800, 800);
}
}
// Check for second secret boss spawn condition (3 spearmen + 1 archer)
if (secondSecretBossConditionMet && !secondSecretBossSpawned) {
secondSecretBossSpawned = true;
trackingActive2 = false; // Stop tracking once boss spawns
// Clear all existing enemies when secret boss appears
for (var j = enemies.length - 1; j >= 0; j--) {
var existingEnemy = enemies[j];
existingEnemy.destroy();
enemies.splice(j, 1);
}
// Create 3 spearmen and 1 archer as the second secret boss encounter
// Spearman 1
var spearman1 = new Enemy('strongEnemy');
spearman1.x = 500;
spearman1.y = -100;
enemies.push(spearman1);
game.addChild(spearman1);
// Spearman 2
var spearman2 = new Enemy('strongEnemy');
spearman2.x = 1024;
spearman2.y = -100;
enemies.push(spearman2);
game.addChild(spearman2);
// Spearman 3
var spearman3 = new Enemy('strongEnemy');
spearman3.x = 1548;
spearman3.y = -100;
enemies.push(spearman3);
game.addChild(spearman3);
// Archer (positioned slightly back)
var archerBoss = new Enemy('strongEnemy');
archerBoss.x = 1024;
archerBoss.y = -250;
enemies.push(archerBoss);
game.addChild(archerBoss);
// Play special entrance effects
LK.effects.flashScreen(0x00FF00, 1000);
LK.getSound('jefesecreto').play();
// Visual effects on all boss units
for (var boss_i = 0; boss_i < 4; boss_i++) {
(function (enemyUnit, index) {
LK.setTimeout(function () {
tween(enemyUnit, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyUnit, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(enemyUnit, 0x00FF00, 300);
}, index * 150);
})([spearman1, spearman2, spearman3, archerBoss][boss_i], boss_i);
}
}
// Check for secret boss spawn condition
if (secretBossConditionMet && !secretBossSpawned) {
// Function to create one zigzag segment
var _performZigzag = function performZigzag() {
if (zigzagCount >= maxZigzags) {
// Final thrust attack
tween(attackCharacter, {
x: bossTargetX,
y: bossTargetY - 50
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
// Thrust impact effect
LK.effects.flashObject(secretBoss, 0xFFFFFF, 300);
LK.getSound('Espada').play();
// Shrinking animation for character after attack
tween(attackCharacter, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
attackCharacter.destroy();
}
});
}
});
return;
}
// Calculate zigzag offset
var progressRatio = zigzagCount / maxZigzags;
var baseX = zigzagStartX + (bossTargetX - zigzagStartX) * progressRatio;
var baseY = zigzagStartY + (bossTargetY - zigzagStartY) * progressRatio;
var zigzagOffset = (zigzagCount % 2 === 0 ? 1 : -1) * 150;
// Move to zigzag position
tween(attackCharacter, {
x: baseX + zigzagOffset,
y: baseY
}, {
duration: 250,
easing: tween.easeInOut,
onFinish: function onFinish() {
zigzagCount++;
_performZigzag();
}
});
};
secretBossSpawned = true;
trackingActive = false; // Stop tracking once boss spawns
// Clear all existing enemies when secret boss appears
for (var j = enemies.length - 1; j >= 0; j--) {
var existingEnemy = enemies[j];
existingEnemy.destroy();
enemies.splice(j, 1);
}
// Spawn secret boss
var secretBoss = new SecretBoss();
// Spawn from a dramatic location (top center)
secretBoss.x = 1024;
secretBoss.y = -100;
enemies.push(secretBoss);
game.addChild(secretBoss);
// Create character for zigzag attack animation
var attackCharacter = LK.getAsset('spearman', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
scaleY: 1.5
});
attackCharacter.x = fortress.x;
attackCharacter.y = fortress.y;
game.addChild(attackCharacter);
// Zigzag attack animation sequence
var zigzagStartX = fortress.x;
var zigzagStartY = fortress.y;
var bossTargetX = secretBoss.x;
var bossTargetY = secretBoss.y + 100;
var totalDistance = Math.sqrt(Math.pow(bossTargetX - zigzagStartX, 2) + Math.pow(bossTargetY - zigzagStartY, 2));
var zigzagCount = 0;
var maxZigzags = 6;
_performZigzag();
// Create healing button when secret boss appears - same design as shield button
healButton = new Text2('CURAR (Listo)', {
size: 24,
fill: 0x00FF00
});
healButton.anchor.set(0.5, 1);
healButton.x = 200; // Position next to shield button
healButton.y = -100;
LK.gui.bottom.addChild(healButton);
// Add healing button functionality
healButton.down = function () {
if (healCooldown <= 0) {
// Heal fortress for 11 points
var healAmount = 11;
fortressCurrentHealth = Math.min(fortressCurrentHealth + healAmount, fortressMaxHealth);
fortress.health = fortressCurrentHealth;
// Set cooldown
healCooldown = healMaxCooldown;
// Play heal sound
LK.getSound('heal').play();
// Visual effect on fortress
LK.effects.flashObject(fortress, 0x00FF00, 800);
// Visual effect on button
tween(healButton, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(healButton, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
// Play special sound effect
LK.getSound('jefesecreto').play();
// Start secret boss music
LK.playMusic('Secretboos');
// Visual effect for secret boss appearance
LK.effects.flashScreen(0xFF00FF, 1000);
}
// Spawn scenario cube every 20 seconds
if (LK.ticks - lastCubeSpawn >= cubeSpawnInterval && !scenarioChangeActive) {
lastCubeSpawn = LK.ticks;
cubeSpawnInterval = 1200; // 20 seconds at 60fps
var cube = new ScenarioCube();
// Spawn in safe area away from fortress and away from top left UI
var spawnX,
spawnY,
attempts = 0;
do {
spawnX = Math.random() * 1500 + 274;
spawnY = Math.random() * 1500 + 300;
attempts++;
// Avoid top left 0-200 x 0-200 area (UI), and avoid fortress
} while ((Math.sqrt(Math.pow(spawnX - fortress.x, 2) + Math.pow(spawnY - fortress.y, 2)) < 300 || spawnX < 200 && spawnY < 200) && attempts < 20);
cube.x = spawnX;
cube.y = spawnY;
scenarioCubes.push(cube);
game.addChild(cube);
}
// Update scenario cubes
for (var i = scenarioCubes.length - 1; i >= 0; i--) {
var cube = scenarioCubes[i];
cube.update();
if (cube.markForDestroy) {
cube.destroy();
scenarioCubes.splice(i, 1);
}
}
// Spawn airplane occasionally in alternate scenario only - only if no airplane exists
if (currentScenario === 'alternate') {
var airplaneExists = false;
for (var k = 0; k < enemies.length; k++) {
if (enemies[k].speed !== undefined && enemies[k].direction !== undefined) {
airplaneExists = true;
break;
}
}
if (!airplaneExists && LK.ticks % (Math.floor(Math.random() * 300) + 300) === 0) {
var airplane = new Avion();
game.addChild(airplane);
// Store airplanes in enemies array for easy cleanup
enemies.push(airplane);
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
projectile.update();
if (projectile.markForDestroy) {
projectile.destroy();
projectiles.splice(i, 1);
}
}
// Nuclear Defense System and Missile rain variables are now defined at the top of Game Code section
// Trigger Nuclear Defense System (every 30 seconds after wave 5, not during boss fights)
if (!nuclearDefenseActive && currentWave >= 5 && !inBossMode && !secretBossSpawned && LK.ticks - lastNuclearAttackTime >= nuclearAttackCooldown && gameState === 'playing') {
lastNuclearAttackTime = LK.ticks;
nuclearDefenseActive = true;
// Clear any existing cubes
for (var ndc_i = nuclearDefenseCubes.length - 1; ndc_i >= 0; ndc_i--) {
if (nuclearDefenseCubes[ndc_i] && typeof nuclearDefenseCubes[ndc_i].destroy === "function") {
nuclearDefenseCubes[ndc_i].destroy();
}
nuclearDefenseCubes.splice(ndc_i, 1);
}
// Spawn 4 nuclear defense cubes in different corners/areas
var cubePositions = [{
x: 300,
y: 300
},
// Top left area
{
x: 1748,
y: 300
},
// Top right area
{
x: 300,
y: 2000
},
// Bottom left area
{
x: 1748,
y: 2000
} // Bottom right area
];
for (var pos_i = 0; pos_i < cubePositions.length; pos_i++) {
var nuclearCube = new NuclearDefenseCube();
nuclearCube.x = cubePositions[pos_i].x;
nuclearCube.y = cubePositions[pos_i].y;
nuclearDefenseCubes.push(nuclearCube);
game.addChild(nuclearCube);
}
// Play warning sound
LK.getSound('alerta').play();
// Show warning message
var warningText = new Text2('¡ALERTA NUCLEAR! ¡TOCA LOS CUADROS!', {
size: 80,
fill: 0xFF0000
});
warningText.anchor.set(0.5, 0.5);
warningText.x = 1024;
warningText.y = 200;
game.addChild(warningText);
// Animate warning text
tween(warningText, {
alpha: 0
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
warningText.destroy();
}
});
}
// Update nuclear defense cubes
for (var ndc_j = nuclearDefenseCubes.length - 1; ndc_j >= 0; ndc_j--) {
var nuclearCube = nuclearDefenseCubes[ndc_j];
if (nuclearCube) {
nuclearCube.update();
if (nuclearCube.markForDestroy) {
nuclearCube.destroy();
nuclearDefenseCubes.splice(ndc_j, 1);
}
}
}
// Check if all cubes are cleared to end nuclear defense phase
if (nuclearDefenseActive && nuclearDefenseCubes.length === 0) {
nuclearDefenseActive = false;
}
// Randomly trigger missile rain (1/1200 chance per frame, not during boss/secret boss)
if (!missileRainActive && !inBossMode && !secretBossSpawned && Math.random() < 1 / 1200 && gameState === 'playing') {
missileRainActive = true;
missileRainTimer = 0;
if (missileRainInstance && typeof missileRainInstance.destroy === "function") {
missileRainInstance.destroy();
}
missileRainInstance = new MissileRain();
game.addChild(missileRainInstance);
}
// Update missile rain if active
if (missileRainActive && missileRainInstance) {
missileRainInstance.update();
if (!missileRainInstance.active) {
missileRainActive = false;
if (typeof missileRainInstance.destroy === "function") {
missileRainInstance.destroy();
}
missileRainInstance = null;
}
}
// Update explosions - find and update all explosion objects
var allChildren = game.children.slice();
for (var i = allChildren.length - 1; i >= 0; i--) {
var child = allChildren[i];
if (child.markForDestroy) {
child.destroy();
}
}
};
Una lanza para el guerrero. In-Game asset. 2d. High contrast. No shadows
Flecha. In-Game asset. 2d. High contrast. No shadows
Trollface. In-Game asset. 2d. High contrast. No shadows
Personaje aterrador. In-Game asset. 2d. High contrast. No shadows
Ogro. In-Game asset. 2d. High contrast. No shadows
Ogro músculoso y gigante. In-Game asset. 2d. High contrast. No shadows
Lucky block. In-Game asset. 2d. High contrast. No shadows
Torres gemelas. In-Game asset. 2d. High contrast. No shadows
Avión de color gris sin ruedas 8 bit. In-Game asset. 2d. High contrast. No shadows
Explosión efecto. In-Game asset. 2d. High contrast. No shadows
Cuadrado con signo de !. In-Game asset. 2d. High contrast. No shadows
Imagien bde un jefe terrorífico humanoide de color negro. In-Game asset. 2d. High contrast. No shadows