User prompt
Arregla los errores para poder jugar plis
User prompt
Please fix the bug: 'ReferenceError: currentScenario is not defined' in or related to this line: 'if (currentScenario === 'desert' && enemiesSpawned < enemiesInWave && LK.ticks % spawnDelay === 0 && !inCaballeroCombat && gameState === 'playing') {' Line Number: 4479
User prompt
Retira el bloque de cambio de escenario y todo el mapa de ester egg
User prompt
Quiero que quites el cubo que aparece cada cierto tiempo el que cambia el escenario
User prompt
Please fix the bug: 'ReferenceError: trollfaceOverlay is not defined' in or related to this line: 'if (trollfaceOverlay) {' Line Number: 3630
User prompt
Quiero que borres el Lucky block y todo el ester egg de las Torre y prepares un nuevo ester egg
User prompt
El caballero desapareció arreglalo
User prompt
El juego está lleno de errores s arreglar y claramente no pudiste cumplir con la orden ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Nooooooo se bugeo todo volvieron a spawnear las tropas y sigues sin poder arreglar eso tan simple hace lo que sea posible para que no pase ya sea creando algo necesario o elimando algo no tan necesario para poder arreglar y que el juego termine con la cinemática tras la derrota del caballero y que no continue de ninguna manera ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Volvió a suseder arregla esto con cualquier medio posible y quiero que ahora tu pienses mejoras para que el juego prospere esta es mi última actualización de hoy
User prompt
Volvió a suspender arregla esto con cualquier medio posible y quiero que ahora tu pienses mejoras para que el jueves prospere esta es mi última actualización de hoy ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ahora me gustó más pero sigue pasando lo mismo el juego siguen continuando arregla esto para hacer que el juego acabe luego de derrotarlo porfavor a y me di cuenta de un bug importante y es que las tropas ya no tiene col down así que arreglalo a como estaban antes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Osea si quiero que desaparzca al derrotarlo pero cuando se le acabe la vida y así activa el final con la cinemática y el juego no continua más ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ook encontré un bug importante el caballero ahora queda estático sin atacar quiero que los turnos sea con más tiempo pero tampoco tanto lo justo para pensar pero luego sea turno del caballero automáticamente y me di cuenta que el caballero desaparece de la nada y el juego continua con normalidad cosas que no debería pasar usa todo tus mecanismos para arreglar este problema ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Otra cosa es que el juego continua y no aparece la cinematica del final como te pedi así que arregla eso y otros errores que consideres importantes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Hubiera varios errores a mejorar por ejemplo que los quick Time event aparecieron juntos cuando tienen que aparecer los 3 en distintos tiempos el caballero no respeta del todos los turnos y estos a la vez sin muy rápidos podrías agregar diálogos a la azar pero con sentido del caballero luego de su turno al estilo undertale ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
a por cierto quiero que estas mecanicas se repitan a lo largo de la pelea y se intercalen para que no sea tan repetitivo osea que no tenga un patron definida luego de los primeros 3 ataques,luego quiero que le pongas la nueva musica del caballeroataque para que suene durante el combate luego tras derrotar el caballero nos se generaran mas enemigos ni nada y aparecera en toda la pantalla un texto que diga continuara... terminando el juego con la victoria ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok ahora quiero explicarte la mecanicas que se usaran para defender los ataques del caballero estan seran solo 3, la primera es escudo esta te dara un tiempo de advertencida de ataque del caballero, asi pudes desplegar el escudo para salvarte la segunda es quick time event esta tiene que tocar los cuadritos que apareceran 3 en difrentes tiemos y rapidamente para frustrar el ataque del caballero, y la ultima es solo tirar el ataque de la torre antes que llege el caballero nada mas ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok vamos bien pero ahora quiero que durante este combate no se pueda invocar ninguna tropa para asi cumplir la rregla del 1 v 1
User prompt
quiero que para substituir el problema de que no habran tropas para defender la torre la torre pueda tirar un ataque con un boton en su turno
User prompt
ok me gusto pero quiero que nigun personaje ni enemigo este en la zona a la hora del combate solo la torre y el caballero tampoco el lancero,el arquero,y la caballeria a y por cierto quiero que el amuento de la vida de la torre sea solo a la hora de la batalla con el caballero
User prompt
quiero que mientras la pelea del jefe se de no ocuran las bombardeos y quiero que el jefe y la torre tengan mas vida para asi hacer la pelea mas larga algo de 12 minutos si haces todo bien, uptdate y arregla bugs ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
quiero que la pelea contra el caballero sea un 1 v1 contra la torre por turnos con difrentes mecanicas para evadir sus ataques como escudo quick time event, o ataques ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
quiero que el nuevo jefe caballero actue difrente a los demas personajes y enemigos que tenga una mecanica propia ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ok ahora quiero agreges este nuevo nivelo tras matar el jefe ogro el jefe "caballero" tras matar al ogro los enemigos no se generan a esepcion de este nuevo jefe empieza a sonar la musica "caballero" y el empieza acercarse lenamente llegando a ser visible y ahi nos dice (hola humano) y empieza a sonar "caballeroataque" y la batalla se vuelve un 1 v 1 entre el caballero y lka torre el caballero tira bolas de fuego ↪💡 Consider importing and using the following plugins: @upit/storage.v1, @upit/tween.v1
/****
* 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
// Pathfinding properties for boss
self.waypoints = [];
self.currentWaypointIndex = 0;
self.pathRecalculateTimer = 0;
self.pathRecalculateInterval = 150; // Recalculate path every 2.5 seconds (boss recalculates less frequently)
self.hasValidPath = false;
// Initial path calculation
self.recalculateBossPath = function () {
pathfindingSystem.updateObstacles();
var newPath = pathfindingSystem.findPath(self.x, self.y, fortress.x, fortress.y);
if (newPath && newPath.length > 0) {
self.waypoints = newPath;
self.currentWaypointIndex = 0;
self.hasValidPath = true;
} else {
self.hasValidPath = false;
}
self.pathRecalculateTimer = 0;
};
self.update = function () {
// Update path recalculation timer
self.pathRecalculateTimer++;
if (self.pathRecalculateTimer >= self.pathRecalculateInterval) {
self.recalculateBossPath();
}
// Boss Weakening Mechanic - take increased damage when surrounded
var nearbyUnits = 0;
for (var unit_i = 0; unit_i < defensiveUnits.length; unit_i++) {
var unit = defensiveUnits[unit_i];
var distToBoss = Math.sqrt(Math.pow(unit.x - self.x, 2) + Math.pow(unit.y - self.y, 2));
if (distToBoss <= 400) {
nearbyUnits++;
}
}
// Increased damage vulnerability when surrounded by 3+ units
self.damageMultiplier = nearbyUnits >= 3 ? 1.5 : 1.0;
// Visual indication of weakened state
if (nearbyUnits >= 3 && LK.ticks % 30 === 0) {
LK.effects.flashObject(self, 0x00FF00, 150);
}
// 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
}
}
}
// Boss moves toward fortress using pathfinding with improved mechanics
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Boss special attack when in range
if (distance <= 450 && distance > 250 && LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
self.lastSpecialAttack = LK.ticks;
// Boss special power - weakens fortress with scaling damage
var specialDamage = 20 + Math.floor(currentWave / 3); // Increases with wave
LK.effects.flashObject(self, 0xFF00FF, 500);
fortress.takeDamage(specialDamage);
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 > 320) {
// Not at fortress yet - move closer using pathfinding
if (self.hasValidPath && self.currentWaypointIndex < self.waypoints.length) {
var waypoint = self.waypoints[self.currentWaypointIndex];
var waypointDist = Math.sqrt(Math.pow(waypoint.x - self.x, 2) + Math.pow(waypoint.y - self.y, 2));
// Move toward current waypoint
if (waypointDist > 30) {
var waypointDx = waypoint.x - self.x;
var waypointDy = waypoint.y - self.y;
self.x += waypointDx / waypointDist * self.speed;
self.y += waypointDy / waypointDist * self.speed;
} else {
// Reached waypoint, move to next
self.currentWaypointIndex++;
}
} else {
// Fallback to direct movement if no valid path
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
} else {
// Boss within attack range - damage fortress and stay engaged
fortress.takeDamage(8);
enemiesEntered++; // Count boss entry
self.markForDestroy = true;
}
};
self.takeDamage = function (damage) {
var actualDamage = damage * (self.damageMultiplier || 1.0);
self.health -= actualDamage;
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;
// DO NOT set gameWon here - boss defeat does not trigger victory
self.markForDestroy = true;
}
};
return self;
});
//{QTE_132}
var Caballero = Container.expand(function () {
var self = Container.call(this);
//{caballero_1}
var bossGraphics = self.attachAsset('caballero', {
//{caballero_2}
anchorX: 0.5,
//{caballero_3}
anchorY: 1.0,
//{caballero_4}
scaleX: 3.0,
//{caballero_5}
scaleY: 3.0 //{caballero_6}
}); //{caballero_7}
// Ensure graphics are visible
bossGraphics.visible = true;
bossGraphics.alpha = 1.0;
// Add health bar background//{caballero_8}
var healthBarBg = self.attachAsset('HealthBarBg', {
//{caballero_9}
anchorX: 0.5,
//{caballero_10}
anchorY: 0.5 //{caballero_11}
}); //{caballero_12}
healthBarBg.x = 0; //{caballero_13}
healthBarBg.y = -400;
// Add health bar fill//{caballero_14}
var healthBarFill = self.attachAsset('HealthBarFill', {
//{caballero_15}
anchorX: 0,
//{caballero_16}
anchorY: 0.5 //{caballero_17}
}); //{caballero_18}
healthBarFill.x = -200; //{caballero_19}
healthBarFill.y = -400;
self.health = 3600; // Extended fight: 600 ticks per turn * 6 turns average = 3600 health for ~12 min//{caballero_20}
self.maxHealth = 3600; //{caballero_21}
self.speed = 0.4; // Slow but methodical//{caballero_22}
self.damage = 8; // Reduced for extended 12-minute 1v1 combat//{caballero_23}
self.coinValue = 150; // Excellent reward//{caballero_24}
self.enemyType = 'caballero'; //{caballero_25}
self.isCaballero = true; //{caballero_26}
// Turn-based 1v1 combat system
self.combatPhase = 'idle'; // 'idle', 'player_turn', 'enemy_turn', 'defeated'
self.turnTimer = 0;
self.turnDuration = 400; // ~6.67 seconds per turn at 60fps - more time to react
self.qteTimer = 0;
self.qteDuration = 180; // 3 seconds for QTE
self.shieldPressed = false;
self.attackCooldown = 0;
self.defenseMode = false;
self.attackCount = 0; // Track number of attacks for randomization
// QTE Manager for the Quick Time Event mechanic//{caballero_QTE_1}
self.qteManager = new QTEManager(self); //{caballero_QTE_2}
// Turn start - begins with player turn
self.startPlayerTurn = function () {
self.combatPhase = 'player_turn';
self.turnTimer = 0;
self.qteActive = true;
self.qteTimer = 0;
self.shieldPressed = false;
self.defenseMode = false;
// Show player turn indicator
LK.effects.flashObject(self, 0x00FF00, 400);
// Start the Quick Time Event with 3 squares//{caballero_QTE_3}
self.qteManager.startQTE(); //{caballero_QTE_4}
};
self.startEnemyTurn = function () {
self.combatPhase = 'enemy_turn';
self.turnTimer = 0;
self.qteActive = false;
// After first 3 attacks (attacks 0, 1, 2), randomize to avoid patterns
if (self.attackCount >= 3) {
// Fully random selection with 3 options
var attackOptions = ['attack', 'fireball', 'mixed'];
self.attackType = attackOptions[Math.floor(Math.random() * 3)];
} else {
// First 3 attacks: balanced 50/50
self.attackType = Math.random() < 0.5 ? 'attack' : 'fireball';
}
self.attackCount++;
// Show enemy turn indicator
LK.effects.flashObject(self, 0xFF0000, 400);
// Display random Caballero dialogue
if (caballeroDialogueSystem) {
//{caballero_dialogue_turn_1}
var randomDialogue = caballeroDialogueSystem.getRandomDialogue();
//{caballero_dialogue_turn_2}
caballeroDialogueSystem.displayDialogue(randomDialogue);
//{caballero_dialogue_turn_3}
}
//{caballero_dialogue_turn_4}
};
self.executeCaballeroAttack = function () {
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (self.attackType === 'attack') {
// Direct melee charge
var chargeTarget = {
x: fortress.x,
y: fortress.y
};
tween(self, {
x: chargeTarget.x - 200,
y: chargeTarget.y
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
// Deal damage based on fortress defense
var damageDealt = self.defenseMode ? 15 : 35;
fortress.takeDamage(damageDealt);
LK.effects.flashScreen(0xFF6600, 500);
LK.getSound('Espada').play();
// Return to center
tween(self, {
x: 1024,
y: 400
}, {
duration: 300,
easing: tween.easeOut
});
}
});
} else if (self.attackType === 'fireball') {
// Fireball pattern attack - 3 fireballs
var baseAngle = Math.atan2(dy, dx);
for (var fb = 0; fb < 3; fb++) {
(function (index) {
LK.setTimeout(function () {
if (self.markForDestroy) return;
var spreadOffset = (index - 1) * 0.3;
var targetAngle = baseAngle + spreadOffset;
var targetDistance = 400;
var targetX = self.x + Math.cos(targetAngle) * targetDistance;
var targetY = self.y + Math.sin(targetAngle) * targetDistance;
var fireballTarget = {
x: targetX,
y: targetY,
takeDamage: function takeDamage(dmg) {
if (fortress) {
var damageDealt = self.defenseMode ? Math.max(1, dmg - 10) : dmg;
fortress.takeDamage(damageDealt);
}
}
};
var fireball = new Fireball(self.x, self.y, fireballTarget, 4); // Reduced from 20 for extended combat
projectiles.push(fireball);
game.addChild(fireball);
LK.getSound('Poder').play();
LK.effects.flashObject(self, 0xFF6600, 150);
}, index * 150);
})(fb);
}
} else if (self.attackType === 'mixed') {
// Mixed attack: 1 melee charge followed by 2 fireballs
var chargeTarget = {
x: fortress.x,
y: fortress.y
};
tween(self, {
x: chargeTarget.x - 200,
y: chargeTarget.y
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
// Melee damage
var damageDealt = self.defenseMode ? 15 : 35;
fortress.takeDamage(damageDealt);
LK.effects.flashScreen(0xFF6600, 500);
LK.getSound('Espada').play();
// Return to center and launch fireballs
tween(self, {
x: 1024,
y: 400
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Launch 2 fireballs after melee
var baseAngle = Math.atan2(dy, dx);
for (var mixFb = 0; mixFb < 2; mixFb++) {
(function (index) {
LK.setTimeout(function () {
if (self.markForDestroy) return;
var spreadOffset = (index - 0.5) * 0.4;
var targetAngle = baseAngle + spreadOffset;
var targetDistance = 400;
var targetX = self.x + Math.cos(targetAngle) * targetDistance;
var targetY = self.y + Math.sin(targetAngle) * targetDistance;
var fireballTarget = {
x: targetX,
y: targetY,
takeDamage: function takeDamage(dmg) {
if (fortress) {
var damageDealt = self.defenseMode ? Math.max(1, dmg - 10) : dmg;
fortress.takeDamage(damageDealt);
}
}
};
var fireball = new Fireball(self.x, self.y, fireballTarget, 3);
projectiles.push(fireball);
game.addChild(fireball);
LK.getSound('Poder').play();
LK.effects.flashObject(self, 0xFF6600, 150);
}, index * 200);
})(mixFb);
}
}
});
}
});
}
};
self.update = function () {
//{caballero_30}
// Update health bar//{caballero_31}
var healthPercent = self.health / self.maxHealth; //{caballero_32}
healthBarFill.width = 400 * healthPercent; //{caballero_33}
if (healthPercent > 0.6) {
//{caballero_34}
healthBarFill.tint = 0x00FF00; //{caballero_35}
} else if (healthPercent > 0.3) {
//{caballero_36}
healthBarFill.tint = 0xFFFF00; //{caballero_37}
} else {
//{caballero_38}
healthBarFill.tint = 0xFF0000; //{caballero_39}
} //{caballero_40}
// Update QTE Manager//{caballero_QTE_5}
self.qteManager.update(); //{caballero_QTE_6}
// Turn-based combat system logic
if (self.combatPhase === 'idle') {
// Start first player turn
self.startPlayerTurn();
} else if (self.combatPhase === 'player_turn') {
self.turnTimer++;
// Show QTE indicator during player turn
if (self.qteActive) {
self.qteTimer++;
// Flash fortress to indicate QTE active
if (self.qteTimer % 20 === 0) {
LK.effects.flashObject(fortress, 0x00FF00, 100);
}
// QTE time expired - player failed to defend
if (self.qteTimer >= self.qteDuration) {
self.qteActive = false;
self.defenseMode = false;
}
}
// Turn duration expired - move to enemy turn
if (self.turnTimer >= self.turnDuration) {
self.startEnemyTurn();
}
} else if (self.combatPhase === 'enemy_turn') {
self.turnTimer++;
// Execute Caballero attack
if (self.turnTimer === 120) {
self.executeCaballeroAttack();
}
// After attack animation completes, return to player turn
if (self.turnTimer >= self.turnDuration) {
self.startPlayerTurn();
}
}
var dx = fortress.x - self.x; //{caballero_42}
var dy = fortress.y - self.y; //{caballero_43}
var distance = Math.sqrt(dx * dx + dy * dy); //{caballero_44}
// Keep Caballero positioned in front of fortress during combat
if (distance > 400) {
self.x += dx / distance * 0.5;
self.y += dy / distance * 0.5;
}
}; //{caballero_75}
self.takeDamage = function (damage) {
// Prevent damage if already defeated or marked for destruction
if (self.combatPhase === 'defeated' || self.markForDestroy) {
return;
}
// Ensure Caballero is still visible before taking damage
if (!self.visible) {
self.visible = true;
}
// Only take damage during enemy turn (fair turn-based mechanic)
if (self.combatPhase === 'enemy_turn') {
//{caballero_76}
self.health -= damage; //{caballero_77}
LK.effects.flashObject(self, 0xFFFFFF, 200); //{caballero_78}
}
if (self.health <= 0) {
// Mark as defeated to prevent any further state changes
self.combatPhase = 'defeated';
//{caballero_79}
coins += self.coinValue; //{caballero_80}
enemiesKilled++; //{caballero_81}
LK.getSound('jefesecreto').play(); //{caballero_82}
// Switch back to normal background music when Caballero is defeated//{caballero_83}
LK.playMusic('background'); //{caballero_84}
// Make Caballero visible and ensure it stays visible during cinematic
self.visible = true;
self.alpha = 1.0;
self.markForDestroy = true; //{caballero_85}
// Set game state to cinematic FIRST to prevent any game logic execution
gameState = 'cinematic'; //{caballero_cinematic_1}
// Prevent any further enemy updates by removing from enemies array immediately
for (var removeIdx = enemies.length - 1; removeIdx >= 0; removeIdx--) {
if (enemies[removeIdx] === self) {
enemies.splice(removeIdx, 1);
break;
}
}
// Show continuation screen after Caballero defeat
LK.setTimeout(function () {
// Display "CONTINUARÁ..." text on full screen
var continuaraText = new Text2('CONTINUARÁ...', {
size: 200,
fill: 0xFFFF00
});
continuaraText.anchor.set(0.5, 0.5);
continuaraText.x = 1024;
continuaraText.y = 1366;
continuaraText.alpha = 0;
game.addChild(continuaraText);
// Fade in text
tween(continuaraText, {
alpha: 1.0,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Keep on screen for 3 seconds then trigger game won
LK.setTimeout(function () {
tween(continuaraText, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
continuaraText.destroy();
// Now set gameWon to trigger victory screen after text fully fades
gameWon = true;
}
});
}, 3000);
}
});
}, 500);
} //{caballero_86}
}; //{caballero_87}
return self; //{caballero_88}
});
//{fireball_58}
// Caballero dialogue system with random responses
var CaballeroDialogueSystem = Container.expand(function () {
var self = Container.call(this);
//{caballero_dialogue_1}
// Pool of Caballero dialogue responses
self.dialogueOptions = [
//{caballero_dialogue_2}
'"¡Vamos, humano débil!"',
//{caballero_dialogue_3}
'"¡Eso no me dolió!"',
//{caballero_dialogue_4}
'"¡Prepárate para más dolor!"',
//{caballero_dialogue_5}
'"¡Tu fortaleza se desmorona!"',
//{caballero_dialogue_6}
'"¡Jajaja, soy invencible!"',
//{caballero_dialogue_7}
'"¡Crees que puedes vencerme?"',
//{caballero_dialogue_8}
'"¡Más rápido, más fuerte!"',
//{caballero_dialogue_9}
'"¡Disfrutaré esta batalla!"',
//{caballero_dialogue_10}
'"¡Tu defensa es patética!"',
//{caballero_dialogue_11}
'"¡Nunca te rendirás verdad?"',
//{caballero_dialogue_12}
'"¡Demuestrame tu poder!"',
//{caballero_dialogue_13}
'"¡Excelente intento, pero insuficiente!"'
//{caballero_dialogue_14}
];
//{caballero_dialogue_15}
self.getRandomDialogue = function () {
//{caballero_dialogue_16}
var randomIndex = Math.floor(Math.random() * self.dialogueOptions.length);
//{caballero_dialogue_17}
return self.dialogueOptions[randomIndex];
//{caballero_dialogue_18}
};
//{caballero_dialogue_19}
self.displayDialogue = function (dialogueText) {
//{caballero_dialogue_20}
// Create dialogue box if not exists
var dialogueBox = LK.getAsset('HealthBarBg', {
//{caballero_dialogue_21}
width: 1400,
//{caballero_dialogue_22}
height: 200,
//{caballero_dialogue_23}
//{caballero_dialogue_24}
anchorX: 0.5,
//{caballero_dialogue_25}
//{caballero_dialogue_26}
anchorY: 0.5 //{caballero_dialogue_27}
}); //{caballero_dialogue_28}
dialogueBox.x = 1024;
dialogueBox.y = 2200;
dialogueBox.tint = 0x1a1a1a;
dialogueBox.alpha = 0;
game.addChild(dialogueBox);
//{caballero_dialogue_29}
// Create text for dialogue
var textDisplay = new Text2(dialogueText, {
//{caballero_dialogue_30}
size: 50,
//{caballero_dialogue_31}
//{caballero_dialogue_32}
fill: 0xFFFF00 //{caballero_dialogue_33}
}); //{caballero_dialogue_34}
textDisplay.anchor.set(0.5, 0.5);
textDisplay.x = 1024;
textDisplay.y = 2200;
textDisplay.alpha = 0;
game.addChild(textDisplay);
//{caballero_dialogue_35}
// Fade in dialogue
tween(dialogueBox, {
//{caballero_dialogue_36}
alpha: 1.0 //{caballero_dialogue_37}
}, {
//{caballero_dialogue_38}
//{caballero_dialogue_39}
duration: 400,
//{caballero_dialogue_40}
//{caballero_dialogue_41}
easing: tween.easeOut //{caballero_dialogue_42}
}); //{caballero_dialogue_43}
tween(textDisplay, {
//{caballero_dialogue_44}
alpha: 1.0 //{caballero_dialogue_45}
}, {
//{caballero_dialogue_46}
//{caballero_dialogue_47}
duration: 400,
//{caballero_dialogue_48}
//{caballero_dialogue_49}
easing: tween.easeOut //{caballero_dialogue_50}
}); //{caballero_dialogue_51}
// Play dialogue sound
LK.getSound('dialogo').play();
//{caballero_dialogue_52}
// Display for 3 seconds then fade out
LK.setTimeout(function () {
//{caballero_dialogue_53}
tween(dialogueBox, {
//{caballero_dialogue_54}
alpha: 0 //{caballero_dialogue_55}
}, {
//{caballero_dialogue_56}
//{caballero_dialogue_57}
duration: 400,
//{caballero_dialogue_58}
//{caballero_dialogue_59}
easing: tween.easeOut,
//{caballero_dialogue_60}
//{caballero_dialogue_61}
onFinish: function onFinish() {
//{caballero_dialogue_62}
dialogueBox.destroy(); //{caballero_dialogue_63}
} //{caballero_dialogue_64}
}); //{caballero_dialogue_65}
tween(textDisplay, {
//{caballero_dialogue_66}
alpha: 0 //{caballero_dialogue_67}
}, {
//{caballero_dialogue_68}
//{caballero_dialogue_69}
duration: 400,
//{caballero_dialogue_70}
//{caballero_dialogue_71}
easing: tween.easeOut,
//{caballero_dialogue_72}
//{caballero_dialogue_73}
onFinish: function onFinish() {
//{caballero_dialogue_74}
textDisplay.destroy(); //{caballero_dialogue_75}
} //{caballero_dialogue_76}
}); //{caballero_dialogue_77}
}, 3000); //{caballero_dialogue_78}
};
//{caballero_dialogue_79}
return self;
//{caballero_dialogue_80}
});
//{caballero_dialogue_81}
// Create global instance of dialogue system
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; // Initialize to 0 so first attack happens immediately
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; // Reset cooldown after attacking
}
}
};
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;
// Pathfinding properties
self.waypoints = [];
self.currentWaypointIndex = 0;
self.pathRecalculateTimer = 0;
self.pathRecalculateInterval = 120; // Recalculate path every 2 seconds
self.hasValidPath = false;
self.lastPathStart = {
x: 0,
y: 0
};
// Initial path calculation
self.recalculatePath = function () {
pathfindingSystem.updateObstacles();
var newPath = pathfindingSystem.findPath(self.x, self.y, fortress.x, fortress.y);
if (newPath && newPath.length > 0) {
self.waypoints = newPath;
self.currentWaypointIndex = 0;
self.hasValidPath = true;
} else {
self.hasValidPath = false;
}
self.pathRecalculateTimer = 0;
};
self.update = function () {
// Update path recalculation timer
self.pathRecalculateTimer++;
if (self.pathRecalculateTimer >= self.pathRecalculateInterval) {
self.recalculatePath();
}
// 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
}
}
}
// Regular enemies move directly toward fortress without pathfinding
var dx = fortress.x - self.x;
var dy = fortress.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 215) {
// Move directly toward fortress
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;
});
//{caballero_89}
var Fireball = Container.expand(function (startX, startY, target, damage) {
var self = Container.call(this);
//{fireball_1}
var fireballGraphics = self.attachAsset('BigPoder', {
//{fireball_2}
anchorX: 0.5,
//{fireball_3}
anchorY: 0.5 //{fireball_4}
}); //{fireball_5}
fireballGraphics.tint = 0xFF6600; // Orange fireball color//{fireball_6}
self.x = startX; //{fireball_7}
self.y = startY; //{fireball_8}
self.target = target; //{fireball_9}
self.damage = damage; //{fireball_10}
self.speed = 7;
// Calculate direction to target//{fireball_11}
var dx = target.x - startX; //{fireball_12}
var dy = target.y - startY; //{fireball_13}
var distance = Math.sqrt(dx * dx + dy * dy); //{fireball_14}
self.dirX = dx / distance; //{fireball_15}
self.dirY = dy / distance; //{fireball_16}
// Set rotation to face target//{fireball_17}
fireballGraphics.rotation = Math.atan2(dy, dx); //{fireball_18}
// Pulsing animation for fireball//{fireball_19}
self.pulseDirection = 1; //{fireball_20}
self.update = function () {
//{fireball_21}
self.x += self.dirX * self.speed; //{fireball_22}
self.y += self.dirY * self.speed; //{fireball_23}
// Spin and pulse the fireball//{fireball_24}
fireballGraphics.rotation += 0.1; //{fireball_25}
// Pulsing scale effect//{fireball_26}
if (self.scaleX >= 1.8) {
//{fireball_27}
self.pulseDirection = -1; //{fireball_28}
} else if (self.scaleX <= 1.0) {
//{fireball_29}
self.pulseDirection = 1; //{fireball_30}
} //{fireball_31}
self.scaleX += self.pulseDirection * 0.03;
self.scaleY += self.pulseDirection * 0.03;
// Check if hit fortress//{fireball_32}
if (fortress && fortress.takeDamage) {
//{fireball_33}
var fortressDist = Math.sqrt(Math.pow(fortress.x - self.x, 2) + Math.pow(fortress.y - self.y, 2)); //{fireball_34}
if (fortressDist < 250) {
//{fireball_35}
fortress.takeDamage(self.damage); //{fireball_36}
// Explosion effect//{fireball_37}
LK.effects.flashObject(fortress, 0xFF6600, 800);
// Shrinking effect on hit//{fireball_38}
tween(self, {
//{fireball_39}
scaleX: 0,
//{fireball_40}
scaleY: 0,
//{fireball_41}
alpha: 0 //{fireball_42}
}, {
//{fireball_43}
duration: 300,
//{fireball_44}
easing: tween.easeIn,
//{fireball_45}
onFinish: function onFinish() {
//{fireball_46}
self.markForDestroy = true; //{fireball_47}
} //{fireball_48}
}); //{fireball_49}
return; //{fireball_50}
} //{fireball_51}
} //{fireball_52}
// Remove if off screen//{fireball_53}
if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) {
self.markForDestroy = true; //{fireball_54}
} //{fireball_55}
}; //{fireball_56}
return self; //{fireball_57}
});
//{fireball_58}
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;
});
// Pathfinding system for intelligent enemy movement
var PathfindingSystem = Container.expand(function () {
var self = Container.call(this);
// Grid-based pathfinding constants
self.gridSize = 150; // Size of each grid cell
self.gridWidth = Math.ceil(2048 / self.gridSize);
self.gridHeight = Math.ceil(2732 / self.gridSize);
// Initialize obstacle map
self.obstacleMap = [];
for (var gy = 0; gy < self.gridHeight; gy++) {
self.obstacleMap[gy] = [];
for (var gx = 0; gx < self.gridWidth; gx++) {
self.obstacleMap[gy][gx] = 0; // 0 = passable, 1 = blocked
}
}
// Convert world coordinates to grid coordinates
self.worldToGrid = function (x, y) {
return {
x: Math.floor(x / self.gridSize),
y: Math.floor(y / self.gridSize)
};
};
// Convert grid coordinates to world coordinates (center of cell)
self.gridToWorld = function (gridX, gridY) {
return {
x: gridX * self.gridSize + self.gridSize / 2,
y: gridY * self.gridSize + self.gridSize / 2
};
};
// Update obstacles based on defensive unit positions
self.updateObstacles = function () {
// Clear previous obstacles
for (var gy = 0; gy < self.gridHeight; gy++) {
for (var gx = 0; gx < self.gridWidth; gx++) {
self.obstacleMap[gy][gx] = 0;
}
}
// Add defensive units as obstacles
for (var i = 0; i < defensiveUnits.length; i++) {
var unit = defensiveUnits[i];
var gridPos = self.worldToGrid(unit.x, unit.y);
// Mark cells around unit as blocked (3x3 area)
for (var oy = -1; oy <= 1; oy++) {
for (var ox = -1; ox <= 1; ox++) {
var gx = gridPos.x + ox;
var gy = gridPos.y + oy;
if (gx >= 0 && gx < self.gridWidth && gy >= 0 && gy < self.gridHeight) {
self.obstacleMap[gy][gx] = 1;
}
}
}
}
};
// Simple A* pathfinding algorithm
self.findPath = function (startX, startY, endX, endY) {
var startGrid = self.worldToGrid(startX, startY);
var endGrid = self.worldToGrid(endX, endY);
// Validate grid coordinates
if (startGrid.x < 0 || startGrid.x >= self.gridWidth || startGrid.y < 0 || startGrid.y >= self.gridHeight || endGrid.x < 0 || endGrid.x >= self.gridWidth || endGrid.y < 0 || endGrid.y >= self.gridHeight) {
return []; // Invalid path
}
var openSet = [];
var closedSet = [];
var cameFrom = [];
// Initialize cameFrom map
for (var cy = 0; cy < self.gridHeight; cy++) {
cameFrom[cy] = [];
}
var gScore = []; // Cost from start to node
var fScore = []; // g + heuristic cost
for (var sy = 0; sy < self.gridHeight; sy++) {
gScore[sy] = [];
fScore[sy] = [];
for (var sx = 0; sx < self.gridWidth; sx++) {
gScore[sy][sx] = Infinity;
fScore[sy][sx] = Infinity;
}
}
gScore[startGrid.y][startGrid.x] = 0;
fScore[startGrid.y][startGrid.x] = self.heuristic(startGrid.x, startGrid.y, endGrid.x, endGrid.y);
openSet.push(startGrid);
// A* main loop
while (openSet.length > 0) {
// Find node in openSet with lowest fScore
var current = null;
var currentIndex = 0;
var lowestF = Infinity;
for (var i = 0; i < openSet.length; i++) {
var node = openSet[i];
if (fScore[node.y][node.x] < lowestF) {
lowestF = fScore[node.y][node.x];
current = node;
currentIndex = i;
}
}
if (!current) break;
if (current.x === endGrid.x && current.y === endGrid.y) {
// Reconstruct path
var path = [];
var curr = current;
while (cameFrom[curr.y] && cameFrom[curr.y][curr.x]) {
path.unshift(curr);
curr = cameFrom[curr.y][curr.x];
}
path.unshift(curr);
// Convert grid path to world coordinates
var worldPath = [];
for (var p = 0; p < path.length; p++) {
var worldPos = self.gridToWorld(path[p].x, path[p].y);
worldPath.push(worldPos);
}
return worldPath;
}
openSet.splice(currentIndex, 1);
closedSet.push(current);
// Check all neighbors (8 directions)
for (var dy = -1; dy <= 1; dy++) {
for (var dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
var nx = current.x + dx;
var ny = current.y + dy;
if (nx < 0 || nx >= self.gridWidth || ny < 0 || ny >= self.gridHeight) continue;
if (self.obstacleMap[ny][nx] === 1) continue; // Blocked
// Check if in closed set
var inClosed = false;
for (var c = 0; c < closedSet.length; c++) {
if (closedSet[c].x === nx && closedSet[c].y === ny) {
inClosed = true;
break;
}
}
if (inClosed) continue;
var neighbor = {
x: nx,
y: ny
};
var tentativeG = gScore[current.y][current.x] + (dx !== 0 && dy !== 0 ? 1.414 : 1); // Diagonal = sqrt(2)
if (gScore[ny][nx] === Infinity) {
cameFrom[ny][nx] = current;
gScore[ny][nx] = tentativeG;
fScore[ny][nx] = gScore[ny][nx] + self.heuristic(nx, ny, endGrid.x, endGrid.y);
// Add to open set if not already there
var inOpen = false;
for (var o = 0; o < openSet.length; o++) {
if (openSet[o].x === nx && openSet[o].y === ny) {
inOpen = true;
break;
}
}
if (!inOpen) {
openSet.push(neighbor);
}
} else if (tentativeG < gScore[ny][nx]) {
cameFrom[ny][nx] = current;
gScore[ny][nx] = tentativeG;
fScore[ny][nx] = gScore[ny][nx] + self.heuristic(nx, ny, endGrid.x, endGrid.y);
}
}
}
}
return []; // No path found
};
// Manhattan distance heuristic
self.heuristic = function (x1, y1, x2, y2) {
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
};
return self;
});
// Create global pathfinding system instance
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;
});
//{QTE_70}
var QTEManager = Container.expand(function (caballeroRef) {
var self = Container.call(this);
//{QTE_71}
//{QTE_72}
self.caballero = caballeroRef; //{QTE_73}
self.squares = []; //{QTE_74}
self.hitsRequired = 3; //{QTE_75}
self.hitsReceived = 0; //{QTE_76}
self.qteActive = false; //{QTE_77}
self.startQTE = function () {
//{QTE_78}
self.qteActive = true; //{QTE_79}
self.hitsReceived = 0; //{QTE_80}
// Spawn 3 squares at different times with proper staggered delays (in milliseconds)
var spawnTimes = [100, 700, 1400]; // Staggered: 100ms, 700ms, 1400ms apart
var xPositions = [512, 1024, 1536]; // Three distinct X positions across screen
for (var sq = 0; sq < 3; sq++) {
//{QTE_81}
(function (index, delay, xPos) {
LK.setTimeout(function () {
//{QTE_82}
if (self.qteActive) {
// Check if QTE is still active before spawning
var square = new QTESquare(xPos, 600, self); //{QTE_83}
self.squares.push(square); //{QTE_84}
game.addChild(square); //{QTE_85}
}
}, delay); //{QTE_86}
})(sq, spawnTimes[sq], xPositions[sq]); //{QTE_87}
} //{QTE_88}
}; //{QTE_89}
self.onSquareHit = function () {
//{QTE_90}
self.hitsReceived++; //{QTE_91}
// Check if QTE succeeded (3 hits)
if (self.hitsReceived >= self.hitsRequired) {
//{QTE_92}
self.onQTESuccess(); //{QTE_93}
} //{QTE_94}
}; //{QTE_95}
self.onSquareMissed = function () {
//{QTE_96}
// Failure - end QTE without success
self.qteActive = false; //{QTE_97}
// Clean up remaining squares
for (var sq_i = self.squares.length - 1; sq_i >= 0; sq_i--) {
//{QTE_98}
var sq = self.squares[sq_i]; //{QTE_99}
if (sq && sq.isActive) {
//{QTE_100}
sq.isActive = false; //{QTE_101}
sq.markForDestroy = true; //{QTE_102}
} //{QTE_103}
} //{QTE_104}
self.squares = []; //{QTE_105}
}; //{QTE_106}
self.onQTESuccess = function () {
//{QTE_107}
self.qteActive = false; //{QTE_108}
// Player successfully completed QTE - frustrate Caballero attack
self.caballero.defenseMode = true; //{QTE_109}
// Visual effect on Caballero
LK.effects.flashObject(self.caballero, 0x00FF00, 500); //{QTE_110}
// Clean up squares
for (var sq_i = self.squares.length - 1; sq_i >= 0; sq_i--) {
//{QTE_111}
var sq = self.squares[sq_i]; //{QTE_112}
if (sq) {
//{QTE_113}
sq.markForDestroy = true; //{QTE_114}
} //{QTE_115}
} //{QTE_116}
self.squares = []; //{QTE_117}
}; //{QTE_118}
self.update = function () {
//{QTE_119}
// Update all active squares
for (var sq_i = self.squares.length - 1; sq_i >= 0; sq_i--) {
//{QTE_120}
var sq = self.squares[sq_i]; //{QTE_121}
if (sq) {
//{QTE_122}
sq.update(); //{QTE_123}
if (sq.markForDestroy) {
//{QTE_124}
sq.destroy(); //{QTE_125}
self.squares.splice(sq_i, 1); //{QTE_126}
} //{QTE_127}
} //{QTE_128}
} //{QTE_129}
}; //{QTE_130}
return self; //{QTE_131}
});
var QTESquare = Container.expand(function (x, y, qteManager) {
var self = Container.call(this);
//{QTE_1}
var squareGraphics = self.attachAsset('BigPoder', {
//{QTE_2}
anchorX: 0.5,
//{QTE_3}
anchorY: 0.5,
//{QTE_4}
scaleX: 0.8,
//{QTE_5}
scaleY: 0.8 //{QTE_6}
}); //{QTE_7}
squareGraphics.tint = 0x00FF00; //{QTE_8}
self.x = x; //{QTE_9}
self.y = y; //{QTE_10}
self.qteManager = qteManager; //{QTE_11}
self.isActive = true; //{QTE_12}
self.spawnTime = LK.ticks; //{QTE_13}
self.duration = 180; // 3 seconds at 60fps//{QTE_14}
self.pulseDirection = 1; //{QTE_15}
self.update = function () {
//{QTE_16}
if (!self.isActive) return; //{QTE_17}
var elapsed = LK.ticks - self.spawnTime; //{QTE_18}
// Pulsing effect
if (squareGraphics.scaleX >= 1.2) {
//{QTE_19}
self.pulseDirection = -1; //{QTE_20}
} else if (squareGraphics.scaleX <= 0.7) {
self.pulseDirection = 1; //{QTE_21}
} //{QTE_22}
squareGraphics.scaleX += self.pulseDirection * 0.02; //{QTE_23}
squareGraphics.scaleY += self.pulseDirection * 0.02;
// Rotate for emphasis//{QTE_24}
squareGraphics.rotation += 0.05; //{QTE_25}
// Fade color as time runs out
var timeRemaining = self.duration - elapsed; //{QTE_26}
var colorProgress = timeRemaining / self.duration; //{QTE_27}
if (colorProgress > 0.5) {
//{QTE_28}
squareGraphics.tint = 0x00FF00; // Green
} else if (colorProgress > 0.25) {
squareGraphics.tint = 0xFFFF00; // Yellow
} else {
//{QTE_29}
squareGraphics.tint = 0xFF0000; // Red
} //{QTE_30}
// Time expired - QTE failed//{QTE_31}
if (elapsed >= self.duration) {
//{QTE_32}
self.isActive = false; //{QTE_33}
if (self.qteManager) {
//{QTE_34}
self.qteManager.onSquareMissed(); //{QTE_35}
} //{QTE_36}
// Fade out
tween(self, {
//{QTE_37}
alpha: 0 //{QTE_38}
}, {
//{QTE_39}
duration: 300,
//{QTE_40}
easing: tween.easeOut,
//{QTE_41}
onFinish: function onFinish() {
//{QTE_42}
self.markForDestroy = true; //{QTE_43}
} //{QTE_44}
}); //{QTE_45}
} //{QTE_46}
}; //{QTE_47}
self.down = function (x, y, obj) {
//{QTE_48}
if (self.isActive) {
//{QTE_49}
self.isActive = false; //{QTE_50}
// Successfully hit - flash green
LK.effects.flashObject(self, 0x00FF00, 300); //{QTE_51}
LK.getSound('Poder').play(); //{QTE_52}
// Shrinking animation
tween(self, {
//{QTE_53}
scaleX: 0,
//{QTE_54}
scaleY: 0,
//{QTE_55}
alpha: 0 //{QTE_56}
}, {
//{QTE_57}
duration: 200,
//{QTE_58}
easing: tween.easeIn,
//{QTE_59}
onFinish: function onFinish() {
//{QTE_60}
self.markForDestroy = true; //{QTE_61}
} //{QTE_62}
}); //{QTE_63}
// Notify manager of successful hit
if (self.qteManager) {
//{QTE_64}
self.qteManager.onSquareHit(); //{QTE_65}
} //{QTE_66}
} //{QTE_67}
}; //{QTE_68}
return self; //{QTE_69}
});
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');
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');
// Trigger ending cinematic instead of immediate game won
// DO NOT set gameWon flag here - it will be set ONLY after cinematic completes
triggerEndingCinematic();
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
****/
// Create global pathfinding system instance before any game code uses it
function _typeof4(o) {
"@babel/helpers - typeof";
return _typeof4 = "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;
}, _typeof4(o);
}
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);
}
function _typeof3(o) {
"@babel/helpers - typeof";
return _typeof3 = "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;
}, _typeof3(o);
}
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "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;
}, _typeof2(o);
}
var pathfindingSystem = new PathfindingSystem(); //{Bs_moved}
// Ensure unitUpgrades is properly initialized before any storage operations
var unitUpgrades = {
archer: {
damage: 0,
power: 0
},
spearman: {
damage: 0,
power: 0
},
cavalry: {
damage: 0,
power: 0
}
};
// Load persisted upgrades into game with proper validation
if (storage && _typeof4(storage) === 'object' && storage.unitUpgrades && _typeof4(storage.unitUpgrades) === 'object') {
// Validate and load archer upgrades
if (storage.unitUpgrades.archer && _typeof4(storage.unitUpgrades.archer) === 'object') {
if (typeof storage.unitUpgrades.archer.damage === 'number' && storage.unitUpgrades.archer.damage >= 0) {
unitUpgrades.archer.damage = Math.floor(storage.unitUpgrades.archer.damage);
}
if (typeof storage.unitUpgrades.archer.power === 'number' && storage.unitUpgrades.archer.power >= 0) {
unitUpgrades.archer.power = Math.floor(storage.unitUpgrades.archer.power);
}
}
// Validate and load spearman upgrades
if (storage.unitUpgrades.spearman && _typeof4(storage.unitUpgrades.spearman) === 'object') {
if (typeof storage.unitUpgrades.spearman.damage === 'number' && storage.unitUpgrades.spearman.damage >= 0) {
unitUpgrades.spearman.damage = Math.floor(storage.unitUpgrades.spearman.damage);
}
if (typeof storage.unitUpgrades.spearman.power === 'number' && storage.unitUpgrades.spearman.power >= 0) {
unitUpgrades.spearman.power = Math.floor(storage.unitUpgrades.spearman.power);
}
}
// Validate and load cavalry upgrades
if (storage.unitUpgrades.cavalry && _typeof4(storage.unitUpgrades.cavalry) === 'object') {
if (typeof storage.unitUpgrades.cavalry.damage === 'number' && storage.unitUpgrades.cavalry.damage >= 0) {
unitUpgrades.cavalry.damage = Math.floor(storage.unitUpgrades.cavalry.damage);
}
if (typeof storage.unitUpgrades.cavalry.power === 'number' && storage.unitUpgrades.cavalry.power >= 0) {
unitUpgrades.cavalry.power = Math.floor(storage.unitUpgrades.cavalry.power);
}
}
}
// Initialize shop coins from storage or set default
if (_typeof4(storage) === 'object' && typeof storage.shopCoins === 'number' && storage.shopCoins >= 0) {
shopCoins = Math.floor(storage.shopCoins);
} else {
shopCoins = 10000;
}
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; // Normal max health for regular gameplay
var fortressCurrentHealth = 200;
var caballeroFortressMaxHealth = 3600; // Extended health for 12-minute Caballero fight
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;
var caballeroBossSpawned = false; //{caballero_vars_1}
// Shop system variables - initialize persistent weapon upgrades
var shopState = 'menu'; // 'menu' or 'shop'
var shopCoins = 10000;
if (storage && typeof storage.shopCoins === 'number' && storage.shopCoins >= 0) {
shopCoins = Math.floor(storage.shopCoins);
}
storage.shopCoins = shopCoins;
// 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);
// Persist to storage properly
if (_typeof4(storage) === 'object') {
storage.shopCoins = shopCoins;
if (!storage.unitUpgrades) {
storage.unitUpgrades = {};
}
if (!storage.unitUpgrades[type]) {
storage.unitUpgrades[type] = {};
}
storage.unitUpgrades[type].damage = unitUpgrades[type].damage;
if (!storage.unitUpgrades[type].power) {
storage.unitUpgrades[type].power = unitUpgrades[type].power || 0;
}
}
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);
// Persist to storage properly
if (_typeof4(storage) === 'object') {
storage.shopCoins = shopCoins;
if (!storage.unitUpgrades) {
storage.unitUpgrades = {};
}
if (!storage.unitUpgrades[type]) {
storage.unitUpgrades[type] = {};
}
storage.unitUpgrades[type].power = unitUpgrades[type].power;
if (!storage.unitUpgrades[type].damage) {
storage.unitUpgrades[type].damage = unitUpgrades[type].damage || 0;
}
}
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;
// Reset fortress health to normal gameplay values (not Caballero extended values)
fortressMaxHealth = 200;
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;
// Reset Caballero boss spawn flag//{caballero_reset_1}
caballeroBossSpawned = false; //{caballero_reset_2}
// Initialize fortress at center
fortress = new Fortress();
fortress.x = 1024;
fortress.y = 1366;
game.addChild(fortress);
// Start background music for gameplay
LK.playMusic('background');
// Initialize pathfinding system for the game
pathfindingSystem.updateObstacles();
}
// 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);
}
// Caballero introduction cinematic function
function triggerCaballeroIntro() {
// Increase fortress health for extended 12-minute Caballero combat
fortressMaxHealth = caballeroFortressMaxHealth;
// Restore fortress to full health for this battle
fortressCurrentHealth = fortressMaxHealth;
// Stop all game activity temporarily
var gameStateSaved = gameState; //{caballero_intro_1}
gameState = 'cinematic'; //{caballero_intro_2}
// Clear all existing enemies//{caballero_intro_3}
for (var clearEnemy_i = enemies.length - 1; clearEnemy_i >= 0; clearEnemy_i--) {
//{caballero_intro_4}
if (enemies[clearEnemy_i] && typeof enemies[clearEnemy_i].destroy === "function") {
//{caballero_intro_5}
enemies[clearEnemy_i].destroy(); //{caballero_intro_6}
} //{caballero_intro_7}
enemies.splice(clearEnemy_i, 1); //{caballero_intro_8}
} //{caballero_intro_9}
// Clear all projectiles//{caballero_intro_10}
for (var clearProj_i = projectiles.length - 1; clearProj_i >= 0; clearProj_i--) {
//{caballero_intro_11}
if (projectiles[clearProj_i] && typeof projectiles[clearProj_i].destroy === "function") {
//{caballero_intro_12}
projectiles[clearProj_i].destroy(); //{caballero_intro_13}
} //{caballero_intro_14}
projectiles.splice(clearProj_i, 1); //{caballero_intro_15}
} //{caballero_intro_16}
// Play Caballero intro music
LK.playMusic('caballero'); //{caballero_intro_17}
// Create Caballero character off-screen at top
var caballeroIntro = LK.getAsset('caballero', {
//{caballero_intro_18}
anchorX: 0.5,
//{caballero_intro_19}
anchorY: 1.0,
//{caballero_intro_20}
scaleX: 2.5,
//{caballero_intro_21}
scaleY: 2.5 //{caballero_intro_22}
}); //{caballero_intro_23}
caballeroIntro.x = 1024;
caballeroIntro.y = -200; // Start off-screen at top//{caballero_intro_24}
game.addChild(caballeroIntro); //{caballero_intro_25}
// Create dialogue box
var caballeroDialogueBox = LK.getAsset('HealthBarBg', {
//{caballero_intro_26}
width: 1400,
//{caballero_intro_27}
height: 250,
//{caballero_intro_28}
anchorX: 0.5,
//{caballero_intro_29}
anchorY: 0.5 //{caballero_intro_30}
}); //{caballero_intro_31}
caballeroDialogueBox.x = 1024;
caballeroDialogueBox.y = 2200;
caballeroDialogueBox.tint = 0x1a1a1a;
caballeroDialogueBox.alpha = 0;
game.addChild(caballeroDialogueBox); //{caballero_intro_32}
// Create dialogue text//{caballero_intro_33}
var caballeroDialogueText = new Text2('', {
//{caballero_intro_34}
size: 60,
//{caballero_intro_35}
fill: 0xFFFF00 //{caballero_intro_36}
}); //{caballero_intro_37}
caballeroDialogueText.anchor.set(0.5, 0.5);
caballeroDialogueText.x = 1024;
caballeroDialogueText.y = 2200;
caballeroDialogueText.alpha = 0;
game.addChild(caballeroDialogueText); //{caballero_intro_38}
// PHASE 1: Caballero slowly approaches (3 seconds)
tween(caballeroIntro, {
//{caballero_intro_39}
y: 800,
//{caballero_intro_40}
scaleX: 2.0,
//{caballero_intro_41}
scaleY: 2.0 //{caballero_intro_42}
}, {
//{caballero_intro_43}
duration: 3000,
//{caballero_intro_44}
easing: tween.easeOut //{caballero_intro_45}
}); //{caballero_intro_46}
// PHASE 2: Caballero becomes fully visible and says "hola humano" (at 2 seconds)
LK.setTimeout(function () {
//{caballero_intro_47}
caballeroDialogueBox.alpha = 1.0;
caballeroDialogueText.alpha = 1.0;
caballeroDialogueText.setText('"¡Hola, humano!"'); //{caballero_intro_48}
LK.getSound('dialogo').play(); //{caballero_intro_49}
// Flash the caballero when speaking//{caballero_intro_50}
LK.effects.flashObject(caballeroIntro, 0xFFFF00, 400); //{caballero_intro_51}
}, 2000);
// PHASE 3: Battle music starts and Caballero moves into position (at 4 seconds)
LK.setTimeout(function () {
//{caballero_intro_52}
// Switch to battle music
LK.playMusic('caballeroelataque'); //{caballero_intro_53}
// Fade out dialogue
tween(caballeroDialogueBox, {
//{caballero_intro_54}
alpha: 0 //{caballero_intro_55}
}, {
//{caballero_intro_56}
duration: 500,
//{caballero_intro_57}
easing: tween.easeOut //{caballero_intro_58}
}); //{caballero_intro_59}
tween(caballeroDialogueText, {
//{caballero_intro_60}
alpha: 0 //{caballero_intro_61}
}, {
//{caballero_intro_62}
duration: 500,
//{caballero_intro_63}
easing: tween.easeOut,
//{caballero_intro_64}
onFinish: function onFinish() {
//{caballero_intro_65}
// Clean up dialogue after fade//{caballero_intro_66}
caballeroDialogueBox.destroy(); //{caballero_intro_67}
caballeroDialogueText.destroy(); //{caballero_intro_68}
// Spawn actual Caballero boss and begin combat//{caballero_intro_69}
var caballeroBoss = new Caballero(); //{caballero_intro_70}
caballeroBoss.x = caballeroIntro.x; //{caballero_intro_71}
caballeroBoss.y = caballeroIntro.y; //{caballero_intro_72}
enemies.push(caballeroBoss); //{caballero_intro_73}
game.addChild(caballeroBoss); //{caballero_intro_74}
// Remove intro Caballero visual//{caballero_intro_75}
caballeroIntro.destroy(); //{caballero_intro_76}
// Resume game state//{caballero_intro_77}
gameState = gameStateSaved; //{caballero_intro_78}
} //{caballero_intro_79}
}); //{caballero_intro_80}
// Dramatic flash effect at start of battle
LK.effects.flashScreen(0xFF6600, 1000); //{caballero_intro_81}
LK.getSound('jefesecreto').play(); //{caballero_intro_82}
}, 4000);
} //{caballero_intro_83}
// Ending cinematic function
function triggerEndingCinematic() {
// Stop all game activity
gameState = 'cinematic';
gameWon = false; // Prevent immediate game over
// Clear all enemies and projectiles
for (var ce_i = enemies.length - 1; ce_i >= 0; ce_i--) {
if (enemies[ce_i] && typeof enemies[ce_i].destroy === "function") {
enemies[ce_i].destroy();
}
enemies.splice(ce_i, 1);
}
for (var cp_i = projectiles.length - 1; cp_i >= 0; cp_i--) {
if (projectiles[cp_i] && typeof projectiles[cp_i].destroy === "function") {
projectiles[cp_i].destroy();
}
projectiles.splice(cp_i, 1);
}
// Set cinematic background
game.setBackgroundColor(0x1a3a3a);
// Play cinematic music
LK.playMusic('cinematica');
// Create house/home interior background
var homeBackground = LK.getAsset('fortress', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 2.5
});
homeBackground.x = 1024;
homeBackground.y = 1366;
homeBackground.tint = 0x8B4513;
homeBackground.alpha = 0.3;
game.addChild(homeBackground);
// Create Kidarquero character - starts searching
var kidarquero = LK.getAsset('Kidarquero', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 2.0
});
kidarquero.x = 200;
kidarquero.y = 1800;
game.addChild(kidarquero);
// Create dialogue box for subtitles
var cinematicDialogueBox = LK.getAsset('HealthBarBg', {
width: 1600,
height: 250,
anchorX: 0.5,
anchorY: 0.5
});
cinematicDialogueBox.x = 1024;
cinematicDialogueBox.y = 2400;
cinematicDialogueBox.tint = 0x000000;
cinematicDialogueBox.alpha = 0.8;
game.addChild(cinematicDialogueBox);
// Create dialogue text
var cinematicText = new Text2('', {
size: 50,
fill: 0xFFFFFF
});
cinematicText.anchor.set(0.5, 0.5);
cinematicText.x = 1024;
cinematicText.y = 2400;
game.addChild(cinematicText);
// SCENE 1: Kidarquero walking and searching (2-3 seconds)
LK.setTimeout(function () {
cinematicText.setText('¡Papá, dónde estás!');
LK.getSound('dialogo').play();
// Kidarquero walks across screen searching
tween(kidarquero, {
x: 1400,
scaleX: -2.0
}, {
duration: 3000,
easing: tween.linear
});
}, 500);
// SCENE 2: Kidarquero says "They attacked again" (3.5 seconds)
LK.setTimeout(function () {
cinematicText.setText('¡Volvieron a atacar!');
LK.getSound('dialogo').play();
// Kidarquero stops and looks around worried
tween(kidarquero, {
scaleY: 2.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(kidarquero, {
scaleY: 2.0
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}, 3500);
// SCENE 3: Dad appears from the side (4.5 seconds)
LK.setTimeout(function () {
// Create Dad character off-screen
var dad = LK.getAsset('Dad', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 2.0
});
dad.x = 1900;
dad.y = 1800;
game.addChild(dad);
// Dad walks toward Kidarquero
tween(dad, {
x: 900
}, {
duration: 2000,
easing: tween.easeOut
});
}, 4500);
// SCENE 4: Kidarquero sees Dad and reunion dialogue (6.5 seconds)
LK.setTimeout(function () {
cinematicText.setText('¡Papá!');
LK.getSound('dialogo').play();
// Kidarquero runs toward Dad
tween(kidarquero, {
x: 950,
scaleX: 2.0
}, {
duration: 800,
easing: tween.easeOut
});
}, 6500);
// SCENE 5: Dad responds with love (7.5 seconds)
LK.setTimeout(function () {
cinematicText.setText('Te quiero, hijo');
LK.getSound('dialogo').play();
// Visual effect - warm glow
LK.effects.flashScreen(0xFFFF00, 800);
}, 7500);
// SCENE 6: Final emotional moment with fade out (8.5 seconds)
LK.setTimeout(function () {
// Fade to black
tween(kidarquero, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut
});
tween(homeBackground, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut
});
tween(cinematicDialogueBox, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut
});
tween(cinematicText, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Clean up cinematic elements
kidarquero.destroy();
homeBackground.destroy();
cinematicDialogueBox.destroy();
cinematicText.destroy();
// Victory! Set gameWon flag ONLY after complete fade is finished
LK.setTimeout(function () {
gameWon = true;
}, 500);
}
});
}, 8500);
}
// 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 () {
// During Caballero 1v1 combat, shield button triggers defense
if (caballeroBossSpawned && enemies.length > 0 && enemies[0].isCaballero) {
var caballeroRef = enemies[0];
if (caballeroRef.combatPhase === 'player_turn' && caballeroRef.qteActive && !caballeroRef.shieldPressed) {
caballeroRef.shieldPressed = true;
caballeroRef.defenseMode = true;
caballeroRef.qteActive = false;
fortress.activateShield();
LK.effects.flashObject(fortress, 0x00FFFF, 500);
LK.getSound('Poder').play();
}
} else {
fortress.activateShield();
}
};
// Add attack button for Caballero fight - fortress attack button
var caballeroAttackButton = new Text2('ATAQUE\nTORRE', {
size: 24,
fill: 0xFF6600
});
caballeroAttackButton.anchor.set(0.5, 1);
caballeroAttackButton.x = -200;
caballeroAttackButton.y = -150;
LK.gui.bottom.addChild(caballeroAttackButton);
caballeroAttackButton.visible = false;
caballeroAttackButton.down = function () {
// During Caballero fight, attack button lets tower deal damage
if (caballeroBossSpawned && enemies.length > 0 && enemies[0].isCaballero) {
var caballeroRef = enemies[0];
if (caballeroRef.combatPhase === 'player_turn') {
var damageDealt = 25;
caballeroRef.takeDamage(damageDealt);
LK.effects.flashObject(caballeroRef, 0xFF0000, 300);
LK.getSound('Espada').play();
caballeroRef.qteActive = false;
// End turn after attack
caballeroRef.turnTimer = caballeroRef.turnDuration;
// Visual effect - fortress shoots projectile at Caballero
var towerAttackProjectile = new BigPoderProjectile(fortress.x, fortress.y, caballeroRef, 8);
projectiles.push(towerAttackProjectile);
game.addChild(towerAttackProjectile);
}
}
};
// Add turn indicator text
var turnIndicatorText = new Text2('TURNO DEL JUGADOR', {
size: 40,
fill: 0x00FF00
});
turnIndicatorText.anchor.set(0.5, 0.5);
turnIndicatorText.x = 0;
turnIndicatorText.y = 250;
LK.gui.top.addChild(turnIndicatorText);
turnIndicatorText.visible = false;
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 Caballero 1v1 combat
if (caballeroBossSpawned && enemies.length > 0 && enemies[0].isCaballero) {
//{caballero_disable_troops_1}
return; // No troop placement allowed during Caballero 1v1 fight //{caballero_disable_troops_2}
} //{caballero_disable_troops_3}
// 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') {
// Check if game won (cinematic completed) - do this FIRST before any other logic
if (gameWon) {
//{Aa_new}
LK.showYouWin(); //{Ab_new}
return; //{Ac_new}
} //{Ad_new}
// During cinematic, prevent ALL game mechanics and return immediately
if (gameState === 'cinematic') {
return; //{Ah_cinematic}
}
// Still process pending UI updates but skip enemy/game mechanics during menu
// Update combo system even during menu
comboSystem.update();
// Update UI displays
coinsText.setText('Monedas: ' + coins);
waveText.setText('Oleada: ' + currentWave);
healthText.setText('Fortaleza: ' + fortressCurrentHealth + '/' + fortressMaxHealth);
enemiesEnteredText.setText('Enemigos Entraron: ' + enemiesEntered + '/' + maxEnemiesAllowed);
// Don't process game logic - return after UI updates
return;
}
// Update pathfinding obstacles every tick to reflect current defensive unit positions
pathfindingSystem.updateObstacles();
// Check lose conditions first
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 && !caballeroBossSpawned) {
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 Caballero 1v1 combat UI when boss is present
if (caballeroBossSpawned && enemies.length > 0 && enemies[0].isCaballero) {
var caballeroRef = enemies[0];
// Show combat UI elements
caballeroAttackButton.visible = true;
turnIndicatorText.visible = true;
// Update turn indicator
if (caballeroRef.combatPhase === 'player_turn') {
turnIndicatorText.setText('¡TU TURNO! (ATACA O DEFIENDE)');
turnIndicatorText.fill = 0x00FF00;
caballeroAttackButton.fill = 0xFFFF00;
// Show QTE indicator
if (caballeroRef.qteActive && !caballeroRef.shieldPressed) {
shieldButton.setText('¡ESCUDO! (QTE)');
shieldButton.fill = 0x00FF00;
// Pulsing effect for QTE
if (LK.ticks % 10 === 0) {
tween(shieldButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(shieldButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
}
}
} else if (caballeroRef.combatPhase === 'enemy_turn') {
turnIndicatorText.setText('¡TURNO DEL CABALLERO!');
turnIndicatorText.fill = 0xFF0000;
caballeroAttackButton.fill = 0x666666;
shieldButton.fill = 0x666666;
}
} else {
// Hide Caballero-specific UI when not in 1v1 combat
caballeroAttackButton.visible = false;
turnIndicatorText.visible = false;
}
// 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 and NOT in Caballero 1v1 combat and NOT in cinematic state
var inCaballeroCombat = caballeroBossSpawned && enemies.length > 0 && enemies[0].isCaballero;
if (currentScenario === 'desert' && enemiesSpawned < enemiesInWave && LK.ticks % spawnDelay === 0 && !inCaballeroCombat && gameState === 'playing') {
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 if boss was defeated and spawn Caballero
var ogreBossExists = false;
for (var ogreBoss_i = 0; ogreBoss_i < enemies.length; ogreBoss_i++) {
if (enemies[ogreBoss_i].isBoss) {
ogreBossExists = true;
break; //{caballero_check_1}
} //{caballero_check_2}
} //{caballero_check_3}
// If Ogre boss was just defeated (no boss exists now but bossSpawned is true)//{caballero_check_4}
if (bossSpawned && !ogreBossExists && !caballeroBossSpawned) {
//{caballero_check_5}
caballeroBossSpawned = true; //{caballero_check_6}
// Clear all defensive units from the map
for (var clearUnits_i = defensiveUnits.length - 1; clearUnits_i >= 0; clearUnits_i--) {
if (defensiveUnits[clearUnits_i] && typeof defensiveUnits[clearUnits_i].destroy === "function") {
defensiveUnits[clearUnits_i].destroy();
}
defensiveUnits.splice(clearUnits_i, 1);
}
// Clear all remaining enemies from the map (except what will be spawned by Caballero intro)
for (var clearEnemies_i = enemies.length - 1; clearEnemies_i >= 0; clearEnemies_i--) {
if (enemies[clearEnemies_i] && typeof enemies[clearEnemies_i].destroy === "function") {
enemies[clearEnemies_i].destroy();
}
enemies.splice(clearEnemies_i, 1);
}
triggerCaballeroIntro(); //{caballero_check_7}
} //{caballero_check_8}
// 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);
}
// Pause enemy advancement during boss encounter
var enemyAdvancementPaused = true; //{uL_new}
// Create dialogue bubble with boss message
var dialogueBubble = LK.getAsset('HealthBarBg', {
//{uM_new}
width: 1200,
//{uN_new}
height: 300,
//{uO_new}
anchorX: 0.5,
//{uP_new}
anchorY: 0.5 //{uQ_new}
}); //{uR_new}
dialogueBubble.x = 1024;
dialogueBubble.y = 600;
dialogueBubble.tint = 0x1a1a1a;
game.addChild(dialogueBubble);
// Boss message text
var bossMessage = new Text2('"¡Me derrotas antes, no tendrás la misma suerte!!!"', {
//{uS_new}
size: 60,
//{uT_new}
fill: 0xFF0000 //{uU_new}
}); //{uV_new}
bossMessage.anchor.set(0.5, 0.5);
bossMessage.x = 1024;
bossMessage.y = 600;
game.addChild(bossMessage);
// Animate dialogue appearance
dialogueBubble.alpha = 0;
bossMessage.alpha = 0;
tween(dialogueBubble, {
//{uW_new}
alpha: 1.0 //{uX_new}
}, {
//{uY_new}
duration: 500,
//{uZ_new}
easing: tween.easeOut //{va_new}
}); //{vb_new}
tween(bossMessage, {
//{vc_new}
alpha: 1.0,
//{vd_new}
scaleX: 1.1,
//{ve_new}
scaleY: 1.1 //{vf_new}
}, {
//{vg_new}
duration: 500,
//{vh_new}
easing: tween.easeOut,
//{vi_new}
onFinish: function onFinish() {
//{vj_new}
// After message displays, remove dialogue and resume game
LK.setTimeout(function () {
//{vk_new}
tween(dialogueBubble, {
//{vl_new}
alpha: 0 //{vm_new}
}, {
//{vn_new}
duration: 400,
//{vo_new}
easing: tween.easeOut,
//{vp_new}
onFinish: function onFinish() {
//{vq_new}
dialogueBubble.destroy(); //{vr_new}
} //{vs_new}
}); //{vt_new}
tween(bossMessage, {
//{vu_new}
alpha: 0 //{vv_new}
}, {
//{vw_new}
duration: 400,
//{vx_new}
easing: tween.easeOut,
//{vy_new}
onFinish: function onFinish() {
//{vz_new}
bossMessage.destroy(); //{wa_new}
enemyAdvancementPaused = false; //{wb_new}
} //{wc_new}
}); //{wd_new}
}, 3000); // Display message for 3 seconds
} //{we_new}
}); //{wf_new}
// 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();
}
}
};
//{caballero_dialogue_81}
// Create global instance of dialogue system
var caballeroDialogueSystem = new CaballeroDialogueSystem();
//{caballero_dialogue_82} ===================================================================
--- original.js
+++ change.js
@@ -391,8 +391,11 @@
scaleX: 3.0,
//{caballero_5}
scaleY: 3.0 //{caballero_6}
}); //{caballero_7}
+ // Ensure graphics are visible
+ bossGraphics.visible = true;
+ bossGraphics.alpha = 1.0;
// Add health bar background//{caballero_8}
var healthBarBg = self.attachAsset('HealthBarBg', {
//{caballero_9}
anchorX: 0.5,
@@ -655,8 +658,12 @@
// Prevent damage if already defeated or marked for destruction
if (self.combatPhase === 'defeated' || self.markForDestroy) {
return;
}
+ // Ensure Caballero is still visible before taking damage
+ if (!self.visible) {
+ self.visible = true;
+ }
// Only take damage during enemy turn (fair turn-based mechanic)
if (self.combatPhase === 'enemy_turn') {
//{caballero_76}
self.health -= damage; //{caballero_77}
@@ -670,8 +677,11 @@
enemiesKilled++; //{caballero_81}
LK.getSound('jefesecreto').play(); //{caballero_82}
// Switch back to normal background music when Caballero is defeated//{caballero_83}
LK.playMusic('background'); //{caballero_84}
+ // Make Caballero visible and ensure it stays visible during cinematic
+ self.visible = true;
+ self.alpha = 1.0;
self.markForDestroy = true; //{caballero_85}
// Set game state to cinematic FIRST to prevent any game logic execution
gameState = 'cinematic'; //{caballero_cinematic_1}
// Prevent any further enemy updates by removing from enemies array immediately
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