Code edit (2 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'storage.get is not a function' in or related to this line: 'var saved_nivel = storage.get('nivel_llegado');' Line Number: 4253 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Please fix the bug: 'storage.get is not a function' in or related to this line: 'var saved_nivel = storage.get('nivel_llegado');' Line Number: 4253 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (11 edits merged)
Please save this source code
User prompt
guarda en el cache del juagdor la variable nivel_llegado, para gaurdar su porgreso porfa ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (2 edits merged)
Please save this source code
User prompt
en el comic final mejro agrega un boton que diga final. y este sumara 1 en vez de darlee clic a la imagen porfa
User prompt
El jeugo se gana al nivel 33, imprime un console.log cunado slaga el comic final
Code edit (1 edits merged)
Please save this source code
User prompt
Ahora si se sube al nivel 32, se mostrara una imagen en pantalla enfrente de todo que sera l aiamgen de despedidia llamda comic_final, si se da clic a la imagen se sube al nievl 33 y se acaba el juego
User prompt
Ahora se gana si se llega al nivel 33,
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var ElementalNote = Container.expand(function (element, x, y) { var self = Container.call(this); var noteGraphics = self.attachAsset(element + 'Note', { anchorX: 0.5, anchorY: 0.5 }); self.element = element; self.x = x; self.y = y; // Text labels removed for cleaner appearance self.isDisabled = false; self.down = function (x, y, obj) { // Check if game is paused if (pausa) { return; } // Check if button is disabled if (self.isDisabled) { return; } // Check if element is unlocked for note buttons (always allow at level 7+) if (unlockedElements.indexOf(self.element) === -1) { return; // Don't play sound or process note if element is not unlocked for note buttons } // Disable button self.isDisabled = true; notePressed(self.element); // Play sound effect with controlled volume when pressed directly // Access current global soundValue instead of captured value var currentSoundValue = typeof soundValue !== 'undefined' ? soundValue : 0.8; if (self.element === 'fire') { var fireSound = LK.getSound('Nota_Fire'); fireSound.volume = currentSoundValue; fireSound.play(); } else if (self.element === 'water') { var waterSound = LK.getSound('Nota_Water'); waterSound.volume = currentSoundValue; waterSound.play(); } else if (self.element === 'earth') { var earthSound = LK.getSound('Nota_Earth'); earthSound.volume = currentSoundValue; earthSound.play(); } else if (self.element === 'wind') { var windSound = LK.getSound('Nota_Wind'); windSound.volume = currentSoundValue; windSound.play(); } else if (self.element === 'light') { var lightSound = LK.getSound('Nota_Light'); lightSound.volume = currentSoundValue; lightSound.play(); } else { var summonSound = LK.getSound('summon'); summonSound.volume = currentSoundValue; summonSound.play(); } // Visual feedback - make smaller on press and reduce opacity tween(self.children[0], { scaleX: 0.8, scaleY: 0.8, alpha: 0.6 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { // Return to normal size and restore opacity quickly tween(self.children[0], { scaleX: 1.0, scaleY: 1.0, alpha: 1.0 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { // Only re-enable button if we have enough mana and haven't reached limit LK.setTimeout(function () { // Check if we still have enough mana and haven't reached the counter limit if (temporaryNoteCounter < currentMana) { self.isDisabled = false; } }, 0); // Re-enable conditionally after color animation } }); } }); }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); self.health = vida_enemigo; self.maxHealth = vida_enemigo; self.damage = dano_enemigo; self.speed = 1.5; self.aumento_velocidad = 0; self.attackCooldown = 0; self.movimiento_enemigo = true; // Boolean to control enemy movement self.enemigo_oro = false; // Boolean to control golden statue state self.ataque_jugador = true; // Boolean to control if enemy can attack self.dormido = false; // Individual sleep state for this enemy self.nivel_enemigo; // Physics properties self.velocityY = 0; self.onGround = false; self.gravity = 0.5; // Beat effect properties self.baseY = 0; self.beatEffectTimer = 0; // Initialize miniola_colision property self.miniola_colision = false; self.vortice = false; // Assign enemy class based on level probabilities self.clase_enemigo = 'cuerpo_a_cuerpo'; // Default class self.probabilidad_cuerpo_a_cuerpo = 100; self.probabilidad_aero = 0; self.probabilidad_a_distancia = 0; // Get probabilities for current level if (nivel >= 1 && nivel <= probabilidades.length) { var levelProbs = probabilidades[nivel - 1]; self.probabilidad_cuerpo_a_cuerpo = levelProbs[1]; self.probabilidad_aero = levelProbs[2]; self.probabilidad_a_distancia = levelProbs[3]; // Generate random number 1-100 to determine class var randomValue = Math.floor(Math.random() * 100) + 1; if (randomValue <= self.probabilidad_cuerpo_a_cuerpo) { self.clase_enemigo = 'cuerpo_a_cuerpo'; } else if (randomValue <= self.probabilidad_cuerpo_a_cuerpo + self.probabilidad_aero) { self.clase_enemigo = 'aero'; self.isFlying = true; // Make enemy flying for aero class self.y = 1800; // Set flying height } else { self.clase_enemigo = 'a_distancia'; self.isRanged = true; // Make enemy ranged self.projectiles = []; // Add projectiles array } } // Function to update stats display self.updateStatsDisplay = function () { var goldState = self.enemigo_oro ? 'true' : 'false'; var sleepState = self.dormido ? 'true' : 'false'; var vorticeState = self.vortice ? 'true' : 'false'; var classInfo = '[' + self.clase_enemigo + ':' + self.probabilidad_cuerpo_a_cuerpo + '-' + self.probabilidad_aero + '-' + self.probabilidad_a_distancia + ']'; var randomIndexInfo = '[RndIdx:' + self.randomIndex + ']'; var statsString = self.health.toFixed(0) + '-' + self.damage.toFixed(0); self.statsText.setText(statsString); }; var enemyImageAsset = 'enemyUnit'; // Default fallback // Select random image based on enemy class var index_maximo = Math.floor(Math.random() * nivel) + 1; var index_random = 0; self.randomIndex = 0; // Initialize randomIndex property if (self.clase_enemigo === 'aero') { // Contar cuántos enemigos están desbloqueados for (var i = 0; i < enemigos_aereos.length; i++) { if (enemigos_aereos[i][0] <= nivel) { index_random++; } } if (index_random > 0) { // Elegir uno aleatoriamente entre los desbloqueados var randomIndex = Math.floor(Math.random() * index_random); self.randomIndex = randomIndex; enemyImageAsset = enemigos_aereos[randomIndex][1]; } else { // Si ningún enemigo está desbloqueado, usar uno por defecto enemyImageAsset = 'enemyUnit4'; } console.log("Index_maximo: " + index_maximo + " index_random: " + index_random + " random_index: " + randomIndex); } else if (self.clase_enemigo === 'cuerpo_a_cuerpo') { // Contar cuántos enemigos están desbloqueados for (var i = 0; i < enemigos_cuerpo.length; i++) { if (enemigos_cuerpo[i][0] <= nivel) { index_random++; } } if (index_random > 0) { // Elegir uno aleatoriamente entre los desbloqueados var randomIndex = Math.floor(Math.random() * index_random); self.randomIndex = randomIndex; enemyImageAsset = enemigos_cuerpo[randomIndex][1]; } else { // Si ningún enemigo está desbloqueado, usar uno por defecto enemyImageAsset = 'enemyUnit'; } } else if (self.clase_enemigo === 'a_distancia') { // Pick random image from ranged enemies list // Contar cuántos enemigos están desbloqueados for (var i = 0; i < enemigos_distancia.length; i++) { if (enemigos_distancia[i][0] <= nivel) { index_random++; } } if (index_random > 0) { // Elegir uno aleatoriamente entre los desbloqueados var randomIndex = Math.floor(Math.random() * index_random); self.randomIndex = randomIndex; enemyImageAsset = enemigos_distancia[randomIndex][1]; } else { // Si ningún enemigo está desbloqueado, usar uno por defecto enemyImageAsset = 'enemyUnit6'; } } self.nivel_enemigo = randomIndex; // Add enemy level to health and damage self.health += self.nivel_enemigo; self.maxHealth += self.nivel_enemigo; self.damage += self.nivel_enemigo; var enemyGraphics = self.attachAsset(enemyImageAsset, { anchorX: 0.5, anchorY: 0.5, width: 280, height: 280 }); // Health bar var healthBar = LK.getAsset('healthBar', { width: 100, height: 12 }); healthBar.anchor.set(0.5, 0.5); healthBar.y = -80; self.addChild(healthBar); self.healthBar = healthBar; // Stats text below enemy var statsText = new Text2('', { size: 48, fill: 0xFFFFFF, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); statsText.anchor.set(0.5, 0.5); statsText.y = 120; // Position below the enemy self.addChild(statsText); self.statsText = statsText; // Initialize stats display self.updateStatsDisplay(); self.update = function () { if (pausa == false) { // Set base Y position for beat effect if (self.baseY === 0) { self.baseY = 2186; // Ground level } // Apply beat effect if active and not golden statue if (self.beatEffectTimer > 0 && !self.enemigo_oro) { self.beatEffectTimer--; // Get current audio level and calculate rotation strength based on sensitivity difference var audioLevel = getGameMusicLevel(); var sensitivityDifference = Math.max(0, audioLevel - musicBeatThreshold); if (sensitivityDifference > 0) { var rotationAmount = Math.sin((120 - self.beatEffectTimer) * 0.3) * sensitivityDifference * 0.785; enemyGraphics.rotation = rotationAmount; } else { enemyGraphics.rotation = 0; // No rotation when no difference } } else { enemyGraphics.rotation = 0; // Reset rotation when effect ends or when golden statue // Apply gravity only to non-flying enemies if (!self.onGround && !self.isFlying) { self.velocityY += self.gravity; self.y += self.velocityY; } // Ground collision for non-flying enemies, flying height maintenance for aerial enemies var groundY = 2186; if (self.isFlying) { // Maintain flying height for aerial enemies self.y = 1800; self.baseY = 1800; self.onGround = false; } else if (self.y >= groundY) { self.y = groundY; self.baseY = groundY; // Update base position self.velocityY = 0; self.onGround = true; } else { self.onGround = false; } } if (self.attackCooldown > 0) { self.attackCooldown--; } // Apply tinting based on emerald and sleep states - prioritize emerald over sleep if (self.enemigo_oro) { // Emerald state active - disable movement and attack self.movimiento_enemigo = false; self.ataque_jugador = false; if (self.dormido) { // Both emerald and sleep active - use black tint for sleeping enemyGraphics.tint = 0x333333; // 80% black tint for sleeping // Start 6-second timer to clear both states if (!self.clearStatesTimer) { // Add debug text when timer activates self.clearStatesTimer = true; LK.setTimeout(function () { self.dormido = false; self.enemigo_oro = false; self.clearStatesTimer = false; }, 6000); } } else if (self.vortice) { // Vortice state active - apply blue tint and mirror effect enemyGraphics.tint = 0x4040ff; // 75% blue tint // Initialize vortice timer if not set if (self.vorticeTimer === undefined) { self.vorticeTimer = 0; } // Increment timer self.vorticeTimer++; // Apply mirror effect every 0.25 seconds (15 frames at 60fps) if (self.vorticeTimer >= 15) { volterar(self); self.vorticeTimer = 0; // Reset timer } } else { // Only emerald state - use green tint enemyGraphics.tint = 0x00FF00; // Pure green color (emerald statue effect) } } else { // No special state - enable movement and attack, but check for aerial collision if (self.clase_enemigo === 'aero') { var hasAerialCollision = false; for (var ae = 0; ae < enemies.length; ae++) { var otherEnemy = enemies[ae]; // Check if other enemy is also aerial and not the same enemy if (otherEnemy !== self && otherEnemy.clase_enemigo === 'aero') { var aerialDistance = Math.sqrt(Math.pow(self.x - otherEnemy.x, 2) + Math.pow(self.y - otherEnemy.y, 2)); if (aerialDistance < 600) { // Detection range for aerial enemies hasAerialCollision = true; break; // Stop checking once we find one aerial enemy nearby } } } if (hasAerialCollision) { self.movimiento_enemigo = false; self.ataque_jugador = true; } else { self.movimiento_enemigo = true; self.ataque_jugador = true; } } else { // For aerial enemies, also check their individual collision state if (self.clase_enemigo === 'aero' && self.hasAerialCollision) { self.movimiento_enemigo = false; } else { self.movimiento_enemigo = true; } self.ataque_jugador = true; } // Reset tint if needed if (enemyGraphics.tint === 0x000000 || enemyGraphics.tint === 0xFFD700 || enemyGraphics.tint === 0xFFFF00 || enemyGraphics.tint === 0xFF0000 || enemyGraphics.tint === 0x00FF00 || enemyGraphics.tint === 0x333333 || enemyGraphics.tint === 0x8080FF) { enemyGraphics.tint = 0xffffff; } } // Add dormido image when sleeping and particle system if (self.dormido && !self.dormidoImageActive) { // Create dormido image overlay self.dormidoImage = LK.getAsset('dormido', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150, width: 100, height: 100 }); self.addChild(self.dormidoImage); self.dormidoImageActive = true; // Create sleep particles continuously while sleeping self.sleepParticleTimer = 0; } else if (!self.dormido && self.dormidoImageActive) { // Remove dormido image when no longer sleeping if (self.dormidoImage) { self.dormidoImage.destroy(); self.dormidoImage = null; } self.dormidoImageActive = false; } // Generate sleep particles while dormido is active if (self.dormido && self.dormidoImageActive) { self.sleepParticleTimer = (self.sleepParticleTimer || 0) + 1; if (self.sleepParticleTimer >= 30) { // Every 0.5 seconds // Create sleep particles for (var sleepP = 0; sleepP < 3; sleepP++) { var sleepParticle = game.addChild(LK.getAsset('dormido', { anchorX: 0.5, anchorY: 0.5, x: self.x + (Math.random() - 0.5) * 80, y: self.y - 50 - Math.random() * 30, width: 30, height: 30 })); sleepParticle.alpha = 0.8; sleepParticle.tint = 0x9999FF; tween(sleepParticle, { y: sleepParticle.y - 120, alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 2500, easing: tween.easeOut, onFinish: function onFinish() { sleepParticle.destroy(); } }); } self.sleepParticleTimer = 0; } } // Movement is now controlled by movimiento_enemigo variable above // Check for aerial enemy vs aerial enemy encounters (aero class only) if (self.clase_enemigo === 'aero') { // Each aerial enemy tracks its own collision state independently self.hasAerialCollision = false; for (var ae = 0; ae < enemies.length; ae++) { var otherEnemy = enemies[ae]; // Check if other enemy is also aerial and not the same enemy if (otherEnemy !== self && otherEnemy.clase_enemigo === 'aero') { var aerialDistance = Math.sqrt(Math.pow(self.x - otherEnemy.x, 2) + Math.pow(self.y - otherEnemy.y, 2)); if (aerialDistance < 600) { // Detection range for aerial enemies // Stop movement when encountering another aerial enemy self.hasAerialCollision = true; // Visual feedback - brief flash to indicate encounter if (LK.ticks % 60 === 0) { // Flash every second tween(enemyGraphics, { tint: 0x00ffff // Cyan flash for aerial encounter }, { duration: 200, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0xffffff }, { duration: 200 }); } }); } break; // Stop checking once we find one aerial enemy nearby } } } // Stop movement if there's an aerial collision for this specific enemy if (self.hasAerialCollision) { self.movimiento_enemigo = false; } } // Check for warriors to attack and combat engagement var inCombat = false; for (var i = 0; i < warriors.length; i++) { var warrior = warriors[i]; // Use half width for enemy and warrior colliders but keep full height var enemyColliderWidth = 280 * 0.5; // Half of enemy width var warriorColliderWidth = 288 * 0.5; // Half of warrior width var distance = Math.sqrt(Math.pow(self.x - warrior.x, 2) + Math.pow(self.y - warrior.y, 2)); // Ranged enemies attack at 600 units distance if (self.clase_enemigo === 'a_distancia' && distance < 600) { // Mark as in combat to stop movement for ranged enemies inCombat = true; // Attack if cooldown is ready and can attack if (self.attackCooldown <= 0 && self.ataque_jugador) { self.attack(warrior); // Play combat sound when fighting with warrior playRandomCombatSound(); // Attack animation - flash red and scale up tween(enemyGraphics, { tint: 0xff0000, scaleX: 1.3, scaleY: 1.3 }, { duration: 200, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } break; } // Melee enemies attack at 250 units distance else if (self.clase_enemigo !== 'a_distancia' && distance < 250) { // Mark as in combat to stop movement inCombat = true; // Attack if cooldown is ready and can attack if (self.attackCooldown <= 0 && self.ataque_jugador) { self.attack(warrior); // Play combat sound when fighting with warrior playRandomCombatSound(); // Attack animation - flash red and scale up tween(enemyGraphics, { tint: 0xff0000, scaleX: 1.3, scaleY: 1.3 }, { duration: 200, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0xffffff, scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } break; } } // Set movimiento_enemigo based on combat state, statue status, and dormido state if (inCombat) { self.movimiento_enemigo = false; // Stop movement when attacking } else if (!self.enemigo_oro) { self.movimiento_enemigo = true; // Allow movement when not in combat, not a statue, and not sleeping } // Move towards player tower only if movement is enabled if (self.movimiento_enemigo) { // Move towards player tower (speed is controlled by combat state) var totalSpeed = self.speed + self.aumento_velocidad; self.x -= totalSpeed; } // Check if enemy is at the tower x position - stop movement to focus on tower attack var atTowerPosition = Math.abs(self.x - enemyTower.x) <= 50; // Control speed based on combat and tower position if (inCombat || atTowerPosition) { self.speed = 0; self.movimiento_enemigo = false; // Stop movement when at tower position or in combat } else { self.speed = 1.5; self.movimiento_enemigo = true; // Allow movement when not in combat or at tower } // Check collision with player tower and destroy enemy - use half width for enemy collider var towerColliderRadius = 500; // Fixed collider size, not scaled with tower var enemyColliderWidth = 280 * 0.5; // Half of enemy width var enemyColliderHeight = 280; // Full enemy height var towerDistance = Math.sqrt(Math.pow(self.x - playerTower.x, 2) + Math.pow(self.y - playerTower.y, 2)); // Use different collision detection for aerial vs ground enemies var collisionDetected = false; if (self.clase_enemigo === 'aero') { // For aerial enemies, use x-distance only since they fly at different height var xDistance = Math.abs(self.x - playerTower.x); collisionDetected = xDistance < towerColliderRadius; } else { // For ground enemies, use traditional distance-based collision collisionDetected = towerDistance < towerColliderRadius; } if (collisionDetected) { if (self.attackCooldown <= 0 && self.ataque_jugador) { self.attackPlayerTower(); } // Destroy enemy when it touches the tower self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } } // Check if enemy is off-screen and destroy if (self.x < -500 || self.x > 4500 || self.y > 3500) { self.destroy(); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } return; // Exit update to prevent further execution } // Update projectiles for ranged enemies if (self.clase_enemigo === 'a_distancia' && self.projectiles) { for (var p = self.projectiles.length - 1; p >= 0; p--) { var proj = self.projectiles[p]; proj.x += proj.speedX; proj.y += proj.speedY; var projectileDestroyed = false; // Check collision with warriors for (var w = 0; w < warriors.length; w++) { var warrior = warriors[w]; var projDistance = Math.sqrt(Math.pow(proj.x - warrior.x, 2) + Math.pow(proj.y - warrior.y, 2)); if (projDistance < 80) { // Hit warrior warrior.takeDamage(self.damage); proj.destroy(); self.projectiles.splice(p, 1); projectileDestroyed = true; break; } } // Check collision with walls (earth warrior walls) if (!projectileDestroyed) { for (var childIndex = 0; childIndex < game.children.length; childIndex++) { var child = game.children[childIndex]; if (child && child.isWall && child.health > 0) { var wallDistance = Math.sqrt(Math.pow(proj.x - child.x, 2) + Math.pow(proj.y - child.y, 2)); if (wallDistance < 200) { // Hit wall - deal damage to wall child.takeDamage(self.damage); proj.destroy(); self.projectiles.splice(p, 1); projectileDestroyed = true; break; } } } } // Remove projectiles that go off screen if (!projectileDestroyed && (proj.x > 4500 || proj.x < -500 || proj.y > 3500 || proj.y < -500)) { proj.destroy(); self.projectiles.splice(p, 1); } } } // Update health bar var healthPercent = self.health / self.maxHealth; self.healthBar.scaleX = healthPercent; } }; self.attack = function (target) { self.attackCooldown = 80; if (target === playerTower) { self.attackPlayerTower(); } else { // Ranged enemies create projectiles if (self.clase_enemigo === 'a_distancia') { // Create projectile for ranged enemies var projectile = game.addChild(LK.getAsset('proyectil_enemigo', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y - 20, width: 80, height: 80 })); // Calculate direction to target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var speed = 8; projectile.speedX = dx / distance * speed; projectile.speedY = dy / distance * speed; // Set projectile appearance for enemy projectiles projectile.tint = 0xff4444; // Red tint for enemy projectiles projectile.alpha = 0.9; // Add projectile to enemy's projectile array self.projectiles.push(projectile); // Auto-destroy projectile after 3 seconds tween(projectile, { alpha: projectile.alpha }, { duration: 3000, onFinish: function onFinish() { var index = self.projectiles.indexOf(projectile); if (index > -1) { self.projectiles.splice(index, 1); } projectile.destroy(); } }); } else { // Melee enemies deal direct damage target.takeDamage(self.damage); } } }; self.attackPlayerTower = function () { self.attackCooldown = 80; // Damage is now equal to the game level (nivel) var towerDamage = nivel; playerTowerHealth -= towerDamage; // Grant 1 mana to player when tower takes damage currentMana = Math.min(currentMana + 1, maxMana); updateManaDisplay(); // Play random tower damage sound var towerSounds = ['sonido_torre_1', 'sonido_torre_2', 'sonido_torre_3', 'sonido_torre_4']; var randomIndex = Math.floor(Math.random() * towerSounds.length); var sound = LK.getSound(towerSounds[randomIndex]); sound.volume = soundValue; sound.play(); // Create escombros particles when tower takes damage - quantity based on damage var particleCount = towerDamage * 12; // Multiply particle count by damage for (var escombrosIndex = 0; escombrosIndex < particleCount; escombrosIndex++) { // 360-degree explosion pattern var angle = escombrosIndex / particleCount * Math.PI * 2; // Distribute evenly in 360 degrees var force = (200 + Math.random() * 300) * 4; // Quadruple the force var velocityX = Math.cos(angle) * force; var velocityY = Math.sin(angle) * force; var escombrosParticle = game.addChild(LK.getAsset('escombro_torre_jugador', { anchorX: 0.5, anchorY: 0.5, x: playerTower.x, y: playerTower.y - 800, width: (60 + Math.random() * 40) * 5, height: (60 + Math.random() * 40) * 5, rotation: Math.random() * Math.PI * 2 })); escombrosParticle.alpha = 0.9; escombrosParticle.tint = 0x8B4513; // Brown color for debris // Add gravity and physics properties escombrosParticle.velocityX = velocityX * 0.02; // Scale down for smooth movement escombrosParticle.velocityY = velocityY * 0.02; escombrosParticle.gravity = 0.8; // Gravity effect // Animate escombros particles with physics tween(escombrosParticle, { y: playerTower.y + 200 + escombrosParticle.velocityY * 100, x: playerTower.x + escombrosParticle.velocityX * 100, alpha: 0, rotation: escombrosParticle.rotation + Math.PI * 4, scaleX: 0.1, scaleY: 0.1 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeOut, onFinish: function onFinish() { escombrosParticle.destroy(); } }); } if (playerTowerHealth <= 0) { LK.showGameOver(); } updateTowerHealthBars(); }; self.takeBurnDamage = function (initialDamage, duration, burnDamage) { // Apply initial damage self.takeDamage(initialDamage); // Don't apply burn if enemy is already dead if (self.health <= 0) { return; } // Start burn sound loop - store sound instance for stopping later var burnSound = LK.getSound('burn_sound'); burnSound.volume = soundValue * 0.8; self.burnSoundInstance = burnSound; burnSound.play(); // Set up sound looping var soundLoopTimer = LK.setInterval(function () { if (self.health > 0 && self.burnSoundInstance) { self.burnSoundInstance.play(); } }, 1000); // Loop every second // Add red tint at 80% opacity when burned tween(enemyGraphics, { tint: 0xff0000 }, { duration: 200, onFinish: function onFinish() { // Keep 80% red tint during burn var redTint = 0xff3333; // 80% red color tween(enemyGraphics, { tint: redTint }, { duration: 100 }); } }); // Apply burn effect over time var burnTicks = duration / 1000 * 60; // Convert duration to ticks (60fps) var burnInterval = 60; // Damage every second var ticksPerBurn = 0; var _burnEffect = function burnEffect() { ticksPerBurn++; if (ticksPerBurn >= burnInterval) { if (self.health > 0) { self.takeDamage(burnDamage); // Visual burn effect if (self.children[0]) { tween(self.children[0], { tint: 0xff4400 }, { duration: 200, onFinish: function onFinish() { if (self.children[0]) { tween(self.children[0], { tint: 0xff3333 }, { duration: 200 }); } } }); } } ticksPerBurn = 0; burnTicks -= burnInterval; } if (burnTicks > 0 && self.health > 0) { LK.setTimeout(_burnEffect, 16); // ~60fps } else { // Burn effect ended, stop sound loop and restore normal color LK.clearInterval(soundLoopTimer); self.burnSoundInstance = null; tween(enemyGraphics, { tint: 0xffffff }, { duration: 500 }); } }; _burnEffect(); }; self.takeDamage = function (damage) { self.health -= damage; // Update stats display to show current health self.updateStatsDisplay(); if (self.health <= 0) { // Destroy tornado if it exists if (self.tornado) { self.tornado.destroy(); self.tornado = null; } // Play enemy death sound var sound = LK.getSound('enemy_death'); sound.volume = soundValue; sound.play(); // Death animation - fade out and scale down tween(enemyGraphics, { alpha: 0, scaleX: 0.1, scaleY: 0.1, rotation: Math.PI * 2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } LK.setScore(LK.getScore() + 10); scoreText.setText('Score: ' + LK.getScore()); // 50% chance to drop mana if not at max capacity if (Math.random() < 0.5 && currentMana < maxMana) { // Create mana drop at enemy position var manaDrop = game.addChild(LK.getAsset('manaOrb', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); // Visual effect - pulsing and floating up tween(manaDrop, { scaleX: 3.0, scaleY: 3.0, y: self.y - 100 }, { duration: 800, easing: tween.easeOut }); // Add mana after short delay and destroy drop LK.setTimeout(function () { currentMana = Math.min(currentMana + 1, maxMana); updateManaDisplay(); // Flash effect on collection tween(manaDrop, { alpha: 0, scaleX: 4, scaleY: 4 }, { duration: 300, onFinish: function onFinish() { manaDrop.destroy(); } }); }, 600); } } }; return self; }); var Espora = Container.expand(function (targetWarrior) { var self = Container.call(this); var esporaGraphics = self.attachAsset('esporas', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100 }); self.targetWarrior = targetWarrior; self.velocityX = (Math.random() - 0.5) * 1; // Minimal random horizontal velocity self.velocityY = -2 - Math.random() * 3; // Initial upward velocity self.gravity = 0.1; // Reduced gravity force self.lifespan = 300; // 5 seconds at 60fps self.age = 0; // Set initial position near target warrior self.x = targetWarrior.x + (Math.random() - 0.5) * 100; self.y = targetWarrior.y + (Math.random() - 0.5) * 50; self.update = function () { self.age++; // Apply gravity self.velocityY += self.gravity; // Update position self.x += self.velocityX; self.y += self.velocityY; // Follow target warrior with gentle attraction if (self.targetWarrior && !self.targetWarrior.isBeingDestroyed) { var distanceToTarget = Math.sqrt(Math.pow(self.targetWarrior.x - self.x, 2) + Math.pow(self.targetWarrior.y - self.y, 2)); if (distanceToTarget > 100) { // Gently drift towards warrior var directionX = (self.targetWarrior.x - self.x) / distanceToTarget; var directionY = (self.targetWarrior.y - self.y) / distanceToTarget; self.velocityX += directionX * 0.05; // Reduced attraction force self.velocityY += directionY * 0.05; // Reduced attraction force } } // Apply air resistance to reduce horizontal drift self.velocityX *= 0.95; // Air resistance dampens horizontal movement // Ground collision var groundY = 2186; if (self.y >= groundY) { self.y = groundY; self.velocityY = -self.velocityY * 0.3; // Bounce with energy loss self.velocityX *= 0.7; // Increased friction on ground contact } // Fade out over lifetime var fadeAlpha = 1.0 - self.age / self.lifespan; esporaGraphics.alpha = Math.max(0, fadeAlpha); // Check collision with enemies for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < 150) { // Collision detected - deal damage FIRST before applying any effects var spoeDamage = self.targetWarrior ? self.targetWarrior.damage : 6; // Use warrior's damage or fallback to 6 // Ensure damage is dealt immediately before any other effects enemy.takeDamage(spoeDamage); // Apply dormido state after damage enemy.dormido = true; // Then apply emerald statue effect enemy.enemigo_oro = true; // Create visual effect at collision point var explosion = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 120, height: 120 })); explosion.tint = 0x44ff44; // Green color for spore explosion explosion.alpha = 0.8; // Animate explosion with growing and fading effect if (explosion && explosion.children && explosion.children[0]) { tween(explosion, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); } else { // Fallback destruction after 0.5 seconds if tween fails LK.setTimeout(function () { if (explosion && explosion.parent) { explosion.destroy(); } }, 500); } // Create spore particles around impact for (var p = 0; p < 4; p++) { var angle = p / 4 * Math.PI * 2; var particleDistance = 60 + Math.random() * 40; var particleX = self.x + Math.cos(angle) * particleDistance; var particleY = self.y + Math.sin(angle) * particleDistance; var particle = game.addChild(LK.getAsset('esporas', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 40 + Math.random() * 20, height: 40 + Math.random() * 20 })); particle.tint = 0x88ff88; particle.alpha = 0.9; tween(particle, { x: particleX, y: particleY, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 500 + Math.random() * 300, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Destroy spore after collision self.destroy(); var index = esporas.indexOf(self); if (index > -1) { esporas.splice(index, 1); } return; // Exit update after collision } } // Destroy after lifespan if (self.age >= self.lifespan) { self.destroy(); var index = esporas.indexOf(self); if (index > -1) { esporas.splice(index, 1); } } }; return self; }); var Meteorito = Container.expand(function () { var self = Container.call(this); var meteoritoGraphics = self.attachAsset('meteorito_elemental', { anchorX: 0.5, anchorY: 0.5, scaleX: 6.0, scaleY: 6.0 }); self.speed = 20; self.rotationSpeed = 0.1; // Store the base scale to prevent shrinking self.baseScale = 6.0; // Rainbow cycling effect for mixed element meteorite meteoritoGraphics.tint = 0xffffff; // Add rainbow cycling animation var rainbowColors = [0xff4400, 0x1e90ff, 0x8b4513, 0x44ff44, 0xffff00]; var colorIndex = 0; var colorTimer = 0; self.updateRainbow = function () { colorTimer++; if (colorTimer >= 10) { // Change color every 10 frames colorTimer = 0; colorIndex = (colorIndex + 1) % rainbowColors.length; meteoritoGraphics.tint = rainbowColors[colorIndex]; } }; // Meteorite maintains full opacity (no alpha animation) meteoritoGraphics.alpha = 1.0; self.update = function () { // Calculate target position (center of map at ground level) var targetX = 1920; // Center of the game map (3840/2) var targetY = 2186; // Ground level // Calculate direction vector toward target var deltaX = targetX - self.x; var deltaY = targetY - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Move diagonally toward target - double speed if (distance > self.speed) { // Normalize direction and apply double speed var directionX = deltaX / distance; var directionY = deltaY / distance; self.x += directionX * self.speed; self.y += directionY * self.speed; } else { // Reached target position - explode self.explode(); return; } // Update rainbow effect self.updateRainbow(); // Remove if off-screen if (self.y > 3000) { self.destroy(); var index = meteoritos.indexOf(self); if (index > -1) { meteoritos.splice(index, 1); } } }; self.explode = function () { // Play meteorite explosion sound var explosionSound = LK.getSound('explosion_meteorito'); explosionSound.volume = soundValue; explosionSound.play(); // Generate random color for explosion var randomColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, 0xff8000, 0x8000ff, 0x80ff00, 0xff0080]; var explosionColor = randomColors[Math.floor(Math.random() * randomColors.length)]; // Create explosion effect var explosion = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 400, height: 400 })); explosion.tint = explosionColor; explosion.alpha = 0.9; // Animate massive explosion with enormous scale tween(explosion, { scaleX: 40.0, scaleY: 40.0, alpha: 0 }, { duration: 2500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Create massive particle explosion for (var i = 0; i < 64; i++) { var angle = i / 64 * Math.PI * 2; var distance = 1600 + Math.random() * 2400; var particleEndX = self.x + Math.cos(angle) * distance; var particleEndY = self.y + Math.sin(angle) * distance; // Generate random color for each particle var particleColor = randomColors[Math.floor(Math.random() * randomColors.length)]; var particle = game.addChild(LK.getAsset('explosion_fuego', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 240 + Math.random() * 360, height: 240 + Math.random() * 360 })); particle.tint = particleColor; particle.alpha = 0.9; tween(particle, { x: particleEndX, y: particleEndY, alpha: 0, scaleX: 0.05, scaleY: 0.05 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Hide apocalyptic notes and reset counter when meteorite explodes (before explosion starts) ocultar_notas_apocalitica(); // Deal massive damage to all enemies on the map for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; enemy.takeDamage(1000000000000000000); // Deal 1 quintillion damage to guarantee destruction } // Flash screen effect with longer duration for massive impact LK.effects.flashScreen(explosionColor, 2000); // Clear element list first, then call function to create new random meteorite combination elementList = []; // Play the meteorite melody instantly when exploding playMelodySequenceWithIntervals(['fire', 'water', 'earth', 'wind', 'light'], [0, 200, 200, 200, 200]); crea_combinacion_meteorito(); // Remove meteorite self.destroy(); var index = meteoritos.indexOf(self); if (index > -1) { meteoritos.splice(index, 1); } }; return self; }); var Nube = Container.expand(function () { var self = Container.call(this); // Create cloud with random uniform scale to maintain proportions - 50% larger overall var uniformScale = 0.75 + Math.random() * 2.25; // Random scale between 75% and 300% (50% larger than before) var cloudGraphics = self.attachAsset('shape', { anchorX: 0.5, anchorY: 0.5, scaleX: uniformScale, scaleY: uniformScale, alpha: 0.4 + Math.random() * 0.5 // Random opacity between 40% and 90% }); // Random cloud speed (slower clouds for depth) - will be adjusted by height layer self.speed = 0.5 + Math.random() * 1.0; // Speed between 0.5 and 1.5 // Adjust speed based on scale to simulate depth (smaller clouds move slower) self.baseSpeed = self.speed; // Store initial Y for floating animation self.initialY = 0; // Will be set when cloud is positioned // Method to start floating animation after positioning self.startFloating = function () { if (self.initialY === 0) { self.initialY = self.y; // Store the manually set position } var floatAmount = 20 + Math.random() * 30; var floatDuration = 3000 + Math.random() * 2000; tween(self, { y: self.initialY + floatAmount }, { duration: floatDuration, yoyo: true, repeat: -1, easing: tween.easeInOut }); }; self.update = function () { // Adjust speed based on scale for depth effect (smaller = slower, further away) self.speed = self.baseSpeed * (self.scaleX * 0.7 + 0.3); // Scale speed between 30% and 100% // Move cloud to the right self.x += self.speed; // Optimized cloud cleanup - check bounds more efficiently and prevent memory leaks var cloudWidth = cloudGraphics.width * self.scaleX; // Account for actual scaled width if (self.x > 4500 + cloudWidth || self.y < -500 || self.y > 3000) { // Remove from clouds array first to prevent duplicate removal attempts var index = clouds.indexOf(self); if (index > -1) { clouds.splice(index, 1); } // Then destroy the cloud object if (self.parent) { self.destroy(); } return; // Exit update early after destruction } }; return self; }); var Warrior = Container.expand(function (element, tier) { var self = Container.call(this); var assetName = element + 'Warrior_nivel_' + tier; var warriorGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5, width: 288, height: 288 }); self.element = element; self.tier = tier; // Add wind warrior tier 5 attack tracking if (element === 'wind' && tier === 5) { self.attackedEnemies = []; // Track which enemies have been attacked } // Base stats - same as enemies var baseDamage = 1; var baseHealth = 5; // Set elemental-specific stats based on tier if (element === 'fire') { if (tier === 1) { self.maxHealth = 5; self.damage = 6; self.speed = 2.2; self.attackCooldown = 100; } else if (tier === 2) { self.maxHealth = 10; self.damage = 6; self.speed = 2.4; self.attackCooldown = 50; } else if (tier === 3) { self.maxHealth = 15; self.damage = 20; self.speed = 2.6; self.attackCooldown = 100; self.burnDuration = 3000; // 3 seconds self.burnDamage = 6; self.canBurn = true; } else if (tier === 4) { self.maxHealth = 20; self.damage = 24; self.speed = 2.8; self.attackCooldown = 60; self.canAreaDamage = true; self.areaRange = 400; self.burnDuration = 5000; // 5 seconds self.burnDamage = 8; self.canBurn = true; } else if (tier === 5) { self.maxHealth = 25; self.damage = 50; self.speed = 3.0; self.attackCooldown = 60; self.canActivateFireMode = true; self.fireModeActive = false; } } else if (element === 'water') { if (tier === 1) { self.maxHealth = 5; self.damage = 1; self.speed = 1.8; self.attackCooldown = 120; } else if (tier === 2) { self.maxHealth = 10; self.damage = 2; self.speed = 2.0; self.attackCooldown = 120; self.selfHealRate = 2; // Heals 2 HP per second self.selfHealTimer = 0; } else if (tier === 3) { self.maxHealth = 15; self.damage = 4; self.speed = 2.2; self.attackCooldown = 120; self.selfHealRate = 3.5; // Heals 3.5 HP per second self.selfHealTimer = 0; self.auraRange = 500; self.auraDamageBonus = 4; self.explosionTimer = 0; self.explosionInterval = 120; // Every 2 seconds } else if (tier === 4) { self.maxHealth = 20; self.damage = 6; self.speed = 2.4; self.attackCooldown = 120; self.selfHealRate = 4; // Heals 4 HP per second self.selfHealTimer = 0; self.selfHealRate = 5; // Heals 5 HP per second self.selfHealTimer = 0; self.auraRange = 800; self.auraDamageBonus = 10; self.explosionTimer = 0; self.explosionInterval = 120; // Every 2 seconds } else if (tier === 5) { self.maxHealth = 25; self.damage = 8; self.speed = 2.5; self.attackCooldown = 120; self.selfHealRate = 5; // Heals 5 HP per second self.selfHealTimer = 0; self.waveTimer = 0; self.waveInterval = 300; // Every 5 seconds } self.canHeal = true; self.healCooldown = 0; self.isRanged = true; self.projectiles = []; } else if (element === 'earth') { if (tier === 1) { self.maxHealth = 20; self.damage = 1; self.speed = 1.8; self.attackCooldown = 120; } else if (tier === 2) { self.maxHealth = 80; self.damage = 6; self.speed = 1.8; self.attackCooldown = 120; } else if (tier === 3) { self.maxHealth = 120; self.damage = 6; self.speed = 1.8; self.attackCooldown = 120; self.canCreateWalls = true; self.wallHealth = 400; self.distancia_recorrida = Math.random() * 1000; // Random start between 0-1000 } else if (tier === 4) { self.maxHealth = 160; self.damage = 8; self.speed = 1.8; self.attackCooldown = 120; self.canCreateWalls = true; self.wallHealth = 800; self.damageImmunity = 10; // Immune to damage <= 10 self.distancia_recorrida = Math.random() * 1000; // Random start between 0-1000 } else if (tier === 5) { self.maxHealth = 250; self.damage = 10; self.speed = 1.8; self.attackCooldown = 120; self.canDragEnemies = true; self.canSelfDestruct = true; self.damageImmunity = 20; // Immune to damage <= 20 self.distancia_recorrida = Math.random() * 1000; // Random start between 0-1000 } } else if (element === 'light') { if (tier === 1) { self.maxHealth = 5; self.damage = 1; self.speed = 2; self.attackCooldown = 100; } else if (tier === 2) { self.maxHealth = 15; self.damage = 2; self.speed = 2.2; self.attackCooldown = 100; self.canSecondExplosion = true; } else if (tier === 3) { self.maxHealth = 15; self.damage = 5; self.speed = 2.4; self.attackCooldown = 100; self.canCreateClouds = true; } else if (tier === 4) { self.maxHealth = 20; self.damage = 15; self.speed = 2.6; self.attackCooldown = 100; self.canLaserBeam = true; } else if (tier === 5) { self.maxHealth = 25; self.damage = 5; self.speed = 2.8; self.attackCooldown = 100; self.canTrackingClouds = true; } else { self.maxHealth = 5; self.damage = 1; self.speed = 2; self.attackCooldown = 100; } self.isRanged = true; self.canAreaDamage = true; self.projectiles = []; } else if (element === 'wind') { if (tier === 1) { self.maxHealth = 5; self.damage = 2; self.speed = 3; self.attackCooldown = 60; } else if (tier === 2) { self.maxHealth = 10; self.damage = 4; self.speed = 3.4; self.attackCooldown = 60; self.goldenStatueChance = 0.2; // 20% chance } else if (tier === 3) { self.maxHealth = 15; self.damage = 6; self.speed = 3.8; self.attackCooldown = 60; self.sleepParticleSystem = true; self.sleepDuration = 5000; // 5 seconds } else if (tier === 4) { self.maxHealth = 20; self.damage = 10; self.speed = 5; self.attackCooldown = 60; self.emeraldSlaveChance = 0.2; // 20% chance self.sleepDuration = 10000; // 10 seconds } else if (tier === 5) { self.maxHealth = 25; self.damage = 20; self.speed = 1.05; self.attackCooldown = 60; } else { self.maxHealth = baseHealth; self.damage = baseDamage; self.speed = 2.8; } self.isFlying = true; self.isRanged = true; self.projectiles = []; } else { // Default stats for any other element self.maxHealth = baseHealth; self.damage = baseDamage; self.speed = 2; } self.health = self.maxHealth; self.aumento_velocidad = 0; // Attack cooldown will be set by elemental stats above if (!self.attackCooldown) { self.attackCooldown = 0; // Default fallback } self.baseCooldown = self.attackCooldown || 60; // Store base cooldown for resets self.target = null; self.isBeingDestroyed = false; // Flag to prevent multiple destruction attempts self.activar_movimiento = true; // Boolean to control if warrior can move // Physics properties self.velocityY = 0; self.onGround = false; self.gravity = 0.5; // Beat effect properties self.baseY = 0; self.beatEffectTimer = 0; // Set warrior size based on tier level - using scale multipliers for visible differences var tierScale = 1.0; // Base scale for tier 1 if (tier === 1) { tierScale = 1.0; // Tier 1: normal size (288px) } else if (tier === 2) { tierScale = 1.17; // Tier 2: 17% bigger (338px) } else if (tier === 3) { tierScale = 1.35; // Tier 3: 35% bigger (388px) } else if (tier === 4) { tierScale = 1.52; // Tier 4: 52% bigger (438px) } else if (tier === 5) { tierScale = 1.69; // Tier 5: 69% bigger (488px) } // Store the tier scale for later reference self.tierScale = tierScale; // Apply tier-based scaling immediately and permanently warriorGraphics.scaleX = tierScale; warriorGraphics.scaleY = tierScale; // Apply a simple bounce effect that preserves the tier scale var bounceScale = tierScale * 1.1; tween(warriorGraphics, { scaleX: bounceScale, scaleY: bounceScale }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Ensure we return to the exact tier scale, not a reference that might change tween(warriorGraphics, { scaleX: self.tierScale, scaleY: self.tierScale }, { duration: 200, easing: tween.easeInOut }); } }); // Health bar var healthBar = LK.getAsset('warriorHealthBar', { width: 120, height: 16 }); healthBar.anchor.set(0.5, 0.5); healthBar.y = -100; self.addChild(healthBar); self.healthBar = healthBar; // Stats text below warrior var statsText = new Text2('', { size: 48, fill: 0xFFFFFF, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); statsText.anchor.set(0.5, 0.5); statsText.y = 120; // Position below the warrior self.addChild(statsText); self.statsText = statsText; // Function to update stats display self.updateStatsDisplay = function () { var attackSpeed = self.baseCooldown || 60; var range = 600; if (self.isRanged) { if (self.element === 'wind') { if (self.tier === 2) { range = 700; } else if (self.tier === 3) { range = 800; } else if (self.tier === 4) { range = 900; } else if (self.tier === 5) { range = 600; } } } else { range = 250; } var totalSpeed = self.speed + (self.aumento_velocidad || 0); var auraInfo = ''; if (self.waterAuraCount && self.waterAuraCount > 0) { var speedReduction = self.waterAuraCount * 10; auraInfo = ' [Aura:' + speedReduction + '%]'; } // Add distance counter for earth warriors tier 3+ var distanceInfo = ''; if (self.element === 'earth' && self.tier >= 3 && self.distancia_recorrida !== undefined) { distanceInfo = ' [Dist:' + Math.round(self.distancia_recorrida) + ']'; } // Add conversion probability for wind warriors var conversionInfo = ''; if (self.element === 'wind') { var conversionChance = 0; if (self.tier === 2 && self.goldenStatueChance) { conversionChance = Math.round(self.goldenStatueChance * 100); conversionInfo = ' [Conv:' + conversionChance + '%]'; } } var displaySpeed = Math.round(attackSpeed / 100 * 100) / 100; // Round to 2 decimal places var statsString = self.health + '-' + self.damage + '-' + displaySpeed.toFixed(1); self.statsText.setText(statsString); }; // Initialize stats display self.updateStatsDisplay(); self.update = function () { if (pausa == false) { // Set base Y position for beat effect if (self.baseY === 0) { if (self.isFlying) { self.baseY = 1800; // Flying height } else { self.baseY = 2186; // Ground level } } // Apply beat effect if active if (self.beatEffectTimer > 0) { self.beatEffectTimer--; // Get current audio level and calculate rotation strength based on sensitivity difference var audioLevel = getGameMusicLevel(); var sensitivityDifference = Math.max(0, audioLevel - musicBeatThreshold); if (sensitivityDifference > 0) { var rotationAmount = Math.sin((120 - self.beatEffectTimer) * 0.3) * sensitivityDifference * 0.785; warriorGraphics.rotation = rotationAmount; } else { warriorGraphics.rotation = 0; // No rotation when no difference } } else { warriorGraphics.rotation = 0; // Reset rotation when effect ends // Apply gravity only to non-flying units if (!self.isFlying && !self.onGround) { self.velocityY += self.gravity; self.y += self.velocityY; } // Ground collision for non-flying units var groundY = 2186; if (!self.isFlying && self.y >= groundY) { self.y = groundY; self.baseY = groundY; // Update base position self.velocityY = 0; self.onGround = true; } else if (!self.isFlying) { self.onGround = false; } // Flying units hover at a specific height if (self.isFlying) { self.y = 1800; // Fly above ground level self.baseY = 1800; // Update base position } } // Move towards enemy tower only if movement is enabled if (!self.target && self.activar_movimiento) { var totalSpeed = self.speed + (self.aumento_velocidad || 0); var previousX = self.x; // Store previous position for distance calculation self.x += totalSpeed; // Track distance for earth warriors tier 3+ if (self.element === 'earth' && self.tier >= 3 && self.distancia_recorrida !== undefined) { // Add 1 per movement unit (remove random offset from movement tracking) self.distancia_recorrida += 1; // Update stats display in real time to show distance changes self.updateStatsDisplay(); } } // Attack logic if (self.attackCooldown > 0) { self.attackCooldown--; } // Light warrior tier 4 automatic laser beam attack if (self.element === 'light' && self.tier === 4 && self.canLaserBeam) { // Fire laser beam every 3 seconds (180 frames) instead of projectiles if (self.attackCooldown <= 0) { // Find enemies in front of warrior var enemiesInRange = []; for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < 2000 && enemy.x > self.x) { // Enemies in front and within 2000 units enemiesInRange.push(enemy); } } if (enemiesInRange.length > 0) { // Create laser beam from warrior position to far right of map using rayo_amarillo image asset var laserBeam = game.addChild(LK.getAsset('rayo_amarillo', { anchorX: 0.0, anchorY: 0.5, x: self.x, y: self.y, width: 4000, height: 50 })); laserBeam.alpha = 0.9; // Animate laser beam appearance and fade tween(laserBeam, { scaleY: 3.0, alpha: 1.0 }, { duration: 200, onFinish: function onFinish() { tween(laserBeam, { alpha: 0, scaleY: 0.1 }, { duration: 800, onFinish: function onFinish() { laserBeam.destroy(); } }); } }); // Deal damage to all enemies in laser path for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Check if enemy is in laser path (Y coordinate within beam height) var yDistance = Math.abs(enemy.y - self.y); var xDistance = enemy.x - self.x; if (yDistance < 100 && xDistance > 0 && xDistance < 4000) { var finalDamage = self.damage + (self.auraDamageBonus || 0); enemy.takeDamage(finalDamage); } } self.attackCooldown = 180; // 3 second cooldown } } } // Update heal cooldown for water warriors if (self.canHeal && self.healCooldown > 0) { self.healCooldown--; } // Water warrior self-healing (tier 2+) if (self.element === 'water' && self.tier >= 2 && self.selfHealTimer !== undefined) { self.selfHealTimer++; if (self.selfHealTimer >= 60) { // Every second (60 frames at 60fps) if (self.health < self.maxHealth) { self.health = Math.min(self.health + self.selfHealRate, self.maxHealth); // Self-healing visual effect tween(warriorGraphics, { tint: 0x00ff00 }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff }, { duration: 300 }); } }); // Add healing particles for tier 2 water warrior self-healing if (self.tier === 2) { for (var p = 0; p < 2; p++) { var particle = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: self.x + (Math.random() - 0.5) * 80, y: self.y + (Math.random() - 0.5) * 80, width: 120 + Math.random() * 80, height: 120 + Math.random() * 80 })); particle.alpha = 0.8; tween(particle, { y: self.y - 100 - Math.random() * 50, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } } self.selfHealTimer = 0; } } // Water warrior aura effects (tier 3+) if (self.element === 'water' && self.tier >= 3 && self.auraRange !== undefined) { // Apply aura damage bonus and attack speed reduction to nearby allies for (var j = 0; j < warriors.length; j++) { var ally = warriors[j]; if (ally !== self && ally.element !== 'water') { var allyDistance = Math.sqrt(Math.pow(self.x - ally.x, 2) + Math.pow(self.y - ally.y, 2)); if (allyDistance < self.auraRange) { // Count number of water tier 3+ warriors affecting this ally var waterAuraCount = 0; for (var k = 0; k < warriors.length; k++) { var waterWarrior = warriors[k]; if (waterWarrior.element === 'water' && waterWarrior.tier >= 3) { var waterDistance = Math.sqrt(Math.pow(waterWarrior.x - ally.x, 2) + Math.pow(waterWarrior.y - ally.y, 2)); if (waterDistance < waterWarrior.auraRange) { waterAuraCount++; } } } // Only apply speed changes if aura count has changed var previousAuraCount = ally.waterAuraCount || 0; if (waterAuraCount !== previousAuraCount) { // Initialize originalBaseCooldown if not set if (!ally.originalBaseCooldown) { ally.originalBaseCooldown = ally.baseCooldown || 60; } // Calculate cumulative speed reduction: each aura reduces by 10% var speedReduction = waterAuraCount * 10; // 10% per aura var finalSpeedMultiplier = Math.max(0.1, (100 - speedReduction) / 100); // Minimum 0.1 (10% attack speed) // Apply the speed reduction to original base cooldown (multiply to reduce cooldown = attack faster) ally.baseCooldown = Math.floor(ally.originalBaseCooldown * finalSpeedMultiplier); ally.waterAuraCount = waterAuraCount; ally.auraAffected = waterAuraCount > 0; ally.auraDamageBonus = waterAuraCount > 0 ? self.auraDamageBonus : 0; } else { // Keep existing values without recalculation ally.waterAuraCount = waterAuraCount; ally.auraAffected = waterAuraCount > 0; ally.auraDamageBonus = waterAuraCount > 0 ? self.auraDamageBonus : 0; } // Create continuous particles for allies under aura effect (without tinting) if (waterAuraCount > 0) { // Create continuous particles (7.5x larger) if (LK.ticks % 60 === 0) { // Every 1 second for (var p = 0; p < 1; p++) { var particle = game.addChild(LK.getAsset('particulas_delfin', { anchorX: 0.5, anchorY: 0.5, x: ally.x + (Math.random() - 0.5) * 80, y: ally.y + (Math.random() - 0.5) * 80, width: (30 + Math.random() * 15) * 7.5, height: (30 + Math.random() * 15) * 7.5 })); particle.alpha = 0.8; particle.tint = 0xff22aa; // More intense pink color for dolphin particles tween(particle, { y: ally.y - 100 - Math.random() * 40, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } } // Visual feedback when aura count changes if (previousAuraCount !== waterAuraCount && waterAuraCount > 0) { // Blue glow effect when gaining aura tween(ally.children[0], { tint: 0x00aaff }, { duration: 300, onFinish: function onFinish() { tween(ally.children[0], { tint: 0xffffff // Back to white instead of pink }, { duration: 300 }); } }); // Create visual particles around affected ally for (var p = 0; p < 1; p++) { var particle = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: ally.x + (Math.random() - 0.5) * 60, y: ally.y + (Math.random() - 0.5) * 60, width: 40 + Math.random() * 20, height: 40 + Math.random() * 20 })); particle.alpha = 0.6; particle.tint = 0x00aaff; // Blue for speed aura tween(particle, { y: ally.y - 80 - Math.random() * 30, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } } } } } // Water warrior explosion healing (tier 3+) if (self.element === 'water' && self.tier >= 3 && self.explosionTimer !== undefined) { self.explosionTimer++; if (self.explosionTimer >= self.explosionInterval) { // Every 2 seconds // Create healing explosion at warrior position var explosion = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, width: 400, height: 400 })); explosion.alpha = 0.7; explosion.tint = 0x00aaff; // Animate explosion tween(explosion, { scaleX: 3.0, scaleY: 3.0, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Heal all allies within 400 units for (var j = 0; j < warriors.length; j++) { var ally = warriors[j]; var healDistance = Math.sqrt(Math.pow(self.x - ally.x, 2) + Math.pow(self.y - ally.y, 2)); if (healDistance < 400) { // Apply aura damage bonus to explosion healing amount var finalHealAmount = self.damage + (self.auraDamageBonus || 0); ally.health = Math.min(ally.health + finalHealAmount, ally.maxHealth); // Healing particles for affected allies for (var p = 0; p < 2; p++) { var particle = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: ally.x + (Math.random() - 0.5) * 80, y: ally.y + (Math.random() - 0.5) * 80, width: 60 + Math.random() * 40, height: 60 + Math.random() * 40 })); particle.alpha = 0.8; tween(particle, { y: ally.y - 100 - Math.random() * 50, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } } self.explosionTimer = 0; } } // Water warrior healing wave (tier 5) if (self.element === 'water' && self.tier === 5 && self.waveTimer !== undefined) { self.waveTimer++; if (self.waveTimer >= self.waveInterval) { // Every 5 seconds // Create healing wave from player tower to end of map var wave = game.addChild(LK.getAsset('ola', { anchorX: 0.0, anchorY: 0.5, x: playerTower.x, y: playerTower.y - 200, width: 800, height: 1000 })); wave.tint = 0x00ffff; // Store wave damage for collision detection wave.finalWaveDamage = self.damage + (self.auraDamageBonus || 0); // Track which enemies have been hit by this wave wave.hitEnemies = []; // Add wave update method for collision detection wave.update = function () { // Check collision with enemies for damage for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Check if enemy hasn't been hit by this wave yet if (wave.hitEnemies.indexOf(enemy) === -1) { // Check collision between wave and enemy - use wave center for better detection var waveCenterX = wave.x + wave.width * 0.5; // Center of wave var waveCenterY = wave.y; // Center Y position var waveDistance = Math.sqrt(Math.pow(waveCenterX - enemy.x, 2) + Math.pow(waveCenterY - enemy.y, 2)); if (waveDistance < 300) { // Enemy is hit by wave - mark immediately to prevent multiple hits wave.hitEnemies.push(enemy); // Deal damage to enemy enemy.takeDamage(wave.finalWaveDamage); // Create splash effect at impact using salpicadura asset var splash = game.addChild(LK.getAsset('salpicadura', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, width: 350, height: 350 })); splash.alpha = 0.9; splash.tint = 0x00ffff; // Animate splash with scale and rotation tween(splash, { scaleX: 3.0, scaleY: 3.0, alpha: 0, rotation: Math.PI * 0.75 }, { duration: 700, easing: tween.easeOut, onFinish: function onFinish() { splash.destroy(); } }); // Create additional water splash particles in a circular pattern for (var splashIndex = 0; splashIndex < 12; splashIndex++) { var angle = splashIndex / 12 * Math.PI * 2; var distance = 100 + Math.random() * 80; var splashEndX = enemy.x + Math.cos(angle) * distance; var splashEndY = enemy.y + Math.sin(angle) * distance; var splashParticle = game.addChild(LK.getAsset('salpicadura', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, width: 60 + Math.random() * 40, height: 60 + Math.random() * 40 })); splashParticle.alpha = 0.8; splashParticle.tint = 0x00ddff; // Animate splash particles spreading outward tween(splashParticle, { x: splashEndX, y: splashEndY, alpha: 0, scaleX: 0.2, scaleY: 0.2, rotation: Math.random() * Math.PI * 2 }, { duration: 500 + Math.random() * 300, easing: tween.easeOut, onFinish: function onFinish() { splashParticle.destroy(); } }); } } } } // Check collision with warriors for speed boost for (var w = 0; w < warriors.length; w++) { var warrior = warriors[w]; // Check if warrior hasn't been hit by this wave yet if (wave.hitEnemies.indexOf(warrior) === -1) { // Check collision between wave and warrior - use wave center for better detection var waveCenterX = wave.x + wave.width * 0.5; // Center of wave var waveDistance = Math.sqrt(Math.pow(waveCenterX - warrior.x, 2) + Math.pow(wave.y - warrior.y, 2)); if (waveDistance < 300) { // Warrior is hit by wave wave.hitEnemies.push(warrior); // Increase speed only if not already boosted by tier 5 water warrior wave if (!warrior.tier5WaveSpeedBoosted) { warrior.aumento_velocidad = (warrior.aumento_velocidad || 0) + 2; warrior.tier5WaveSpeedBoosted = true; // Visual effect for speed boost tween(warrior.children[0], { tint: 0x00ffff }, { duration: 500, onFinish: function onFinish() { tween(warrior.children[0], { tint: 0xffffff }, { duration: 500 }); } }); warrior.updateStatsDisplay(); } } } } }; // Animate wave across the map tween(wave, { x: 4500, // Move to end of map scaleX: 2.0 }, { duration: 3000, easing: tween.linear, onFinish: function onFinish() { wave.destroy(); } }); // Heal all allies to full health immediately for (var j = 0; j < warriors.length; j++) { warriors[j].health = warriors[j].maxHealth; } self.waveTimer = 0; } } // Water warriors heal nearby allies if (self.canHeal && self.healCooldown <= 0) { self.healCooldown = 120; // 2 seconds // Heal nearby warriors for (var j = 0; j < warriors.length; j++) { var ally = warriors[j]; if (ally !== self) { var allyDistance = Math.sqrt(Math.pow(self.x - ally.x, 2) + Math.pow(self.y - ally.y, 2)); if (allyDistance < 600 && ally.health < ally.maxHealth) { // Apply aura damage bonus to healing amount var finalHealAmount = self.damage + (self.auraDamageBonus || 0); ally.health = Math.min(ally.health + finalHealAmount, ally.maxHealth); // Healing effect tween(ally.children[0], { tint: 0x00ff88 }, { duration: 200, onFinish: function onFinish() { tween(ally.children[0], { tint: 0xffffff }, { duration: 200 }); } }); // Create healing particles for (var particleIndex = 0; particleIndex < 2; particleIndex++) { var particle = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: ally.x + (Math.random() - 0.5) * 100, y: ally.y + (Math.random() - 0.5) * 100, width: 140 + Math.random() * 100, height: 140 + Math.random() * 100 })); particle.alpha = 0.8; // Animate particles upward and fade out tween(particle, { y: ally.y - 150 - Math.random() * 50, x: ally.x + (Math.random() - 0.5) * 200, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 800 + Math.random() * 400, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } } } } // Check for enemies to attack and combat engagement var inCombat = false; var attackRange = 250; // Default melee range if (self.isRanged) { attackRange = 600; // Default ranged if (self.element === 'wind') { if (self.tier === 1) { attackRange = 600; } else if (self.tier === 2) { attackRange = 700; } else if (self.tier === 3) { attackRange = 800; } else if (self.tier === 4) { attackRange = 900; } else if (self.tier === 5) { attackRange = 600; } } } // Check for aerial unit encounters (wind warriors only) - stopping behavior if (self.element === 'wind' && self.isFlying) { var hasAerialCollision = false; // Check collision with aerial enemies ONLY for (var ae = 0; ae < enemies.length; ae++) { var otherEnemy = enemies[ae]; // Check if other enemy is aerial and not the same warrior if (otherEnemy.clase_enemigo === 'aero') { var aerialDistance = Math.sqrt(Math.pow(self.x - otherEnemy.x, 2) + Math.pow(self.y - otherEnemy.y, 2)); if (aerialDistance < 600) { // Detection range for wind warrior vs aerial enemy hasAerialCollision = true; // Visual feedback - brief flash to indicate encounter if (LK.ticks % 60 === 0) { // Flash every second tween(self.children[0], { tint: 0x00ffff // Cyan flash for aerial encounter }, { duration: 200, onFinish: function onFinish() { tween(self.children[0], { tint: 0xffffff }, { duration: 200 }); } }); } break; // Stop checking once we find one aerial enemy nearby } } } // Update movement based on aerial collision state if (hasAerialCollision) { self.activar_movimiento = false; } else { // Resume movement when no aerial collisions detected self.activar_movimiento = true; } } // For wind warriors (aerial units), find the closest enemy if (self.element === 'wind' && self.isFlying) { var closestEnemy = null; var closestDistance = Infinity; // Start with infinite distance // For tier 5, find closest enemy regardless of range if (self.tier === 5) { // Find the closest enemy on the entire map for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < closestDistance) { closestEnemy = enemy; closestDistance = distance; } } } else { // For other tiers, find closest enemy within range for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < attackRange && distance < closestDistance) { closestEnemy = enemy; closestDistance = distance; } } } // Attack the closest enemy if found if (closestEnemy && self.attackCooldown <= 0) { inCombat = true; self.attack(closestEnemy); // Attack animation - flash blue and scale up while preserving tier scale var attackScale = self.tierScale * 1.4; tween(warriorGraphics, { tint: 0x00ffff, scaleX: attackScale, scaleY: attackScale }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: self.tierScale, scaleY: self.tierScale }, { duration: 300 }); } }); } } else { // Original logic for all other units for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Use half width for warrior and enemy colliders but keep full height var warriorColliderWidth = 288 * 0.5; // Half of warrior width var enemyColliderWidth = 280 * 0.5; // Half of enemy width var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < attackRange) { // Mark as in combat to stop movement for all ranged units including water warriors inCombat = true; // Attack logic based on warrior type if (self.attackCooldown <= 0) { if (self.element === 'water') { // Water warriors only do ranged attacks if (self.isRanged) { self.attack(enemy); // Attack animation - flash blue and scale up while preserving tier scale var attackScale = self.tierScale * 1.4; tween(warriorGraphics, { tint: 0x00ffff, scaleX: attackScale, scaleY: attackScale }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: self.tierScale, scaleY: self.tierScale }, { duration: 300 }); } }); } } else if (self.isRanged && !(self.element === 'light' && self.tier === 4)) { // Other ranged warriors (light except tier 4, wind) self.attack(enemy); // Attack animation - flash blue and scale up while preserving tier scale var attackScale = self.tierScale * 1.4; tween(warriorGraphics, { tint: 0x00ffff, scaleX: attackScale, scaleY: attackScale }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: self.tierScale, scaleY: self.tierScale }, { duration: 300 }); } }); } else { // Melee warriors (fire, earth) - attack directly self.attack(enemy); // Attack animation - flash red for fire warriors and scale up while preserving tier scale var attackTint = self.element === 'fire' ? 0xff4400 : 0x8b4513; // Fire red or earth brown var attackScale = self.tierScale * 1.4; tween(warriorGraphics, { tint: attackTint, scaleX: attackScale, scaleY: attackScale }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: self.tierScale, scaleY: self.tierScale }, { duration: 300 }); } }); } } // Break out of loop for all ranged units except wind warriors to stop movement if (self.isRanged && self.element !== 'wind') { break; } else if (!self.isRanged) { break; } } } } // Check if warrior is at the tower x position - stop movement to focus on tower attack var atTowerPosition = Math.abs(self.x - playerTower.x) <= 50; // Update projectiles for ranged units if (self.isRanged) { for (var p = self.projectiles.length - 1; p >= 0; p--) { var proj = self.projectiles[p]; proj.x += proj.speedX; proj.y += proj.speedY; var projectileDestroyed = false; // Water warrior projectiles can heal allies if (self.element === 'water') { // Check collision with allies first for (var w = 0; w < warriors.length; w++) { var ally = warriors[w]; if (ally !== self) { // Use half width for ally collider but keep full height var allyColliderWidth = 288 * 0.5; // Half of ally width var allyDistance = Math.sqrt(Math.pow(proj.x - ally.x, 2) + Math.pow(proj.y - ally.y, 2)); if (allyDistance < 50) { // Hit ally if (ally.health < ally.maxHealth) { // Heal ally if not at full health (heal amount equals water warrior's damage plus aura bonus) var finalHealAmount = self.damage + (self.auraDamageBonus || 0); ally.health = Math.min(ally.health + finalHealAmount, ally.maxHealth); // Healing effect tween(ally.children[0], { tint: 0x00ff88 }, { duration: 200, onFinish: function onFinish() { tween(ally.children[0], { tint: 0xffffff }, { duration: 200 }); } }); // Create healing particles for (var particleIndex = 0; particleIndex < 2; particleIndex++) { var particle = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: ally.x + (Math.random() - 0.5) * 100, y: ally.y + (Math.random() - 0.5) * 100, width: 140 + Math.random() * 100, height: 140 + Math.random() * 100 })); particle.alpha = 0.8; // Animate particles upward and fade out tween(particle, { y: ally.y - 150 - Math.random() * 50, x: ally.x + (Math.random() - 0.5) * 200, alpha: 0, scaleX: 0.3, scaleY: 0.3 }, { duration: 800 + Math.random() * 400, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } proj.destroy(); self.projectiles.splice(p, 1); projectileDestroyed = true; break; } // If ally is at full health, projectile passes through } } } } // Check collision with enemies only if projectile wasn't destroyed by healing if (!projectileDestroyed) { for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Use half width for enemy collider but keep full height var enemyColliderWidth = 280 * 0.5; // Half of enemy width var projDistance = Math.sqrt(Math.pow(proj.x - enemy.x, 2) + Math.pow(proj.y - enemy.y, 2)); if (projDistance < 80) { // Increased collision radius for better detection // Hit enemy if (self.canAreaDamage) { // Create lightning explosion visual effect at projectile impact point var explosion = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: proj.x, y: proj.y, width: 240, height: 240 })); explosion.alpha = 0.9; // Animate explosion - scale up and fade out tween(explosion, { scaleX: 3.0, scaleY: 3.0, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Light warrior tier 2 - create second explosion at 300 units away if (self.element === 'light' && self.tier === 2 && self.canSecondExplosion) { // Calculate direction vector from projectile to enemy for horizontal distance var dirX = enemy.x - proj.x; var distance = Math.sqrt(dirX * dirX + Math.pow(enemy.y - proj.y, 2)); var normalizedX = dirX / distance; // Create second explosion 300 units away horizontally, same Y as first explosion var secondExplosionX = proj.x + normalizedX * 300; var secondExplosionY = proj.y; // Same Y position as first explosion var secondExplosion = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: secondExplosionX, y: secondExplosionY, width: 240, height: 240 })); secondExplosion.alpha = 0.9; secondExplosion.tint = 0xffff88; // Slightly yellow tint for second explosion // Animate second explosion tween(secondExplosion, { scaleX: 3.0, scaleY: 3.0, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { secondExplosion.destroy(); } }); // Apply area damage around second explosion for (var ae2 = 0; ae2 < enemies.length; ae2++) { var areaEnemy2 = enemies[ae2]; var areaDistance2 = Math.sqrt(Math.pow(secondExplosionX - areaEnemy2.x, 2) + Math.pow(secondExplosionY - areaEnemy2.y, 2)); if (areaDistance2 < 300) { areaEnemy2.takeDamage(self.damage); } } } // Create electric particles expanding from explosion center for (var particleIndex = 0; particleIndex < 12; particleIndex++) { var angle = particleIndex / 12 * Math.PI * 2; // Distribute evenly in circle var distance = 150 + Math.random() * 100; var particleEndX = proj.x + Math.cos(angle) * distance; var particleEndY = proj.y + Math.sin(angle) * distance; var electricParticle = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: proj.x, y: proj.y, width: 20 + Math.random() * 20, height: 20 + Math.random() * 20 })); electricParticle.alpha = 0.8; // Animate particles expanding outward and fading tween(electricParticle, { x: particleEndX, y: particleEndY, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 300 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { electricParticle.destroy(); } }); } // Lightning area damage - increased damage for area effect for (var ae = 0; ae < enemies.length; ae++) { var areaEnemy = enemies[ae]; var areaDistance = Math.sqrt(Math.pow(proj.x - areaEnemy.x, 2) + Math.pow(proj.y - areaEnemy.y, 2)); if (areaDistance < 600) { areaEnemy.takeDamage(1); // Set damage to 1 for area effect } } } else { // Handle wind warrior special effects if (self.element === 'wind') { // Apply base damage first var finalDamage = self.damage + (self.auraDamageBonus || 0); // Tier 5: Check if enemy has already been attacked by this warrior if (self.tier === 5) { // Check if this enemy has already been attacked by this specific warrior if (self.attackedEnemies && self.attackedEnemies.indexOf(enemy) !== -1) { // Enemy already attacked, skip damage and effects proj.destroy(); self.projectiles.splice(p, 1); projectileDestroyed = true; break; } // Mark this enemy as attacked by this warrior before dealing damage if (self.attackedEnemies) { self.attackedEnemies.push(enemy); } } enemy.takeDamage(finalDamage); // Tier 3: Apply esmeralda with 80% probability if (self.tier === 3) { // Apply emerald statue effect with 80% probability if (Math.random() < 0.8) { enemy.enemigo_oro = true; } } // Tier 2: Golden statue effect if (proj.goldenStatueChance && Math.random() < proj.goldenStatueChance) { enemy.isGoldenStatue = true; enemy.enemigo_oro = true; // Set golden statue variable enemy.activar_movimiento = false; enemy.canAttack = false; // Remove golden statue effect after some time (permanent for now) } // Tier 4: Apply sleep effect first, then emerald statue effect, then convert enemy with 33% chance else if (self.tier === 4) { // Apply sleep effect first enemy.dormido = true; // Then apply emerald statue effect enemy.enemigo_oro = true; // Call conversion function with 33% probability if (Math.random() < 0.33) { convertir_enemigos_esmeralda(enemy); } } // Tier 5: Apply esmeralda and vortex effects else if (self.tier === 5) { // Apply emerald statue effect enemy.enemigo_oro = true; // Apply vortex effect enemy.vortice = true; // Create tornado image at enemy center var tornado = game.addChild(LK.getAsset('tornado', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, width: 500, height: 500 })); tornado.alpha = 0.5; // Store tornado reference in enemy enemy.tornado = tornado; // Start 10-second vortex damage timer var vortexDamageTimer = 0; var vortexDamageInterval = LK.setInterval(function () { if (enemy && enemy.vortice && enemy.health > 0) { // Deal 10% of max health as damage var vortexDamage = Math.ceil(enemy.maxHealth * 0.1); enemy.takeDamage(vortexDamage); // Update tornado position to follow enemy if (tornado) { tornado.x = enemy.x; tornado.y = enemy.y; } vortexDamageTimer += 1000; // Increment by 1 second // Check if 10 seconds have passed if (vortexDamageTimer >= 10000) { // Remove vortex effect after 10 seconds enemy.vortice = false; if (tornado) { tornado.destroy(); } enemy.tornado = null; LK.clearInterval(vortexDamageInterval); } } else { // Clean up if enemy is destroyed if (tornado) { tornado.destroy(); } enemy.tornado = null; LK.clearInterval(vortexDamageInterval); } }, 1000); // Every 1 second } } else if (self.element === 'water') { enemy.takeDamage(self.damage); } else { // Apply aura damage bonus if affected by water warrior aura var finalDamage = self.damage + (self.auraDamageBonus || 0); enemy.takeDamage(finalDamage); } } proj.destroy(); self.projectiles.splice(p, 1); projectileDestroyed = true; break; } } } // Remove projectiles that go off screen only if not already destroyed if (!projectileDestroyed && (proj.x > 4500 || proj.x < -500 || proj.y > 3500 || proj.y < -500)) { proj.destroy(); self.projectiles.splice(p, 1); } } } // Set activar_movimiento based on combat state and element type if (inCombat && self.element !== 'wind') { // Stop movement for all elements except wind when in combat range self.activar_movimiento = false; } else { // Allow movement when not in combat, and always allow movement for wind warriors self.activar_movimiento = true; } // Special case: Wind warriors maintain movement during combat, but respect aerial collisions if (self.element === 'wind') { // Only force movement if there's no aerial collision if (!hasAerialCollision) { self.activar_movimiento = true; } else { // Ensure movement is disabled when there's an aerial collision self.activar_movimiento = false; } } // Apply movement based on activar_movimiento flag // Wind warriors ignore tower position blocking and maintain movement if (!self.activar_movimiento || atTowerPosition && self.element !== 'wind') { self.speed = 0; } else { // Resume normal movement - use individual warrior's original speed instead of overriding with base speed // Only restore to base speed if warrior doesn't have an individual speed set if (!self.originalSpeed) { // Store the original speed set during warrior creation if (self.element === 'fire') { self.originalSpeed = 2.2; } else if (self.element === 'water') { self.originalSpeed = 1.8; } else if (self.element === 'earth') { self.originalSpeed = 1.5; } else if (self.element === 'light') { self.originalSpeed = 2.5; } else if (self.element === 'wind') { self.originalSpeed = self.speed; // Use the speed already set during creation } else { self.originalSpeed = 2; // Default } } self.speed = self.originalSpeed; } // Earth warrior wall creation (tier 3+) if (self.element === 'earth' && self.tier >= 3 && self.canCreateWalls && self.distancia_recorrida !== undefined) { // Check if we've traveled 1100 units to create a wall if (self.distancia_recorrida >= 1100) { // Determine wall asset and size based on tier var wallAsset, wallWidth, wallHeight; if (self.tier === 3) { wallAsset = 'muro_tierra'; wallWidth = 360; // Normal size wallHeight = 393; } else if (self.tier === 4) { wallAsset = 'muro_tierra_nivel_2'; wallWidth = 540; // 50% bigger wallHeight = 590; } else if (self.tier === 5) { wallAsset = 'muro_tierra_nivel_3'; wallWidth = 720; // 100% bigger wallHeight = 786; } // Create wall positioned like a warrior unit var wall = game.addChild(LK.getAsset(wallAsset, { anchorX: 0.5, anchorY: 0.5, x: self.x, y: 2186, // Ground level - centered like warriors width: wallWidth, height: wallHeight })); wall.alpha = 0.75; // 75% opacity wall.isWall = true; wall.health = self.wallHealth; wall.maxHealth = self.wallHealth; wall.activar_movimiento = false; // Cannot move like warriors wall.canAttack = false; // Cannot attack like warriors // Create green health bar for wall (50% larger and visible) var wallHealthBar = LK.getAsset('warriorHealthBar', { width: 180, height: 24 }); wallHealthBar.anchor.set(0.5, 0.5); wallHealthBar.x = 0; // Centered above wall wallHealthBar.y = -200; // Move much higher above wall wallHealthBar.alpha = 1.0; // Ensure full visibility // Add health bar directly to game instead of wall to ensure it's on top game.addChild(wallHealthBar); wall.healthBar = wallHealthBar; // Update health bar position when wall position changes wall.updateHealthBarPosition = function () { if (this.healthBar) { this.healthBar.x = this.x; this.healthBar.y = this.y - 200; } }; wall.updateHealthBarPosition(); wall.takeDamage = function (damage) { this.health -= damage; var healthPercent = this.health / this.maxHealth; if (this.healthBar) { this.healthBar.scaleX = healthPercent; } // Health bar color is already green from warriorHealthBar asset - no tinting needed // Flash wall red when taking damage tween(this.children[0], { tint: 0xff0000 }, { duration: 200, onFinish: function () { tween(this.children[0], { tint: 0xffffff }, { duration: 200 }); }.bind(this) }); // Create debris particles when wall takes damage var particleCount = damage * 4; // Scale particles with damage amount (reduced by half) for (var debrisIndex = 0; debrisIndex < particleCount; debrisIndex++) { // 180-degree spread pattern (forward facing) var angle = (debrisIndex / particleCount - 0.5) * Math.PI; // Spread from -90 to +90 degrees var force = (150 + Math.random() * 200) * 3; // Random force var velocityX = Math.cos(angle) * force; var velocityY = Math.sin(angle) * force; var debrisParticle = game.addChild(LK.getAsset('escombros_torre_enemiga', { anchorX: 0.5, anchorY: 0.5, x: this.x, y: this.y - 100, width: (40 + Math.random() * 30) * 3, height: (40 + Math.random() * 30) * 3, rotation: Math.random() * Math.PI * 2 })); debrisParticle.alpha = 0.9; debrisParticle.tint = 0x8B4513; // Brown color for earth debris // Add physics properties debrisParticle.velocityX = velocityX * 0.015; // Scale down for smooth movement debrisParticle.velocityY = velocityY * 0.015; debrisParticle.gravity = 0.6; // Gravity effect // Animate debris particles with physics tween(debrisParticle, { y: this.y + 150 + debrisParticle.velocityY * 80, x: this.x + debrisParticle.velocityX * 80, alpha: 0, rotation: debrisParticle.rotation + Math.PI * 3, scaleX: 0.1, scaleY: 0.1 }, { duration: 1500 + Math.random() * 800, easing: tween.easeOut, onFinish: function onFinish() { debrisParticle.destroy(); } }); } if (this.health <= 0) { // Clean up health bar before destroying wall if (this.healthBar) { this.healthBar.destroy(); this.healthBar = null; } this.destroy(); } }; // Wall update function - acts like a stationary warrior wall.update = function () { // Update health bar position to stay above wall this.updateHealthBarPosition(); // Check collision with enemies using warrior-like collision detection for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Use similar collision detection as warriors but with wall dimensions var wallColliderWidth = 240 * 0.5; // Half of wall width like warriors var enemyColliderWidth = 280 * 0.5; // Half of enemy width var distance = Math.sqrt(Math.pow(this.x - enemy.x, 2) + Math.pow(this.y - enemy.y, 2)); if (distance < 200) { // Enemy in combat range with wall if (enemy.attackCooldown <= 0) { this.takeDamage(enemy.damage); enemy.attackCooldown = 80; } // Stop enemy movement when in combat with wall enemy.speed = 0; enemy.inCombat = true; } else if (distance > 350) { // Enemy far from wall, can resume movement if (!enemy.inCombat) { enemy.speed = 1.5; } } } }; // Reset distance counter to 0 after creating wall self.distancia_recorrida = 0; self.updateStatsDisplay(); // Update display to show reset counter } } // Light warrior tier 5 tracking clouds - create one cloud per enemy if (self.element === 'light' && self.tier === 5 && self.canTrackingClouds) { // Check every 2 seconds (120 frames) if (LK.ticks % 120 === 0) { // Create a cloud for each enemy on the map for (var enemyIndex = 0; enemyIndex < enemies.length; enemyIndex++) { var targetEnemy = enemies[enemyIndex]; // Check if this enemy already has a tracking cloud var hasCloud = false; for (var childIndex = 0; childIndex < game.children.length; childIndex++) { var child = game.children[childIndex]; if (child.isTrackingCloud && child.targetEnemy === targetEnemy) { hasCloud = true; break; } } // Only create cloud if enemy doesn't have one if (!hasCloud) { var trackingCloud = game.addChild(LK.getAsset('shape', { anchorX: 0.5, anchorY: 0.5, x: targetEnemy.x, y: targetEnemy.y - 300, width: 280, height: 180 })); trackingCloud.alpha = 0.8; trackingCloud.tint = 0x1a1aff; // 90% blue tint trackingCloud.targetEnemy = targetEnemy; trackingCloud.attackTimer = 0; trackingCloud.isTrackingCloud = true; trackingCloud.creatorDamage = 5; // Store tier 5 damage value at creation time trackingCloud.seguimiento_nube = false; // Tier 5 clouds don't retarget, destroy when enemy dies // Removed text display for cleaner visual appearance // Tracking cloud update function trackingCloud.update = function () { // Follow target enemy in X axis, stay 300 units above if (this.targetEnemy && !this.targetEnemy.isBeingDestroyed && this.targetEnemy.health > 0) { if (this.seguimiento_nube) { // Smooth movement for tracking clouds (tier 3) tween(this, { x: this.targetEnemy.x }, { duration: 200, easing: tween.easeOut }); } else { // Instant movement for non-tracking clouds (tier 5) this.x = this.targetEnemy.x; } } else { // Target is destroyed, remove cloud (tier 5 behavior) this.destroy(); return; } this.attackTimer++; // Attack every 1 second (60 frames) if (this.attackTimer >= 60) { this.attackTimer = 0; // Create lightning strike var lightning = game.addChild(LK.getAsset('rayo_electrico', { anchorX: 0.5, anchorY: 0.5, x: this.targetEnemy.x, y: this.targetEnemy.y, width: 300, height: 900 })); lightning.alpha = 0.9; // Animate lightning tween(lightning, { scaleX: 2.0, scaleY: 0.1, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { lightning.destroy(); } }); // Store target health before damage to detect kills var targetHealthBefore = this.targetEnemy.health; // Direct hit damage to target using stored creator damage var directDamage = this.creatorDamage || 5; // Use stored damage or fallback to 5 this.targetEnemy.takeDamage(directDamage); // Check if target died and this is the second cloud if (this.targetEnemy.health <= 0 && this.seguimiento_nube) { // Determine which cloud this is var cloudIndex = -1; var tier3Clouds = []; for (var childIndex = 0; childIndex < game.children.length; childIndex++) { var child = game.children[childIndex]; if (child.createdByWarrior === this.createdByWarrior && child.seguimiento_nube === true) { tier3Clouds.push(child); } } // Find this cloud's index for (var i = 0; i < tier3Clouds.length; i++) { if (tier3Clouds[i] === this) { cloudIndex = i; break; } } // If this is the second cloud and it killed its target, find new farthest enemy if (cloudIndex === 1) { var farthestDistance = 0; var farthestEnemy = null; for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; if (enemy !== this.targetEnemy && enemy.health > 0 && !enemy.isBeingDestroyed) { var distanceToTower = Math.sqrt(Math.pow(enemyTower.x - enemy.x, 2) + Math.pow(enemyTower.y - enemy.y, 2)); if (distanceToTower > farthestDistance) { farthestEnemy = enemy; farthestDistance = distanceToTower; } } } // Retarget to farthest enemy if found if (farthestEnemy) { this.targetEnemy = farthestEnemy; } } } // Explosion damage to nearby enemies (600 unit radius) var explosionX = this.targetEnemy.x; var explosionY = this.targetEnemy.y; for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; if (enemy !== this.targetEnemy) { var distance = Math.sqrt(Math.pow(explosionX - enemy.x, 2) + Math.pow(explosionY - enemy.y, 2)); if (distance < 600) { // Use stored creator damage from cloud creation time var areaDamage = this.creatorDamage || 5; // Use stored damage or fallback to 5 enemy.takeDamage(areaDamage); // 5 damage for area effect } } } // Create invisible explosion collider for area damage detection var explosionCollider = { x: explosionX, y: explosionY, radius: 600 }; // Create invisible explosion for additional area damage detection var invisibleExplosion = { x: explosionX, y: explosionY, radius: 600, damage: this.creatorDamage || 5 }; // Apply area damage to all enemies within 600-unit radius using invisible explosion for (var explosionIndex2 = 0; explosionIndex2 < enemies.length; explosionIndex2++) { var explosionEnemy2 = enemies[explosionIndex2]; var explosionDistance2 = Math.sqrt(Math.pow(invisibleExplosion.x - explosionEnemy2.x, 2) + Math.pow(invisibleExplosion.y - explosionEnemy2.y, 2)); if (explosionDistance2 < invisibleExplosion.radius) { // Apply damage from invisible explosion explosionEnemy2.takeDamage(invisibleExplosion.damage); // 5 damage for explosion area effect } } // Create explosion visual effect var explosion = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: explosionX, y: explosionY, width: 200, height: 200 })); explosion.alpha = 0.8; tween(explosion, { scaleX: 3.0, scaleY: 3.0, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); } }; } } } } // Light warrior tier 4 laser beam attack if (self.element === 'light' && self.tier === 4 && self.canLaserBeam) { // Fire laser beam every 10 seconds (600 frames) if (LK.ticks % 600 === 0) { // Create laser beam from warrior position to far right of map using rayo_amarillo image asset var laserBeam = game.addChild(LK.getAsset('rayo_amarillo', { anchorX: 0.0, anchorY: 0.5, x: self.x, y: self.y, width: 4000, height: 50 })); laserBeam.alpha = 0.9; // Animate laser beam appearance and fade tween(laserBeam, { scaleY: 3.0, alpha: 1.0 }, { duration: 200, onFinish: function onFinish() { tween(laserBeam, { alpha: 0, scaleY: 0.1 }, { duration: 800, onFinish: function onFinish() { laserBeam.destroy(); } }); } }); // Deal damage to all enemies in laser path for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Check if enemy is in laser path (Y coordinate within beam height) var yDistance = Math.abs(enemy.y - self.y); var xDistance = enemy.x - self.x; if (yDistance < 100 && xDistance > 0 && xDistance < 4000) { var finalDamage = self.damage + (self.auraDamageBonus || 0); enemy.takeDamage(finalDamage); } } } } // Light warrior tier 3 cloud generation - only for existing warriors if (self.element === 'light' && self.tier === 3 && self.canCreateClouds && !self.cloudsCreated) { // Create 2 clouds only once when warrior is spawned self.cloudsCreated = true; for (var cloudIndex = 0; cloudIndex < 2; cloudIndex++) { // Create cloud positioned at enemy base with different offsets var cloudOffset = cloudIndex === 0 ? 300 : 500; var cloudXOffset = cloudIndex === 0 ? -300 : -600; var lightCloud = game.addChild(LK.getAsset('shape', { anchorX: 0.5, anchorY: 0.5, x: enemyTower.x + cloudXOffset, y: self.y - cloudOffset, width: 300, height: 200 })); lightCloud.alpha = 0.7; lightCloud.tint = 0x1a1aff; // 90% blue tint lightCloud.targetEnemy = null; // Will be assigned dynamically lightCloud.attackTimer = 0; lightCloud.createdByWarrior = self; // Reference to the warrior that created this cloud lightCloud.creatorDamage = self.damage; // Store creator's damage at creation time lightCloud.seguimiento_nube = true; // Tier 3 clouds have tracking behavior // Removed text display for cleaner visual appearance // Cloud update function lightCloud.update = function () { // Check if creator warrior still exists if (!this.createdByWarrior || this.createdByWarrior.isBeingDestroyed || this.createdByWarrior.health <= 0) { // Creator destroyed, remove cloud this.destroy(); return; } // Find target enemy for tracking behavior if (this.seguimiento_nube) { var targetEnemy = null; // Determine which cloud this is based on creation order var cloudIndex = -1; var tier3Clouds = []; for (var childIndex = 0; childIndex < game.children.length; childIndex++) { var child = game.children[childIndex]; if (child.createdByWarrior === this.createdByWarrior && child.seguimiento_nube === true) { tier3Clouds.push(child); } } // Find this cloud's index for (var i = 0; i < tier3Clouds.length; i++) { if (tier3Clouds[i] === this) { cloudIndex = i; break; } } if (cloudIndex === 0) { // First cloud: target closest enemy to tower var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distanceToTower = Math.sqrt(Math.pow(enemyTower.x - enemy.x, 2) + Math.pow(enemyTower.y - enemy.y, 2)); if (distanceToTower < closestDistance) { targetEnemy = enemy; closestDistance = distanceToTower; } } } else if (cloudIndex === 1) { // Second cloud: target farthest enemy if no current target or current target is dead if (!this.targetEnemy || this.targetEnemy.health <= 0 || this.targetEnemy.isBeingDestroyed) { var farthestDistance = 0; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distanceToTower = Math.sqrt(Math.pow(enemyTower.x - enemy.x, 2) + Math.pow(enemyTower.y - enemy.y, 2)); if (distanceToTower > farthestDistance) { targetEnemy = enemy; farthestDistance = distanceToTower; } } } else { // Keep current target if still alive targetEnemy = this.targetEnemy; } } // Update target and follow if (targetEnemy) { this.targetEnemy = targetEnemy; if (this.seguimiento_nube) { // Smooth movement for tracking clouds tween(this, { x: this.targetEnemy.x }, { duration: 200, easing: tween.easeOut }); } else { // Instant movement for non-tracking clouds this.x = this.targetEnemy.x; } } } this.attackTimer++; // Attack every 2 seconds (120 frames) if (this.attackTimer >= 120 && this.targetEnemy) { this.attackTimer = 0; // Create lightning bolt projectile var lightning = game.addChild(LK.getAsset('rayo_electrico', { anchorX: 0.5, anchorY: 0.5, x: this.targetEnemy.x, y: this.targetEnemy.y, width: 300, height: 900 })); lightning.alpha = 0.9; // Animate lightning strike tween(lightning, { scaleY: 0.1, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { lightning.destroy(); } }); // Area damage at impact point for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var distance = Math.sqrt(Math.pow(this.x - enemy.x, 2) + Math.pow(this.y + 300 - enemy.y, 2)); if (distance < 300) { // Use stored creator damage from cloud creation time var areaDamage = this.creatorDamage || 3; // Use stored damage or fallback to 3 enemy.takeDamage(areaDamage); // Debug logging to verify area damage application console.log("Tier 3 cloud area damage applied:", areaDamage, "to enemy at distance:", Math.round(distance)); } } // Create invisible explosion collider for area damage detection var explosionCollider = { x: this.x, y: this.y + 300, radius: 300 }; // Create invisible explosion for additional area damage detection var invisibleExplosion = { x: this.x, y: this.y + 300, radius: 300, damage: this.creatorDamage || 3 }; // Apply area damage to all enemies within 300-unit radius using invisible explosion for (var explosionIndex = 0; explosionIndex < enemies.length; explosionIndex++) { var explosionEnemy = enemies[explosionIndex]; var explosionDistance = Math.sqrt(Math.pow(invisibleExplosion.x - explosionEnemy.x, 2) + Math.pow(invisibleExplosion.y - explosionEnemy.y, 2)); if (explosionDistance < invisibleExplosion.radius) { // Apply damage from invisible explosion explosionEnemy.takeDamage(invisibleExplosion.damage); // 3 damage for explosion area effect } } // Create explosion visual effect - 200x200 size var explosion = game.addChild(LK.getAsset('efecto_electricidad', { anchorX: 0.5, anchorY: 0.5, x: this.x, y: this.y + 300, width: 200, height: 200 })); explosion.alpha = 0.8; tween(explosion, { scaleX: 3.0, scaleY: 3.0, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); } }; } } // Wind warrior tier 3 spore generation if (self.element === 'wind' && self.tier === 3) { // Generate spores every 60 frames (1 second) if (LK.ticks % 15 === 0) { // Create 2 spores (doubled from 1) var numSpores = 2; for (var sporeIndex = 0; sporeIndex < numSpores; sporeIndex++) { var espora = new Espora(self); game.addChild(espora); esporas.push(espora); } } } // Wind warrior tier 5 enemy dragging and tower destruction if (self.element === 'earth' && self.tier === 5) { // Drag enemies backward every second if (LK.ticks % 60 === 0) { // Every second for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var distance = Math.sqrt(Math.pow(self.x - enemy.x, 2) + Math.pow(self.y - enemy.y, 2)); if (distance < 600) { // Push enemy forward 300 units smoothly (away from our towers) var targetX = enemy.x + 300; tween(enemy, { x: targetX }, { duration: 1000, // 1 second easing: tween.easeOut }); // Deal damage each second of dragging enemy.takeDamage(self.damage); // Visual effect for dragging tween(enemy.children[0], { tint: 0x8b4513 }, { duration: 200, onFinish: function onFinish() { tween(enemy.children[0], { tint: 0xffffff }, { duration: 200 }); } }); } } } // Check if close to enemy tower for self-destruction var towerDistance = Math.sqrt(Math.pow(self.x - enemyTower.x, 2) + Math.pow(self.y - enemyTower.y, 2)); if (towerDistance < 800 && self.canSelfDestruct) { // Self-destruct and become wall with 250 health // Create mega wall positioned like a warrior unit var megaWall = game.addChild(LK.getAsset('muro_tierra_nivel_3', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: 2186, width: 720, // 100% larger wall (level 3) height: 786 })); megaWall.alpha = 0.75; // 75% opacity megaWall.isWall = true; megaWall.health = 2000; megaWall.maxHealth = 2000; megaWall.activar_movimiento = false; // Cannot move like warriors megaWall.canAttack = false; // Cannot attack like warriors // Create green health bar for mega wall (50% larger and visible) var megaWallHealthBar = LK.getAsset('warriorHealthBar', { width: 216, height: 32 }); megaWallHealthBar.anchor.set(0.5, 0.5); megaWallHealthBar.x = 0; // Centered above mega wall megaWallHealthBar.y = -280; // Move much higher above mega wall megaWallHealthBar.alpha = 1.0; // Ensure full visibility // Add health bar directly to game instead of mega wall to ensure it's on top game.addChild(megaWallHealthBar); megaWall.healthBar = megaWallHealthBar; // Update health bar position when mega wall position changes megaWall.updateHealthBarPosition = function () { if (this.healthBar) { this.healthBar.x = this.x; this.healthBar.y = this.y - 280; } }; megaWall.updateHealthBarPosition(); megaWall.takeDamage = function (damage) { this.health -= damage; var healthPercent = this.health / this.maxHealth; if (this.healthBar) { this.healthBar.scaleX = healthPercent; } // Health bar color is already green from warriorHealthBar asset - no tinting needed // Flash mega wall red when taking damage tween(this.children[0], { tint: 0xff0000 }, { duration: 200, onFinish: function () { tween(this.children[0], { tint: 0xffffff }, { duration: 200 }); }.bind(this) }); // Create debris particles when mega wall takes damage var particleCount = damage * 6; // More particles for mega wall (reduced by half) for (var debrisIndex = 0; debrisIndex < particleCount; debrisIndex++) { // 180-degree spread pattern (forward facing) var angle = (debrisIndex / particleCount - 0.5) * Math.PI; // Spread from -90 to +90 degrees var force = (200 + Math.random() * 250) * 4; // Stronger force for mega wall var velocityX = Math.cos(angle) * force; var velocityY = Math.sin(angle) * force; var debrisParticle = game.addChild(LK.getAsset('escombros_torre_enemiga', { anchorX: 0.5, anchorY: 0.5, x: this.x, y: this.y - 150, width: (50 + Math.random() * 40) * 4, height: (50 + Math.random() * 40) * 4, rotation: Math.random() * Math.PI * 2 })); debrisParticle.alpha = 0.9; debrisParticle.tint = 0x8B4513; // Brown color for earth debris // Add physics properties debrisParticle.velocityX = velocityX * 0.02; // Scale down for smooth movement debrisParticle.velocityY = velocityY * 0.02; debrisParticle.gravity = 0.8; // Gravity effect // Animate debris particles with physics tween(debrisParticle, { y: this.y + 200 + debrisParticle.velocityY * 100, x: this.x + debrisParticle.velocityX * 100, alpha: 0, rotation: debrisParticle.rotation + Math.PI * 4, scaleX: 0.1, scaleY: 0.1 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeOut, onFinish: function onFinish() { debrisParticle.destroy(); } }); } if (this.health <= 0) { // Clean up health bar before destroying mega wall if (this.healthBar) { this.healthBar.destroy(); this.healthBar = null; } this.destroy(); } }; // Mega wall update function - acts like a stationary warrior megaWall.update = function () { // Update health bar position to stay above mega wall this.updateHealthBarPosition(); // Check collision with enemies using warrior-like collision detection for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Use similar collision detection as warriors but with mega wall dimensions var megaWallColliderWidth = 360 * 0.5; // Half of mega wall width like warriors var enemyColliderWidth = 280 * 0.5; // Half of enemy width var distance = Math.sqrt(Math.pow(this.x - enemy.x, 2) + Math.pow(this.y - enemy.y, 2)); if (distance < 250) { // Enemy in combat range with mega wall if (enemy.attackCooldown <= 0) { this.takeDamage(enemy.damage); enemy.attackCooldown = 80; } // Stop enemy movement when in combat with mega wall enemy.speed = 0; enemy.inCombat = true; } else if (distance > 450) { // Enemy far from mega wall, can resume movement if (!enemy.inCombat) { enemy.speed = 1.5; } } } }; // Remove warrior from game self.isBeingDestroyed = true; var index = warriors.indexOf(self); if (index > -1) { warriors.splice(index, 1); } self.destroy(); return; // Exit update after self-destruction } } // Check collision with enemy tower - use separate x and y collision detection for flying units var towerColliderRadius = 700; // Fixed collider size, not scaled with tower var warriorColliderWidth = 288 * 0.5; // Half of warrior width var warriorColliderHeight = 288; // Full warrior height // Increase collider height for tier 4 light warriors if (self.element === 'light' && self.tier === 4) { warriorColliderHeight = 288 + 500; // Add 500 pixels to height } var towerDistance = Math.sqrt(Math.pow(self.x - enemyTower.x, 2) + Math.pow(self.y - enemyTower.y, 2)); var xDistance = Math.abs(self.x - enemyTower.x); var yDistance = Math.abs(self.y - enemyTower.y); // Use different collision detection for flying units vs ground units var collisionDetected = false; if (self.isFlying) { // For flying units, use x-distance only (ignore y difference) collisionDetected = xDistance < towerColliderRadius; } else { // For ground units, use traditional distance-based collision collisionDetected = towerDistance < towerColliderRadius; } if (collisionDetected && !self.isBeingDestroyed) { // Deal damage to tower first - always attack regardless of cooldown when touching tower self.attackTower(); // Mark as being destroyed to prevent multiple collision detections self.isBeingDestroyed = true; // Remove from warriors array immediately var index = warriors.indexOf(self); if (index > -1) { warriors.splice(index, 1); } // Warrior self-destructs when touching the tower - immediate destruction self.destroy(); return; // Exit update to prevent further execution after destruction } // Check collision with enemy tower for ranged units if (self.isRanged) { for (var p = self.projectiles.length - 1; p >= 0; p--) { var proj = self.projectiles[p]; var projTowerDistance = Math.sqrt(Math.pow(proj.x - enemyTower.x, 2) + Math.pow(proj.y - enemyTower.y, 2)); } } // Check if warrior is off-screen and destroy if (self.x < -500 || self.x > 4500 || self.y > 3500) { self.destroy(); var index = warriors.indexOf(self); if (index > -1) { warriors.splice(index, 1); } return; // Exit update to prevent further execution } // Update health bar var healthPercent = self.health / self.maxHealth; self.healthBar.scaleX = healthPercent; self.healthBar.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; } }; self.attack = function (target) { self.attackCooldown = self.baseCooldown || 60; // Use tier-based cooldown if (target === enemyTower) { self.attackTower(); } else if (self.isRanged) { // Light tier 4 warriors use laser beam instead of projectiles if (self.element === 'light' && self.tier === 4) { // Laser beam attack is handled in update method, not here return; // Exit without creating projectiles } else if (self.element === 'water' && self.tier === 4) { // Create mini-wave that travels 800 units with larger size (same as tier 5) var miniWave = game.addChild(LK.getAsset('ola', { anchorX: 0.0, anchorY: 0.5, x: self.x - 400, y: self.y, width: 800, height: 500 })); miniWave.tint = 0x00ffff; miniWave.alpha = 0.8; // Store wave damage for collision detection var finalWaveDamage = self.damage + (self.auraDamageBonus || 0); miniWave.finalWaveDamage = finalWaveDamage; // Track which enemies and allies have been hit by this wave miniWave.hitEnemies = []; miniWave.healedAllies = []; // Add wave update method for collision detection miniWave.update = function () { // Check collision with enemies - only once per enemy per wave for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; // Check if enemy hasn't been hit by this specific wave yet if (miniWave.hitEnemies.indexOf(enemy) === -1) { // Check collision between wave and enemy - use thin collider positioned at center of image var waveCenterX = miniWave.x + miniWave.width * 0.5; // Center of wave image var waveCenterY = miniWave.y; // Center Y position var waveDistance = Math.sqrt(Math.pow(waveCenterX - enemy.x, 2) + Math.pow(waveCenterY - enemy.y, 2)); if (waveDistance < 50) { // Thin collision radius of 50 units for precise detection // Enemy is hit by wave - mark immediately to prevent multiple hits miniWave.hitEnemies.push(enemy); // Always deal damage when hit by mini-wave - use base damage only (6) enemy.takeDamage(6); // Fixed damage of 6 for mini-waves // Set miniola_colision to true and update enemy stats enemy.miniola_colision = true; enemy.updateStatsDisplay(); // Set immunity timer for 1 second using setTimeout LK.setTimeout(function () { enemy.miniola_colision = false; enemy.updateStatsDisplay(); }, 1000); // Create splash effect at impact using salpicadura asset var splash = game.addChild(LK.getAsset('salpicadura', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, width: 300, height: 300 })); splash.alpha = 0.9; splash.tint = 0x00aaff; // Animate splash with scale and fade tween(splash, { scaleX: 2.5, scaleY: 2.5, alpha: 0, rotation: Math.PI * 0.5 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { splash.destroy(); } }); // Create additional water droplet particles around the splash for (var dropletIndex = 0; dropletIndex < 8; dropletIndex++) { var angle = dropletIndex / 8 * Math.PI * 2; var distance = 80 + Math.random() * 60; var dropletEndX = enemy.x + Math.cos(angle) * distance; var dropletEndY = enemy.y + Math.sin(angle) * distance; var droplet = game.addChild(LK.getAsset('salpicadura', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, width: 40 + Math.random() * 30, height: 40 + Math.random() * 30 })); droplet.alpha = 0.7; droplet.tint = 0x1e90ff; // Animate droplets spreading outward and fading tween(droplet, { x: dropletEndX, y: dropletEndY, alpha: 0, scaleX: 0.3, scaleY: 0.3, rotation: Math.random() * Math.PI * 2 }, { duration: 400 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { droplet.destroy(); } }); } } } } // Check collision with allies for healing for (var w = 0; w < warriors.length; w++) { var ally = warriors[w]; // Check if ally hasn't been healed by this wave yet if (miniWave.healedAllies.indexOf(ally) === -1) { // Check collision between wave and ally - use thin collider positioned at center of image var waveCenterX = miniWave.x + miniWave.width * 0.5; // Center of wave image var waveCenterY = miniWave.y; // Center Y position var allyDistance = Math.sqrt(Math.pow(waveCenterX - ally.x, 2) + Math.pow(waveCenterY - ally.y, 2)); if (allyDistance < 50) { // Thin collision radius of 50 units for precise detection // Ally is healed by wave - mark immediately to prevent multiple heals miniWave.healedAllies.push(ally); // Heal ally if not at full health if (ally.health < ally.maxHealth) { // Heal for exactly 10 (6 base + 4 bonus) ally.health = Math.min(ally.health + 10, ally.maxHealth); // Healing effect tween(ally.children[0], { tint: 0x00ff88 }, { duration: 200, onFinish: function onFinish() { tween(ally.children[0], { tint: 0xffffff }, { duration: 200 }); } }); // Create healing particles for (var particleIndex = 0; particleIndex < 1; particleIndex++) { var particle = game.addChild(LK.getAsset('particulas_curacion', { anchorX: 0.5, anchorY: 0.5, x: ally.x + (Math.random() - 0.5) * 80, y: ally.y + (Math.random() - 0.5) * 80, width: 80 + Math.random() * 40, height: 80 + Math.random() * 40 })); particle.alpha = 0.8; tween(particle, { y: ally.y - 100 - Math.random() * 50, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } } } } }; // Animate wave across 800 units distance tween(miniWave, { x: self.x + 800, scaleX: 1.5 }, { duration: 1500, easing: tween.linear, onFinish: function onFinish() { miniWave.destroy(); } }); self.projectiles.push(miniWave); // Play water projectile sound playRandomProjectileSound(self.element); return; // Exit after creating mini-wave } else { // Regular projectiles for other ranged units var projectileSize = 120; // Larger projectiles for better visibility (doubled from 60) var projectileAsset = 'projectile'; // Default projectile if (self.element === 'light' && self.tier === 3) { projectileAsset = 'proyectil_electricidad'; // Light tier 3 shoots electricity projectile } else if (self.element === 'light') { projectileAsset = 'proyectil_electricidad'; } else if (self.element === 'wind' && self.tier === 3) { projectileAsset = 'esporas'; // Use spore image for wind tier 3 } else if (self.element === 'wind' && self.tier === 4) { projectileAsset = 'lanza'; // Use lanza image for wind tier 4 } var projectile = game.addChild(LK.getAsset(projectileAsset, { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y - 20, // Start slightly above warrior width: projectileSize, height: projectileSize })); // Calculate direction to target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var speed = 10; // Increased speed for better gameplay projectile.speedX = dx / distance * speed; projectile.speedY = dy / distance * speed; // Set initial rotation for lanza projectiles to match aim direction if (self.element === 'wind' && self.tier === 4 && projectileAsset === 'lanza') { projectile.rotation = Math.atan2(dy, dx); // Make lanza thinner and double the size projectile.width = projectileSize * 2; // Double the width projectile.height = projectileSize * 0.6; // Double the height relative to thinned version (30% * 2) } // Set projectile appearance based on element if (self.element === 'light') { projectile.alpha = 0.9; // Slightly transparent for glow effect // Add pulsing effect for lightning tween(projectile, { scaleX: 1.3, scaleY: 1.3 }, { duration: 300, yoyo: true, repeat: -1 }); } else if (self.element === 'wind') { projectile.alpha = 0.8; // Add spinning effect for wind (except tier 4 lanza) if (self.tier !== 4) { tween(projectile, { rotation: Math.PI * 2 }, { duration: 500, repeat: -1 }); } // Add tier-specific projectile properties if (self.tier === 2) { projectile.goldenStatueChance = self.goldenStatueChance; } else if (self.tier === 4) { projectile.emeraldSlaveChance = self.emeraldSlaveChance; projectile.sleepDuration = self.sleepDuration; } } else if (self.element === 'water') { projectile.tint = 0x1e90ff; // Sea blue for water (DodgerBlue) projectile.alpha = 0.85; // Add wobbling effect for water tween(projectile, { scaleX: 1.2, scaleY: 0.8 }, { duration: 200, yoyo: true, repeat: -1 }); } self.projectiles.push(projectile); // Auto-destroy projectile after 5 seconds to prevent map clutter tween(projectile, { alpha: projectile.alpha // Dummy property to track time }, { duration: 5000, // 5 seconds onFinish: function onFinish() { // Find and remove projectile from array var index = self.projectiles.indexOf(projectile); if (index > -1) { self.projectiles.splice(index, 1); } // Destroy the projectile projectile.destroy(); } }); // Play element-specific projectile sound playRandomProjectileSound(self.element); return; // Exit after creating projectile to prevent melee sound } } else { // Melee combat - deal damage directly to target var finalDamage = self.damage + (self.auraDamageBonus || 0); target.takeDamage(finalDamage); // Fire warriors with burn ability (tier 3+) if (self.element === 'fire' && self.canBurn && target.takeBurnDamage) { target.takeBurnDamage(0, self.burnDuration, self.burnDamage); // No initial damage since we already dealt damage above } // Fire tier 4 warriors have area damage with fire explosion visual effect if (self.element === 'fire' && self.canAreaDamage) { // Create fire explosion visual effect at target position var explosion = game.addChild(LK.getAsset('explosion_fuego', { anchorX: 0.5, anchorY: 0.5, x: target.x, y: target.y, width: 120, height: 120 })); explosion.alpha = 0.9; explosion.tint = 0xff4400; // Fire color // Animate explosion - scale up and fade out tween(explosion, { scaleX: 5.0, scaleY: 5.0, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Create fire particles expanding from explosion center for (var particleIndex = 0; particleIndex < 16; particleIndex++) { var angle = particleIndex / 16 * Math.PI * 2; // Distribute evenly in circle var distance = 200 + Math.random() * 150; var particleEndX = target.x + Math.cos(angle) * distance; var particleEndY = target.y + Math.sin(angle) * distance; var fireParticle = game.addChild(LK.getAsset('explosion_fuego', { anchorX: 0.5, anchorY: 0.5, x: target.x, y: target.y, width: 30 + Math.random() * 30, height: 30 + Math.random() * 30 })); fireParticle.alpha = 0.7; fireParticle.tint = 0xff6600; // Orange fire color // Animate particles expanding outward and fading tween(fireParticle, { x: particleEndX, y: particleEndY, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 600 + Math.random() * 400, easing: tween.easeOut, onFinish: function onFinish() { fireParticle.destroy(); } }); } for (var ae = 0; ae < enemies.length; ae++) { var areaEnemy = enemies[ae]; var areaDistance = Math.sqrt(Math.pow(target.x - areaEnemy.x, 2) + Math.pow(target.y - areaEnemy.y, 2)); if (areaDistance < 400 && areaEnemy !== target) { if (areaEnemy.takeBurnDamage) { areaEnemy.takeBurnDamage(self.damage * 0.5, self.burnDuration, self.burnDamage); } } } } // Play sword attack sound for melee attacks sonidoEspadas(); } }; self.attackTower = function () { self.attackCooldown = 60; enemyTowerHealth -= self.damage; // Play random tower damage sound var towerSounds = ['sonido_torre_1', 'sonido_torre_2', 'sonido_torre_3', 'sonido_torre_4']; var randomIndex = Math.floor(Math.random() * towerSounds.length); var sound = LK.getSound(towerSounds[randomIndex]); sound.volume = soundValue; sound.play(); // Create escombros particles when enemy tower takes damage - quantity based on damage var particleCount = self.damage * 12; // Multiply particle count by damage for (var escombrosIndex = 0; escombrosIndex < particleCount; escombrosIndex++) { // 360-degree explosion pattern var angle = escombrosIndex / particleCount * Math.PI * 2; // Distribute evenly in 360 degrees var force = (200 + Math.random() * 300) * 4; // Quadruple the force var velocityX = Math.cos(angle) * force; var velocityY = Math.sin(angle) * force; var escombrosParticle = game.addChild(LK.getAsset('escombros_torre_enemiga', { anchorX: 0.5, anchorY: 0.5, x: enemyTower.x, y: enemyTower.y - 800, width: (60 + Math.random() * 40) * 5, height: (60 + Math.random() * 40) * 5, rotation: Math.random() * Math.PI * 2 })); escombrosParticle.alpha = 0.9; escombrosParticle.tint = 0x696969; // Dark gray color for enemy debris // Add gravity and physics properties escombrosParticle.velocityX = velocityX * 0.02; // Scale down for smooth movement escombrosParticle.velocityY = velocityY * 0.02; escombrosParticle.gravity = 0.8; // Gravity effect // Animate escombros particles with physics tween(escombrosParticle, { y: enemyTower.y + 200 + escombrosParticle.velocityY * 100, x: enemyTower.x + escombrosParticle.velocityX * 100, alpha: 0, rotation: escombrosParticle.rotation + Math.PI * 4, scaleX: 0.1, scaleY: 0.1 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeOut, onFinish: function onFinish() { escombrosParticle.destroy(); } }); } if (enemyTowerHealth <= 0) { subir_nivel(); } updateTowerHealthBars(); }; self.takeDamage = function (damage) { // Earth warrior tier 4+ damage immunity if (self.element === 'earth' && self.tier >= 4 && self.damageImmunity && damage <= self.damageImmunity) { // Immune to damage - show visual feedback tween(self.children[0], { tint: 0x8b4513 }, { duration: 200, onFinish: function onFinish() { tween(self.children[0], { tint: 0xffffff }, { duration: 200 }); } }); return; // No damage taken } self.health -= damage; // Update stats display to show current health self.updateStatsDisplay(); if (self.health <= 0) { // Play warrior death sound var sound = LK.getSound('muerte_guerrero'); sound.volume = soundValue; sound.play(); // Remove tracking clouds when light warrior tier 3 is destroyed if (self.element === 'light' && self.tier === 3) { // Find and destroy all tracking clouds created by this warrior for (var childIndex = game.children.length - 1; childIndex >= 0; childIndex--) { var child = game.children[childIndex]; if (child.isTrackingCloud || child.update && child.targetEnemy) { child.destroy(); } } } // Mark as being destroyed to prevent multiple destruction attempts self.isBeingDestroyed = true; // Death animation - fade out and scale down from current tier scale var deathScale = self.tierScale * 0.1; tween(warriorGraphics, { alpha: 0, scaleX: deathScale, scaleY: deathScale, rotation: -Math.PI * 2 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); // Only remove from warriors array if not already removed by collision detection if (!self.isBeingDestroyed || self.isBeingDestroyed) { var index = warriors.indexOf(self); if (index > -1) { warriors.splice(index, 1); } } } }; return self; }); /**** * Initialize Game ****/ // Reset the game by creating a new game instance var game = new LK.Game({ backgroundColor: 0x2c3e50, width: 3840, height: 1080, description: "Combine elemental notes to summon warriors with unique abilities and destroy the enemy tower in this strategic summoning game. UpitMusicContest" }); /**** * Game Code ****/ // true = English (default), false = Spanish // Variables globales para el control de bucle de sonidos de comics // Spanish level completion sounds (levels 0-25) // English level completion sounds (levels 0-25) // Reset all game variables to initial state // Language toggle variable var vida_enemigo = 5; var dano_enemigo = 1; function actualizar_dificultad() { // Add bounds checking to prevent accessing undefined array elements if (nivel >= 1 && nivel <= enemigos.length) { vida_enemigo = enemigos[nivel - 1][1]; dano_enemigo = enemigos[nivel - 1][2]; spameo_enemigos = enemigos[nivel - 1][3]; } else { // Default values for invalid levels vida_enemigo = 5; dano_enemigo = 1; spameo_enemigos = 4; } console.log(nivel + " - " + vida_enemigo + " - " + dano_enemigo + " - " + spameo_enemigos); } var idioma = false; var currentComicSound = null; var currentSoundTimer = null; warriors = []; enemies = []; meteoritos = []; esporas = []; globalFireModeActive = false; fireModeTimer = 0; playerTowerHealth = 30; enemyTowerHealth = 10; enemySpawnTimer = 0; isDragging = false; lastMouseX = 0; cameraX = 0; currentMana = 5; maxMana = 5; manaRegenTimer = 0; miniola_colision = false; currentCombination = []; combinationTimer = 0; combinationCooldown = 0; temporaryNoteCounter = 0; tiempoDeApocalipsis = 0; elementList = []; noteTimingIntervals = []; lastNoteTime = 0; isFirstNote = true; initialTouchX = 0; initialTouchY = 0; currentTouchX = 0; currentTouchY = 0; musicBeatThreshold = 0; lastBeatTime = 0; clouds = []; cloudSpawnTimer = 0; settingsPanelVisible = false; musicValue = 0.8; soundValue = 0.8; sensitivityValue = 0.5; revelacionTimer = 0; i = 0; 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); } 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); } // Array de datos de enemigos para 30 niveles // Formato: [nivel, vida, daño, velocidad_spawn_en_segundos] var enemigos = [[1, 5, 2, 8], [2, 7, 2, 6], [3, 10, 2, 8], [4, 4, 10, 8], [5, 10, 2, 4], [6, 10, 2, 3], [7, 20, 2, 8], [8, 40, 3, 8], [9, 8, 10, 8], [10, 10, 20, 8], [11, 10, 10, 3], [12, 80, 2, 8], [13, 90, 8, 16], [14, 60, 3, 4], [15, 40, 5, 3], [16, 60, 6, 8], [17, 80, 4, 4], // Nota: nivel 17 agregado ya que falta en los datos [18, 120, 4, 6], [19, 100, 7, 4], [20, 100, 10, 4], [21, 80, 9, 3], [22, 400, 2, 16], [23, 100, 9, 4], [24, 100, 4, 3], [25, 800, 11, 16], [26, 200, 9, 4], [27, 200, 100, 8], [28, 200, 10, 16], [29, 200, 40, 4], [30, 200, 5, 6], [31, 1, 1, 10]]; // Array de probabilidades para las clases de enemigos // Formato: [nivel, probabilidad_cuerpo_a_cuerpo, probabilidad_aero, probabilidad_a_distancia] var probabilidades = [[1, 100, 0, 0], [2, 100, 0, 0], [3, 80, 0, 20], [4, 50, 50, 0], [5, 60, 25, 15], [6, 50, 30, 20], [7, 45, 30, 25], [8, 40, 30, 30], [9, 35, 32, 33], [10, 30, 60, 10], [11, 25, 37, 38], [12, 20, 40, 40], [13, 20, 40, 40], [14, 60, 10, 30], [15, 80, 5, 5], [16, 34, 33, 33], [17, 90, 3, 7], [18, 70, 5, 25], [19, 55, 44, 10], [20, 30, 40, 30], [21, 65, 5, 30], [22, 70, 15, 15], [23, 25, 5, 70], [24, 80, 5, 15], [25, 20, 70, 10], [26, 78, 20, 2], [27, 100, 0, 0], [28, 0, 0, 100], [29, 0, 100, 0], [30, 34, 33, 33], [31, 60, 10, 30]]; var enemigos_aereos = [[4, 'enemyUnit4'], [8, 'enemyUnit8'], [11, 'enemyUnit11'], [14, 'enemyUnit14'], [17, 'enemyUnit17'], [20, 'enemyUnit20'], [23, 'enemyUnit23'], [26, 'enemyUnit26'], [29, 'enemyUnit29']]; var enemigos_cuerpo = [[1, 'enemyUnit'], [2, 'enemyUnit2'], [3, 'enemyUnit3'], [5, 'enemyUnit5'], [7, 'enemyUnit7'], [10, 'enemyUnit10'], [13, 'enemyUnit13'], [16, 'enemyUnit16'], [19, 'enemyUnit19'], [22, 'enemyUnit22'], [25, 'enemyUnit25'], [28, 'enemyUnit28'], [31, 'enemyUnit31']]; var enemigos_distancia = [[6, 'enemyUnit6'], [9, 'enemyUnit9'], [12, 'enemyUnit12'], [15, 'enemyUnit15'], [18, 'enemyUnit18'], [21, 'enemyUnit21'], [24, 'enemyUnit24'], [27, 'enemyUnit27'], [30, 'enemyUnit30']]; var nivel = -1; var nivel_llegado = 31; var pausa = true; // Level-based unlocking system variables var unlockedElements = []; // Array to track which elements are unlocked for note buttons var unlockedCombinationElements = []; // Array to track which elements are unlocked for combinations var isApocalypticNotesVisible = false; // Track if apocalyptic notes should be visible var unlockedCombinations = []; // Track which combination types are unlocked var warriors = []; var enemies = []; var meteoritos = []; var esporas = []; // Individual dormido property will be added to each enemy instance var globalFireModeActive = false; var fireModeTimer = 0; var playerTowerHealth = 10; var enemyTowerHealth = 10; var enemySpawnTimer = 0; var spameo_enemigos = 4; var tem = spameo_enemigos; var Unidades_invocadas = 0; // Counter for spawned enemies var enemy_spawn_cooldown = false; // Flag to control spawn cooldown var isDragging = false; var lastMouseX = 0; var cameraX = 0; var maxCameraX = 1792; // 3840 - 2048 = max scroll distance var currentMana = 5; var maxMana = 5; var manaRegenTimer = 0; var miniola_colision = false; // Combination system variables var currentCombination = []; var combinationTimer = 0; var combinationCooldown = 0; var maxCombinationLength = 10; var combinationWindowTime = 60; // 1 second at 60fps var combinationResetTime = 60; // 1 second at 60fps var temporaryNoteCounter = 0; // Helper counter for tracking notes (1-5) // Apocalipsis system variables var tiempoDeApocalipsis = 0; // Timer for tracking idle time without note presses (10 seconds = 600 frames) var apocalipsisThreshold = 600; // 10 seconds at 60fps // Element list system for meteorite invocation var elementList = []; var maxElementList = 5; // Timing interval tracking variables var noteTimingIntervals = []; // Store time intervals between note presses var lastNoteTime = 0; // Timestamp of last note press var isFirstNote = true; // Track if this is the first note in a sequence // Touch position tracking variables var initialTouchX = 0; var initialTouchY = 0; var currentTouchX = 0; var currentTouchY = 0; // Music beat detection variables var musicBeatThreshold = 0; // Default sensitivity threshold set to 0 - will be controlled by sensitivity button var lastBeatTime = 0; var beatCooldown = 15; // Reduced cooldown for more frequent detections (0.25 seconds at 60fps) // UI Elements var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 120; scoreText.y = 50; LK.gui.topLeft.addChild(scoreText); // Create level display text above mana var levelText = new Text2('Nivel: 0', { size: 40, fill: 0xFFFF00 }); levelText.anchor.set(0, 0); levelText.x = 1100; levelText.y = 50; LK.gui.topLeft.addChild(levelText); // Create mana display var manaText = new Text2('Mana: 5/5', { size: 40, fill: 0x00FFFF }); manaText.anchor.set(0, 0); manaText.x = 1100; manaText.y = 100; LK.gui.topLeft.addChild(manaText); // Create mana orbs visual display var manaOrbs = []; for (var i = 0; i < maxMana; i++) { var orb = LK.getAsset('manaOrbShape', { width: 40, height: 40 }); orb.anchor.set(0.5, 0.5); orb.x = 1100 + i * 50; orb.y = 160; LK.gui.topLeft.addChild(orb); manaOrbs.push(orb); } // Settings panel state var settingsPanelVisible = false; var settingsBars = []; // Settings values (0.0 to 1.0 range) var musicValue = 0.8; var soundValue = 0.8; var sensitivityValue = 0.5; // Create settings button below mana bar var settingsButton = LK.getAsset('settingsIcon', { width: 80, height: 80 }); settingsButton.anchor.set(0.5, 0.5); settingsButton.x = 1300; // Move slightly to the right settingsButton.y = 250; // Move down a bit more settingsButton.alpha = 0.8; LK.gui.topLeft.addChild(settingsButton); //Crear boton de menu var menubutton = LK.getAsset('MenuIcon', { width: 80, height: 80 }); menubutton.anchor.set(0.5, 0.5); menubutton.x = 1100; // Move slightly to the right menubutton.y = 250; // Move down a bit more menubutton.alpha = 0.8; LK.gui.topLeft.addChild(menubutton); // Add menu button click handler menubutton.down = function (x, y, obj) { nivel = -1; // Call the activar_menu function activar_menu(); musiclevel(); // Visual feedback with bounce effect tween(menubutton, { scaleX: 0.85, scaleY: 0.85, alpha: 0.6, rotation: Math.PI * 0.1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(menubutton, { scaleX: 1.05, scaleY: 1.05, alpha: 0.9, rotation: 0 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { tween(menubutton, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 100 }); } }); } }); }; // Create pause button next to settings button var pauseButton = LK.getAsset('PausarIcon', { width: 80, height: 80 }); pauseButton.anchor.set(0.5, 0.5); pauseButton.x = 1200; // Position to the right of settings button pauseButton.y = 250; // Same Y position as settings button pauseButton.alpha = 0.8; LK.gui.topLeft.addChild(pauseButton); // Create pause button text var pauseButtonText = new Text2('||', { size: 50, fill: 0xffffff }); pauseButtonText.anchor.set(0.5, 0.5); pauseButton.addChild(pauseButtonText); // Ensure pauseButtonText is rendered on top of pauseButton LK.gui.topLeft.removeChild(pauseButton); LK.gui.topLeft.addChild(pauseButton); // Create music volume text input var musicVolumeInput = new Text2('Volumen Música: 80', { size: 30, fill: 0xFFFFFF }); musicVolumeInput.anchor.set(0.5, 0.5); musicVolumeInput.x = 1220; musicVolumeInput.y = 330; musicVolumeInput.alpha = 0; LK.gui.topLeft.addChild(musicVolumeInput); // Store all settings elements settingsBars = [musicVolumeInput]; // Function to update music volume function updateMusicVolume(value) { // Parse and validate the input value var volumeNumber = parseInt(value); if (isNaN(volumeNumber)) { volumeNumber = 80; } volumeNumber = Math.max(0, Math.min(100, volumeNumber)); musicValue = volumeNumber / 100; musicVolumeInput.setText('Volumen Música: ' + volumeNumber); // Apply music volume changes immediately LK.stopMusic(); musiclevel(); } // Music volume input interaction musicVolumeInput.down = function (x, y, obj) { // Stop any existing tweens on this object to prevent stacking animations tween.stop(musicVolumeInput); // Simple interaction for now - could be enhanced with actual text input var currentVolume = Math.round(musicValue * 100); var newVolume = (currentVolume + 10) % 100; // Cycle through 0-100 by 10s updateMusicVolume(newVolume); // Add a small visual feedback animation that stays within bounds var originalY = musicVolumeInput.y; tween(musicVolumeInput, { y: originalY - 10 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(musicVolumeInput, { y: originalY }, { duration: 100, easing: tween.easeInOut }); } }); }; settingsButton.down = function (x, y, obj) { // Play settings click sound with centralized control like music - stop and restart with current volume LK.getSound('settings_click').stop(); // Stop any currently playing instance var settingsSound = LK.getSound('settings_click'); settingsSound.volume = soundValue * 0.7; // Apply current soundValue with 70% modifier settingsSound.play(); // Visual feedback with bounce effect tween(settingsButton, { scaleX: 0.85, scaleY: 0.85, alpha: 0.6, rotation: Math.PI * 0.1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(settingsButton, { scaleX: 1.05, scaleY: 1.05, alpha: 0.9, rotation: 0 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { tween(settingsButton, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 100 }); } }); } }); // Toggle settings panel with staggered animations if (settingsPanelVisible) { // Hide settings panel with staggered fade out settingsPanelVisible = false; for (var i = 0; i < settingsBars.length; i++) { var delay = i * 30; // Stagger each element by 30ms tween(settingsBars[i], { alpha: 0, y: settingsBars[i].y - 20, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, delay: delay, easing: tween.easeInOut }); } } else { // Show settings panel with bouncy entrance settingsPanelVisible = true; for (var i = 0; i < settingsBars.length; i++) { var delay = i * 40; // Stagger each element by 40ms var targetY = settingsBars[i].y; settingsBars[i].y = targetY - 30; settingsBars[i].scaleX = 0.6; settingsBars[i].scaleY = 0.6; tween(settingsBars[i], { alpha: 1, y: targetY, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, delay: delay, easing: tween.elasticOut }); } } }; pauseButton.down = function (x, y, obj) { // Play settings click sound LK.getSound('settings_click').stop(); // Stop any currently playing instance var pauseSound = LK.getSound('settings_click'); pauseSound.volume = soundValue * 0.7; // Apply current soundValue with 70% modifier pauseSound.play(); // Toggle pause state pausa = !pausa; // Update button text and appearance based on pause state if (pausa) { pauseButtonText.setText('||'); // Pause symbol pauseButton.tint = 0xff4444; // Red tint when paused // Switch to menu music when paused } else { pauseButtonText.setText('▶'); // Play symbol pauseButton.tint = 0xffffff; // Green tint when playing // Switch back to regular music when unpaused } // Visual feedback with bounce effect tween(pauseButton, { scaleX: 0.85, scaleY: 0.85, alpha: 0.6, rotation: Math.PI * 0.1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(pauseButton, { scaleX: 1.05, scaleY: 1.05, alpha: 0.9, rotation: 0 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { tween(pauseButton, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 100 }); } }); } }); }; // Create update message text (multilingual support) var updateMessageText = new Text2('If the game slows down, please reset the browser tab.\nSi se ralentiza el juego, resetee la pestaña del navegador', { size: 30, fill: 0xFFFFFF, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); updateMessageText.anchor.set(0.0, 1.0); // Align to bottom-left corner updateMessageText.x = 50; // Left margin updateMessageText.y = 1900; // Bottom left position updateMessageText.alpha = 1.0; // Fully visible // Create black background for comic system in GUI layer var fondoNegro = LK.getAsset('fondo_negro', { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 2048, height: 2732 }); fondoNegro.alpha = 0; // Initially invisible LK.gui.addChild(fondoNegro); // Create f_m image to cover full screen on GUI layer var fmImage = LK.getAsset('f_m', { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 1548, height: 1932 }); fmImage.alpha = 1.0; // Fully visible LK.gui.addChild(fmImage); // Add update message text after f_m image to appear above it LK.gui.addChild(updateMessageText); // Create language toggle button above f_m image var languageButton = LK.getAsset('IdiomaIcon', { anchorX: 0.5, anchorY: 0.5, x: 1200, y: 1750, width: 300, height: 100 }); languageButton.alpha = 0.9; // Add language button after f_m but before fondo_negro var fmImageIndex = LK.gui.children.indexOf(fmImage); if (fmImageIndex >= 0) { LK.gui.addChildAt(languageButton, fmImageIndex + 1); } else { LK.gui.addChild(languageButton); } // Create language button text var languageButtonText = new Text2('Change to English', { size: 32, fill: 0xffffff, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); languageButtonText.anchor.set(0.5, 0.5); languageButtonText.x = 1200; languageButtonText.y = 1750; languageButtonText.alpha = 1; // Add language button text after language button but before fondo_negro var languageButtonIndex = LK.gui.children.indexOf(languageButton); if (languageButtonIndex >= 0) { LK.gui.addChildAt(languageButtonText, languageButtonIndex + 1); } else { LK.gui.addChild(languageButtonText); } // Language button interaction languageButton.down = function (x, y, obj) { // Toggle language idioma = !idioma; // Update button text based on current language if (idioma) { // Currently English, show option to change to Spanish languageButtonText.setText('Cambiar a español'); } else { // Currently Spanish, show option to change to English languageButtonText.setText('Change to English'); } // Visual feedback with bounce effect tween(languageButton, { scaleX: 0.85, scaleY: 0.85, alpha: 0.6 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(languageButton, { scaleX: 1.05, scaleY: 1.05, alpha: 1.0 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { tween(languageButton, { scaleX: 1.0, scaleY: 1.0, alpha: 0.9 }, { duration: 100 }); } }); } }); doblaje(); musiclevel(); }; // Create "jugar" button on top of f_m image var jugarButton = LK.getAsset('jugar', { anchorX: 0.5, anchorY: 0.5, x: 700, y: 600, width: 500, height: 300 }); jugarButton.alpha = 0.9; LK.gui.addChild(jugarButton); // Add button interaction jugarButton.down = function (x, y, obj) { // Visual feedback with bounce effect tween(jugarButton, { scaleX: 0.85, scaleY: 0.85, alpha: 0.6, rotation: Math.PI * 0.05 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(jugarButton, { scaleX: 1.05, scaleY: 1.05, alpha: 1, rotation: 0 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { tween(jugarButton, { scaleX: 1.0, scaleY: 1.0, alpha: 0.9 }, { duration: 100, onFinish: function onFinish() { // Create 30 level selection buttons above the l_m image var levelButtons = []; for (var i = 0; i < 32; i++) { var levelButton = LK.getAsset('boton_nivel', { anchorX: 0.5, anchorY: 0.5, x: 300 + i % 8 * 120, // 8 buttons per row, spaced 120 pixels apart y: 900 + Math.floor(i / 8) * 120, // 4 rows, spaced 120 pixels apart width: 100, height: 100 }); // Set button properties levelButton.nivel = i; // Level 0-29 levelButton.boton_habilitado = i <= nivel_llegado; // Enable if level reached // Use correct asset based on enabled state if (!levelButton.boton_habilitado) { levelButton.destroy(); levelButton = LK.getAsset('boton_nivel_desactivado', { anchorX: 0.5, anchorY: 0.5, x: 300 + i % 8 * 120, y: 900 + Math.floor(i / 8) * 120, width: 100, height: 100, alpha: 0.5 }); levelButton.nivel = i; levelButton.boton_habilitado = false; } // Add level number text with persistent visibility var buttonText = new Text2(i.toString(), { size: 70, fill: 0xffffff }); buttonText.anchor.set(0.5, 0.5); buttonText.alpha = 1.0; // Ensure text is fully visible buttonText.x = 300 + i % 8 * 120; // Position same as button buttonText.y = 900 + Math.floor(i / 8) * 120; // Position same as button // Store reference to prevent garbage collection levelButton.levelText = buttonText; // Add click handler only for enabled buttons if (levelButton.boton_habilitado) { levelButton.down = function (x, y, obj) { destroy_mapa(); // Set nivel variable to the selected button's level nivel = this.nivel; // Update the global level display text (not the button's own text) levelText.setText('Nivel: ' + nivel); // Update unlocking system based on selected level updateLevelUnlocks(); // Reset towers with their function when level is selected // Calculate new tower health: 10 + (10 * nivel) var newTowerHealth = 10 * nivel; playerTowerHealth = newTowerHealth; enemyTowerHealth = newTowerHealth; // Update enemy tower asset if level 31 is selected if (nivel === 31) { // Destroy current enemy tower enemyTower.destroy(); // Create new tower with torre_final asset enemyTower = game.addChild(LK.getAsset('torre_final', { anchorX: 0.5, anchorY: 1.0, x: 3590, y: 2559, width: 1200, height: 1728 })); } else { // Destroy current enemy tower enemyTower.destroy(); // Create new tower with torre_final asset enemyTower = game.addChild(LK.getAsset('enemyTower', { anchorX: 0.5, anchorY: 1.0, x: 3590, y: 2559, width: 1200, height: 1728 })); } //Ejecutar music musiclevel(); // Update tower health bars and reset scale to full size updateTowerHealthBars(); // Reset tower scales to full size (1.0) tween.stop(playerTower, { scaleX: true, scaleY: true }); tween.stop(enemyTower, { scaleX: true, scaleY: true }); tween(playerTower, { scaleX: 1.0, scaleY: 1.0 }, { duration: 500, easing: tween.easeInOut }); tween(enemyTower, { scaleX: 1.0, scaleY: 1.0 }, { duration: 500, easing: tween.easeInOut }); // Hide l_m image fmImage.visible = false; // Hide jugar button jugarButton.visible = false; // Hide all level buttons and their text for (var j = 0; j < levelButtons.length; j++) { if (levelButtons[j] && levelButtons[j].parent) { levelButtons[j].visible = false; } // Hide buttonText as well if (levelButtons[j] && levelButtons[j].levelText) { levelButtons[j].levelText.visible = false; } } // Set pausar to false pausa = false; // Change pause button to play state pauseButtonText.setText('▶'); // Play symbol pauseButton.tint = 0xffffff; // Green tint when playing // Audio al dar clic doblaje(); actualizar_dificultad(); }; } // Add buttons to GUI layer to ensure they appear above l_m image LK.gui.addChild(levelButton); levelButtons.push(levelButton); // Add buttonText after levelButton to ensure it appears on top LK.gui.addChild(buttonText); // Force text to stay visible buttonText.visible = true; } } }); } }); } }); }; function doblaje() { var levelSound = null; // Initialize levelSound variable // Stop any currently playing level sounds by stopping all potential level sounds if (currentComicSound) { currentComicSound.stop(); } // Stop all level sounds at function start var allLevelSounds = ['Level_1_en', 'Level_2_en', 'Level_3_en', 'Level_4_en', 'Level_5_en', 'Level_6_en', 'Level_7_en', 'Level_8_en', 'Level_9_en', 'Level_10_en', 'Level_11_en', 'Level_12_en', 'Level_13_en', 'Level_14_en', 'Level_15_en', 'Level_16_en', 'Level_17_en', 'Level_18_en', 'Level_19_en', 'Level_20_en', 'Level_21_en', 'Level_22_en', 'Level_23_en', 'Level_24_en', 'Level_25_en', 'Level_26_en', 'Level_1_es', 'Level_2_es', 'Level_3_es', 'Level_4_es', 'Level_5_es', 'Level_6_es', 'Level_7_es', 'Level_8_es', 'Level_9_es', 'Level_10_es', 'Level_11_es', 'Level_12_es', 'Level_13_es', 'Level_14_es', 'Level_15_es', 'Level_16_es', 'Level_17_es', 'Level_18_es', 'Level_19_es', 'Level_20_es', 'Level_21_es', 'Level_22_es', 'Level_23_es', 'Level_24_es', 'Level_25_es', 'Level_26_es']; for (var i = 0; i < allLevelSounds.length; i++) { try { var sound = LK.getSound(allLevelSounds[i]); if (sound && typeof sound.stop === 'function') { sound.stop(); } } catch (e) { // Ignore errors for sounds that don't exist } } if (idioma) { if (nivel == 1) { levelSound = LK.getSound('Level_1_en'); } if (nivel == 2) { levelSound = LK.getSound('Level_2_en'); } if (nivel == 3) { levelSound = LK.getSound('Level_3_en'); } if (nivel == 4) { levelSound = LK.getSound('Level_4_en'); } if (nivel == 5) { levelSound = LK.getSound('Level_5_en'); } if (nivel == 6) { levelSound = LK.getSound('Level_6_en'); } if (nivel == 7) { levelSound = LK.getSound('Level_7_en'); } if (nivel == 8) { levelSound = LK.getSound('Level_8_en'); } if (nivel == 9) { levelSound = LK.getSound('Level_9_en'); } if (nivel == 10) { levelSound = LK.getSound('Level_10_en'); } if (nivel == 11) { levelSound = LK.getSound('Level_11_en'); } if (nivel == 12) { levelSound = LK.getSound('Level_12_en'); } if (nivel == 13) { levelSound = LK.getSound('Level_13_en'); } if (nivel == 14) { levelSound = LK.getSound('Level_14_en'); } if (nivel == 15) { levelSound = LK.getSound('Level_15_en'); } if (nivel == 16) { levelSound = LK.getSound('Level_16_en'); } if (nivel == 17) { levelSound = LK.getSound('Level_17_en'); } if (nivel == 18) { levelSound = LK.getSound('Level_18_en'); } if (nivel == 19) { levelSound = LK.getSound('Level_19_en'); } if (nivel == 20) { levelSound = LK.getSound('Level_20_en'); } if (nivel == 21) { levelSound = LK.getSound('Level_21_en'); } if (nivel == 22) { levelSound = LK.getSound('Level_22_en'); } if (nivel == 23) { levelSound = LK.getSound('Level_23_en'); } if (nivel == 24) { levelSound = LK.getSound('Level_24_en'); } if (nivel == 25) { levelSound = LK.getSound('Level_25_en'); } if (nivel == 26) { levelSound = LK.getSound('Level_26_en'); } } else { if (nivel == 1) { levelSound = LK.getSound('Level_1_es'); } if (nivel == 2) { levelSound = LK.getSound('Level_2_es'); } if (nivel == 3) { levelSound = LK.getSound('Level_3_es'); } if (nivel == 4) { levelSound = LK.getSound('Level_4_es'); } if (nivel == 5) { levelSound = LK.getSound('Level_5_es'); } if (nivel == 6) { levelSound = LK.getSound('Level_6_es'); } if (nivel == 7) { levelSound = LK.getSound('Level_7_es'); } if (nivel == 8) { levelSound = LK.getSound('Level_8_es'); } if (nivel == 9) { levelSound = LK.getSound('Level_9_es'); } if (nivel == 10) { levelSound = LK.getSound('Level_10_es'); } if (nivel == 11) { levelSound = LK.getSound('Level_11_es'); } if (nivel == 12) { levelSound = LK.getSound('Level_12_es'); } if (nivel == 13) { levelSound = LK.getSound('Level_13_es'); } if (nivel == 14) { levelSound = LK.getSound('Level_14_es'); } if (nivel == 15) { levelSound = LK.getSound('Level_15_es'); } if (nivel == 16) { levelSound = LK.getSound('Level_16_es'); } if (nivel == 17) { levelSound = LK.getSound('Level_17_es'); } if (nivel == 18) { levelSound = LK.getSound('Level_18_es'); } if (nivel == 19) { levelSound = LK.getSound('Level_19_es'); } if (nivel == 20) { levelSound = LK.getSound('Level_20_es'); } if (nivel == 21) { levelSound = LK.getSound('Level_21_es'); } if (nivel == 22) { levelSound = LK.getSound('Level_22_es'); } if (nivel == 23) { levelSound = LK.getSound('Level_23_es'); } if (nivel == 24) { levelSound = LK.getSound('Level_24_es'); } if (nivel == 25) { levelSound = LK.getSound('Level_25_es'); } if (nivel == 26) { levelSound = LK.getSound('Level_26_es'); } } // Only play sound if levelSound was successfully assigned and is valid if (levelSound && typeof levelSound.volume !== 'undefined') { levelSound.volume = soundValue; levelSound.play(); } } // Create collision debug text in center of game var collisionDebugText = new Text2(/*'Collision Debug'*/'', { size: 60, fill: 0xFFFF00 }); collisionDebugText.anchor.set(0.5, 0.5); collisionDebugText.x = 1024; // Center of screen width (2048/2) collisionDebugText.y = 1366; // Center of screen height (2732/2) game.addChild(collisionDebugText); // Create audio debug text to show real-time values var audioDebugText = new Text2(/*'Audio: 0.00 | Threshold: 0.15 | Diff: 0.00'*/'', { size: 40, fill: 0x00FFFF }); audioDebugText.anchor.set(0.5, 0.5); audioDebugText.x = 1024; // Center of screen width audioDebugText.y = 1200; // Above collision debug text game.addChild(audioDebugText); // Create touch position debug text var touchDebugText = new Text2(/*'Touch Info: Initial(0,0) Current(0,0)'*/'', { size: 40, fill: 0xFF00FF }); touchDebugText.anchor.set(0.5, 0.5); touchDebugText.x = 1024; // Center of screen width touchDebugText.y = 1500; // Below collision debug text game.addChild(touchDebugText); // Create combination message text var combinationMessageText = new Text2('', { size: 80, fill: 0xFFFFFF, font: "'Impact','Arial Black',Courier,'Courier New',monospace", stroke: 0x000000, strokeThickness: 4 }); combinationMessageText.anchor.set(0.5, 0.5); combinationMessageText.x = 800; // Center of screen width (2048/2) combinationMessageText.y = 800; // Above other debug texts combinationMessageText.alpha = 0; game.addChild(combinationMessageText); // Create temporary note counter info text var tempCounterInfoText = new Text2(/*'Notas presionadas: 0'*/'', { size: 40, fill: 0x00FF00 }); tempCounterInfoText.anchor.set(0.5, 0.5); tempCounterInfoText.x = 1024; // Center of screen width tempCounterInfoText.y = 600; // Above combination message text game.addChild(tempCounterInfoText); // Create element list display text var elementListText = new Text2(/*'Lista de Elementos: []'*/'', { size: 45, fill: 0xFFD700 }); elementListText.anchor.set(0.5, 0.5); elementListText.x = 1024; // Center of screen width elementListText.y = 400; // Above temp counter text game.addChild(elementListText); // Create comic counter display text var numberComic = new Text2(/*'Comic: 0/11'*/'' / { size: 40, fill: 0xFF00FF }); numberComic.anchor.set(0.5, 0.5); numberComic.x = 1024; // Center of screen width numberComic.y = 350; // Above element list text game.addChild(numberComic); // Create timing intervals info text var timingIntervalsText = new Text2(/*'Intervalos: []'*/'', { size: 35, fill: 0xFFAA00 }); timingIntervalsText.anchor.set(0.5, 0.5); timingIntervalsText.x = 1024; // Center of screen width timingIntervalsText.y = 500; // Above temp counter text game.addChild(timingIntervalsText); // Create sky background - always behind everything var name_cielo = 'cielo'; var skyBackground = game.addChildAt(LK.getAsset(name_cielo, { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 3840, height: 2732 }), 0); // Add at index 0 to be behind everything // Add normally to be above towers and characters // Add sun behind clouds but above sky background var name_sol_luna = 'luna'; var sol_luna = true; if (sol_luna) { name_sol_luna = 'sol'; } else { name_sol_luna = 'luna'; } var sol = game.addChildAt(LK.getAsset(name_sol_luna, { anchorX: 0.5, anchorY: 0.5, x: -500, y: 400, width: 620, height: 620 }), 1); // Add at index 1 to be behind clouds but above sky var dark_alpha = 0; sol.alpha = 0.8; // Slightly transparent for softer effect function moveSun() { tween(sol, { x: 4500 }, { duration: 60000, // 60 seconds to cross the sky easing: tween.linear, onFinish: function onFinish() { // Store current position and properties var currentX = -500; var currentY = sol.y; var currentAlpha = sol.alpha; var currentIndex = game.children.indexOf(sol); // Alternar entre sol y luna if (sol_luna) { name_sol_luna = 'sol'; name_cielo = 'cielo'; sol_luna = false; dark_alpha = 0; // Fade out darkness overlay for day time tween(darknessOverlay, { alpha: dark_alpha }, { duration: 3000, easing: tween.easeInOut }); } else { name_sol_luna = 'luna'; name_cielo = 'cielo_noche'; sol_luna = true; dark_alpha = 0.6; // Fade in darkness overlay for night time tween(darknessOverlay, { alpha: dark_alpha }, { duration: 3000, easing: tween.easeInOut }); } skyBackground.destroy(); skyBackground = game.addChildAt(LK.getAsset(name_cielo, { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 3840, height: 2732 }), 0); // Destroy current sol object sol.destroy(); // Create new sol object with the correct image sol = game.addChildAt(LK.getAsset(name_sol_luna, { anchorX: 0.5, anchorY: 0.5, x: currentX, y: currentY, width: 620, height: 620 }), currentIndex); // Add at the same index to maintain layer order sol.alpha = currentAlpha; // Restore alpha console.log('Sol ' + sol_luna); moveSun(); // Restart the movement } }); } // Start the sun movement moveSun(); // Create tiled ground floor var groundTiles = []; var tileSize = 592.704; // Size of each ground tile (increased by 5% more) var groundY = 2732; // Position at bottom of screen var groundHeight = 728.6; // Extend height to fill bottom gap (tileSize + 136 pixels) var numTiles = Math.ceil(3840 / tileSize) + 2; // Cover full width plus extra for seamless tiling for (var i = 0; i < numTiles; i++) { var groundTile = game.addChild(LK.getAsset('ground', { anchorX: 0.0, anchorY: 1.0, x: i * tileSize, y: groundY, width: tileSize, height: groundHeight })); groundTiles.push(groundTile); } // Create red overlay for fire mode (initially hidden) var fireOverlay = game.addChild(LK.getAsset('healthBar', { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 3840, height: 2732 })); fireOverlay.tint = 0xff0000; // Red color fireOverlay.alpha = 0; // Initially hidden // Create towers (horizontal layout) var playerTower = game.addChild(LK.getAsset('playerTower', { anchorX: 0.5, anchorY: 1.0, x: 250, y: 2559, width: 1200, height: 1728 })); // Use torre_final for level 31, normal enemyTower for other levels var enemyTowerAsset = nivel === 31 ? 'torre_final' : 'enemyTower'; var enemyTower = game.addChild(LK.getAsset(enemyTowerAsset, { anchorX: 0.5, anchorY: 1.0, x: 3590, y: 2559, width: 1200, height: 1728 })); // Tower health bars var playerTowerHealthBar = LK.getAsset('playerTowerHealthBar', { width: 1200, height: 160 }); playerTowerHealthBar.anchor.set(0.5, 0.5); playerTowerHealthBar.x = playerTower.x; playerTowerHealthBar.y = playerTower.y - 1840; game.addChild(playerTowerHealthBar); // Player tower health text centered on health bar var playerTowerHealthText = new Text2('10/10', { size: 80, fill: 0xFFFFFF }); playerTowerHealthText.anchor.set(0.5, 0.5); playerTowerHealthText.x = playerTowerHealthBar.x; playerTowerHealthText.y = playerTowerHealthBar.y; game.addChild(playerTowerHealthText); var enemyTowerHealthBar = LK.getAsset('enemyTowerHealthBar', { width: 1200, height: 160 }); enemyTowerHealthBar.anchor.set(0.5, 0.5); enemyTowerHealthBar.x = enemyTower.x; enemyTowerHealthBar.y = enemyTower.y - 1840; game.addChild(enemyTowerHealthBar); // Enemy tower health text centered on health bar var enemyTowerHealthText = new Text2('10/10', { size: 80, fill: 0xFFFFFF }); enemyTowerHealthText.anchor.set(0.5, 0.5); enemyTowerHealthText.x = enemyTowerHealthBar.x; enemyTowerHealthText.y = enemyTowerHealthBar.y; game.addChild(enemyTowerHealthText); //crea darkess var darknessOverlay = game.addChild(LK.getAsset('darknessOverlay', { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 3840, height: 2732 })); function effetc() { if (darknessOverlay && darknessOverlay.parent) { darknessOverlay.destroy(); } // Create darkness overlay for night time (initially hidden) darknessOverlay = game.addChild(LK.getAsset('darknessOverlay', { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 3840, height: 2732 })); darknessOverlay.alpha = dark_alpha; // Initially invisible } // Create elemental notes (horizontal layout at top left) - show all elements var elements = ['fire', 'water', 'earth', 'wind', 'light']; var notes = []; for (var i = 0; i < elements.length; i++) { var note = new ElementalNote(elements[i], 0, 0); LK.gui.topLeft.addChild(note); // Make buttons smaller note.scaleX = 0.4; note.scaleY = 0.4; // Position buttons horizontally in upper left with proper spacing (moved 50% right total) note.x = 234 + i * 140; // Start at x:234 (moved from 180), space buttons 140 pixels apart note.y = 120; // Fixed y position in upper area notes.push(note); } // Create five apocalyptic note images below the elemental notes var casillas = []; for (var i = 0; i < 5; i++) { var casilla = LK.getAsset('nota_apocalitica', { width: 60, height: 60, anchorX: 0.5, anchorY: 0.5 }); casilla.x = 409 + i * 70; // Moved left 25 units (from 434 to 409) casilla.y = 220; // Moved down 20 units (from 200 to 220) LK.gui.topLeft.addChild(casilla); casillas.push(casilla); } // Create camera control buttons var leftButton = LK.getAsset('leftCameraButton', { width: 1400, height: 1200, alpha: 0 }); leftButton.anchor.set(0.5, 0.5); LK.gui.left.addChild(leftButton); var leftButtonText = new Text2(' <', { size: 200, fill: 0xffffff, alpha: 0.1 }); leftButtonText.anchor.set(0.5, 0.5); leftButtonText.alpha = 0.4; LK.gui.left.addChild(leftButtonText); var rightButton = LK.getAsset('rightCameraButton', { width: 1400, height: 1200, alpha: 0 }); rightButton.anchor.set(0.5, 0.5); LK.gui.right.addChild(rightButton); var rightButtonText = new Text2('> ', { size: 200, fill: 0xffffff, alpha: 0.1 }); rightButtonText.anchor.set(0.5, 0.5); rightButtonText.alpha = 0.4; LK.gui.right.addChild(rightButtonText); function notePressed(element) { // Note: Removed element unlock check here - let combination validation handle restrictions // All note buttons should be pressable, but combinations will be validated separately // Reset apocalypse timer when any note is pressed tiempoDeApocalipsis = 0; // If this is the start of a new combination, clear previous timing data if (currentCombination.length === 0) { noteTimingIntervals = []; isFirstNote = true; lastNoteTime = 0; } // Record timing for interval tracking var currentTime = Date.now(); if (isFirstNote) { // First note - record time and initialize with 0 interval lastNoteTime = currentTime; isFirstNote = false; noteTimingIntervals = [0]; // Initialize with 0 for first note } else { // Calculate interval since last note var interval = currentTime - lastNoteTime; noteTimingIntervals.push(interval); lastNoteTime = currentTime; } // Update timing intervals display /*var intervalsDisplay = noteTimingIntervals.length > 0 ? 'Intervalos: [' + noteTimingIntervals.map(function (interval) { return Math.round(interval) + 'ms'; }).join(', ') + ']' : 'Intervalos: []'; timingIntervalsText.setText(intervalsDisplay);*/ // Element list is now managed separately by crea_combinacion_meteorito function // Do not add pressed notes to element list // Increment temporary counter temporaryNoteCounter++; // Add element to current combination addToCombination(element); // Check if current combination exactly matches the required meteorite sequence if (currentCombination.length === 5) { var hasMatchingCombination = true; for (var i = 0; i < 5; i++) { if (currentCombination[i] !== elementList[i]) { hasMatchingCombination = false; break; } } if (hasMatchingCombination) { // Current combination matches the required meteorite notes - invoke meteorite and set mana to zero currentMana = 0; updateManaDisplay(); // Summon meteorite summonMeteorito(); //showCombinationMessage('¡METEORITO INVOCADO CON 5 ELEMENTOS!'); // Reset combination resetCombination(); temporaryNoteCounter = 0; enableNoteButtons(); return; } } // Disable buttons if temporary counter equals or exceeds available mana if (temporaryNoteCounter >= currentMana) { disableAllNoteButtons(); } // Only process immediately if we've reached max combination length if (currentCombination.length >= maxCombinationLength) { processCombination(); } } function spawnEnemy() { if (pausa == false && !enemy_spawn_cooldown) { var enemy = new Enemy(); enemy.x = enemyTower.x - 100; enemy.y = 2186; // Spawn on ground level enemy.onGround = true; // Start on ground game.addChild(enemy); enemies.push(enemy); enemy.maxHealth = vida_enemigo + Math.floor(Math.random() * (vida_enemigo * 0.5 - vida_enemigo * -0.5) + vida_enemigo * -0.5); enemy.health = enemy.maxHealth; enemy.damage = dano_enemigo + Math.floor(Math.random() * (dano_enemigo * 0.5 - dano_enemigo * -0.5) + dano_enemigo * -0.5); if (enemy.clase_enemigo == "a_distancia") { enemy.damage = enemy.damage / 2; } else if (enemy.clase_enemigo == "aero") { enemy.health = enemy.health / 2; enemy.maxHealth = enemy.health; } tem = spameo_enemigos + Math.floor(Math.random() * (spameo_enemigos * 0.5 - spameo_enemigos * -0.5) + spameo_enemigos * -0.5); // Mostrar en el texto de stats enemy.updateStatsDisplay(); // Increment enemy counter Unidades_invocadas++; // Check if we've reached 10 enemies if (Unidades_invocadas >= 10) { // Start 6-second cooldown enemy_spawn_cooldown = true; console.log("Espera Activado"); LK.setTimeout(function () { enemy_spawn_cooldown = false; console.log("Espera desactivado"); Unidades_invocadas = 0; // Reset counter after cooldown }, 12000); // 6 seconds } } effetc(); } function summonMeteorito() { var meteorito = new Meteorito(); meteorito.x = 0; // Start at x=0 meteorito.y = 0; // Start at y=0 game.addChild(meteorito); meteoritos.push(meteorito); // Add mana equal to apocalyptic letters (5-i) when meteorite is invoked var manaToAdd = 5 - i; currentMana = Math.min(currentMana + manaToAdd, maxMana); updateManaDisplay(); } function updateTowerHealthBars() { // Calculate max health: 10 + (10 * nivel) var maxHealth = 10 * nivel; var playerHealthPercent = Math.max(0, playerTowerHealth / maxHealth); var enemyHealthPercent = Math.max(0, enemyTowerHealth / maxHealth); playerTowerHealthBar.scaleX = playerHealthPercent; enemyTowerHealthBar.scaleX = enemyHealthPercent; // Update tower health text displays playerTowerHealthText.setText(playerTowerHealth + '/' + maxHealth); enemyTowerHealthText.setText(enemyTowerHealth + '/' + maxHealth); // Scale towers based on health - minimum scale of 0.3 (30%), maximum 1.0 (100%) var playerTowerScale = 0.3 + playerHealthPercent * 0.7; // 0.3 to 1.0 range var enemyTowerScale = 0.3 + enemyHealthPercent * 0.7; // 0.3 to 1.0 range // Apply scaling to towers tween.stop(playerTower, { scaleX: true, scaleY: true }); tween.stop(enemyTower, { scaleX: true, scaleY: true }); tween(playerTower, { scaleX: playerTowerScale, scaleY: playerTowerScale }, { duration: 500, easing: tween.easeInOut }); tween(enemyTower, { scaleX: enemyTowerScale, scaleY: enemyTowerScale }, { duration: 500, easing: tween.easeInOut }); } function subir_nivel() { // Increment level nivel++; // Check for level 32 - show final comic if (nivel === 32) { showFinalComic(); return; // Exit function to prevent further level progression } // Check for win condition at level 33 if (nivel >= 33) { LK.showYouWin(); // Player wins the game return; // Exit function to prevent further level progression } // Update nivel_llegado if current nivel is greater if (nivel > nivel_llegado) { nivel_llegado = nivel; } // Update level display levelText.setText('Nivel: ' + nivel); // Update unlocking system for new level updateLevelUnlocks(); // Update enemy tower asset if we've reached level 31 if (nivel === 31) { // Destroy current enemy tower enemyTower.destroy(); // Create new tower with torre_final asset enemyTower = game.addChild(LK.getAsset('torre_final', { anchorX: 0.5, anchorY: 1.0, x: 3590, y: 2559, width: 1200, height: 1728 })); } // Calculate new tower health: 10 + (10 * nivel) var newTowerHealth = 10 * nivel; // Reset enemy tower health and scale enemyTowerHealth = newTowerHealth; // Reset player tower health and scale playerTowerHealth = newTowerHealth; // Add 5 mana when level is completed currentMana = Math.min(currentMana + 5, maxMana); updateManaDisplay(); // Reset apocalyptic notes ocultar_notas_apocalitica(); // Destroy all enemies instantly for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.destroy(); enemies.splice(i, 1); } // Destroy all warriors instantly for (var i = warriors.length - 1; i >= 0; i--) { var warrior = warriors[i]; warrior.isBeingDestroyed = true; warrior.destroy(); warriors.splice(i, 1); } // Destroy all walls created by earth warriors for (var childIndex = game.children.length - 1; childIndex >= 0; childIndex--) { var child = game.children[childIndex]; if (child && child.isWall) { // Clean up health bar if it exists if (child.healthBar) { child.healthBar.destroy(); child.healthBar = null; } child.destroy(); } } // Reset tower scales to full size (1.0) tween.stop(playerTower, { scaleX: true, scaleY: true }); tween.stop(enemyTower, { scaleX: true, scaleY: true }); tween(playerTower, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1000, easing: tween.easeInOut }); tween(enemyTower, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1000, easing: tween.easeInOut }); // Update health bars and health text to reflect reset values updateTowerHealthBars(); // Reset enemy spawn counter and cooldown for new level Unidades_invocadas = 0; enemy_spawn_cooldown = false; // Show level advancement message if (idioma) { showCombinationMessage('¡Level ' + (nivel - 1) + ' Completed!'); } else { showCombinationMessage('¡Nivel ' + (nivel - 1) + ' Completado!'); } doblaje(); levelSound = LK.getSound('ganar'); levelSound.volume = soundValue; levelSound.play(); musiclevel(); actualizar_dificultad(); } function updateManaDisplay() { // Update text manaText.setText('Mana: ' + currentMana + '/' + maxMana); // Update orb visuals for (var i = 0; i < manaOrbs.length; i++) { if (i < currentMana) { manaOrbs[i].alpha = 1.0; // Full opacity for available mana manaOrbs[i].tint = 0x00FFFF; // Cyan color } else { manaOrbs[i].alpha = 0.3; // Low opacity for depleted mana manaOrbs[i].tint = 0x666666; // Gray color } } // Update note button states based on mana and current combination length var requiredMana = currentCombination.length + 1; // Mana needed to add one more note for (var i = 0; i < notes.length; i++) { if (currentMana < requiredMana) { notes[i].alpha = 0.5; // Grayed out when not enough mana notes[i].isDisabled = true; } else { notes[i].alpha = 1.0; // Normal when mana available notes[i].isDisabled = false; } } } function playRandomCombatSound() { var combatSounds = ['combatir_1', 'combatir_2', 'combatir_3', 'combatir_4']; var randomIndex = Math.floor(Math.random() * combatSounds.length); var sound = LK.getSound(combatSounds[randomIndex]); sound.volume = soundValue; sound.play(); } function sonidoEspadas() { var sound = LK.getSound('attack'); sound.volume = soundValue; sound.play(); } function playRandomProjectileSound(element) { var projectileSounds = []; if (element === 'water') { projectileSounds = ['sonido_proyectil_agua_1', 'sonido_proyectil_agua_2', 'sonido_proyectil_agua_3', 'sonido_proyectil_agua_4']; } else if (element === 'wind') { projectileSounds = ['sonido_proyectil_viento_1', 'sonido_proyectil_viento_2', 'sonido_proyectil_viento_3', 'sonido_proyectil_viento_4']; } else if (element === 'light') { projectileSounds = ['sonido_proyectil_energia_1', 'sonido_proyectil_energia_2', 'sonido_proyectil_energia_3', 'sonido_proyectil_energia_4']; } if (projectileSounds.length > 0) { var randomIndex = Math.floor(Math.random() * projectileSounds.length); var sound = LK.getSound(projectileSounds[randomIndex]); sound.volume = soundValue; sound.play(); } } function disableAllNoteButtons() { for (var i = 0; i < notes.length; i++) { notes[i].isDisabled = true; notes[i].alpha = 0.5; // Visual feedback } } function enableNoteButtons() { // Re-enable buttons only if we have enough mana updateManaDisplay(); } function addToCombination(element) { currentCombination.push(element); combinationTimer = combinationWindowTime; combinationCooldown = combinationResetTime; // Show current combination //showCombinationMessage('Combinación: [' + currentCombination.join(', ') + ']'); } function processCombination() { if (currentCombination.length === 0) { return; } // Store combination and timing intervals for melody playback before reset var combinationForMelody = currentCombination.slice(); // Create a copy var timingIntervalsForMelody = noteTimingIntervals.slice(); // Create a copy of intervals // Check if combination is valid if (isValidCombination(currentCombination)) { // Execute the combination executeCombination(currentCombination); // Play melody after successful summoning with preserved intervals playMelodySequenceWithIntervals(combinationForMelody, timingIntervalsForMelody); } else { // Show invalid combination message if (idioma) { showCombinationMessage('Non-existent combination'); } else { showCombinationMessage('Combinación no existente'); } } // Reset combination only (element list is managed by crea_combinacion_meteorito) // elementList is not reset here - only when meteorite is successfully invoked resetCombination(); // Re-enable buttons after 1 second (invocation phase) LK.setTimeout(function () { temporaryNoteCounter = 0; // Reset helper counter after invocation phase enableNoteButtons(); }, 1000); } function isElementComboUnlocked(element, length, nivel) { var unlockLevels = { 'fire': [1, 7, 12, 17, 22], 'water': [2, 8, 13, 18, 23], 'earth': [3, 9, 14, 19, 24], 'wind': [4, 10, 15, 20, 25], 'light': [5, 11, 16, 21, 26] }; if (!unlockLevels[element]) { return false; } if (length === 1) { return nivel >= unlockLevels[element][0]; } if (length >= 2 && length <= 5) { return nivel >= unlockLevels[element][length - 1]; } return false; } function isValidCombination(combination) { // Check if combination length is unlocked if (unlockedCombinations.indexOf(combination.length) === -1) { return false; } // Single element combinations are valid if (combination.length === 1) { var element = combination[0]; // For level 7+, allow all single element combinations since all note buttons are unlocked if (nivel >= 7) { return unlockedElements.indexOf(element) !== -1; } // For levels before 7, use unlockedCombinationElements return unlockedCombinationElements.indexOf(element) !== -1; } if (combination.length === 2) { var firstElement = combination[0]; // Asegúrate que ambos elementos sean iguales for (var i = 1; i < combination.length; i++) { if (combination[i] !== firstElement) { return false; } } return isElementComboUnlocked(firstElement, 2, nivel); } if (combination.length === 3) { var firstElement = combination[0]; for (var i = 1; i < combination.length; i++) { if (combination[i] !== firstElement) { return false; } } return isElementComboUnlocked(firstElement, 3, nivel); } if (combination.length === 4) { var firstElement = combination[0]; for (var i = 1; i < combination.length; i++) { if (combination[i] !== firstElement) { return false; } } return isElementComboUnlocked(firstElement, 4, nivel); } // Check for meteorite combination first (exactly fire, water, earth, wind, light sequence) // Check if all elements are the same var firstElement = combination[0]; for (var i = 0; i < combination.length; i++) { if (combination[i] !== firstElement) { return false; } } return isElementComboUnlocked(firstElement, 5, nivel); // Multiple element combinations (2-4 notes of same element) if (combination.length >= 2 && combination.length <= 4) { var firstElement = combination[0]; // Check if all elements are the same for (var i = 0; i < combination.length; i++) { if (combination[i] !== firstElement) { return false; } } // For level 12+, allow 2 notes of each element type if (nivel >= 12 && combination.length === 2) { // Check if this element is allowed for combinations return unlockedCombinationElements.indexOf(firstElement) !== -1; } // For combinations of 3+ notes, use standard validation if (combination.length >= 3) { // Check if this element is allowed for combinations return unlockedCombinationElements.indexOf(firstElement) !== -1; } // For level 11 and below with 2-note combinations, use standard validation return unlockedCombinationElements.indexOf(firstElement) !== -1; } return false; } function executeCombination(combination) { // Check if we have enough mana for this combination if (currentMana < combination.length) { if (idioma) { showCombinationMessage('Not enough mana'); } else { showCombinationMessage('No hay suficiente mana'); } return; } // Consume mana based on combination length currentMana -= combination.length; updateManaDisplay(); if (combination.length === 1) { // Single element summon var element = combination[0]; summonWarrior(element, 1); //showCombinationMessage('¡' + element.toUpperCase() + ' nivel 1 invocado!'); } else if (combination.length >= 2 && combination.length <= 4) { // Multiple element combination - create higher tier warrior (2-4 notes) var element = combination[0]; // All elements should be the same var level = combination.length; summonWarrior(element, level); //showCombinationMessage('¡' + element.toUpperCase() + ' nivel ' + level + ' invocado!'); } else if (combination.length === 5) { // Check if it's the meteorite sequence var requiredSequence = ['fire', 'water', 'earth', 'wind', 'light']; var isMeteorite = true; for (var i = 0; i < combination.length; i++) { if (combination[i] !== requiredSequence[i]) { isMeteorite = false; break; } } if (isMeteorite) { // Meteorite combination - summon destructive meteorite with mixed elements summonMeteorito(); //showCombinationMessage('¡METEORITO ELEMENTAL INVOCADO!'); } else { // Regular 5-note same element combination var element = combination[0]; summonWarrior(element, 5); //showCombinationMessage('¡' + element.toUpperCase() + ' nivel 5 invocado!'); } } } function summonWarrior(element, tier) { // Default tier to 1 if not specified if (tier === undefined) { tier = 1; } // Create elemental warrior based on the element and tier var warrior = new Warrior(element, tier); warrior.x = playerTower.x + 300; warrior.y = 2186; // Spawn on ground level warrior.onGround = true; // Start on ground // Add elemental-specific visual effects on spawn var warriorGraphics = warrior.children[0]; // Get the warrior graphics if (element === 'water') { // Water splash effect - blue particles var summonScale = warrior.tierScale * 1.5; tween(warriorGraphics, { tint: 0x0066ff, scaleX: summonScale, scaleY: summonScale }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: warrior.tierScale, scaleY: warrior.tierScale }, { duration: 200 }); } }); } else if (element === 'earth') { // Earth rumble effect - brown particles var originalY = warriorGraphics.y; tween(warriorGraphics, { tint: 0x8b4513, y: originalY - 50 }, { duration: 200, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, y: originalY }, { duration: 200 }); } }); } else if (element === 'fire') { // Fire burst effect - red/orange flames var summonScale = warrior.tierScale * 1.3; tween(warriorGraphics, { tint: 0xff4400, scaleX: summonScale, scaleY: summonScale, rotation: Math.PI * 0.1 }, { duration: 250, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: warrior.tierScale, scaleY: warrior.tierScale, rotation: 0 }, { duration: 150 }); } }); } else if (element === 'light') { // Lightning effect - bright yellow flash var summonScale = warrior.tierScale * 1.2; tween(warriorGraphics, { tint: 0xffff00, alpha: 1.5, scaleX: summonScale, scaleY: summonScale }, { duration: 150, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, alpha: 1.0, scaleX: warrior.tierScale, scaleY: warrior.tierScale }, { duration: 100 }); } }); } else if (element === 'wind') { // Wind swirl effect - green spiral var summonScale = warrior.tierScale * 1.1; tween(warriorGraphics, { tint: 0x44ff44, rotation: Math.PI * 2, scaleX: summonScale, scaleY: summonScale }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, rotation: 0, scaleX: warrior.tierScale, scaleY: warrior.tierScale }, { duration: 200 }); } }); } game.addChild(warrior); warriors.push(warrior); var sound = LK.getSound('summon'); sound.volume = soundValue; sound.play(); // Add jump effect to all units when any warrior is summoned // Make all warriors jump for (var i = 0; i < warriors.length; i++) { var jumpWarrior = warriors[i]; // Store current Y position if not already stored if (jumpWarrior.baseY === 0) { jumpWarrior.baseY = jumpWarrior.y; } // Apply jump tween - go up then back down tween(jumpWarrior, { y: jumpWarrior.baseY - 100 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(jumpWarrior, { y: jumpWarrior.baseY }, { duration: 200, easing: tween.easeIn }); } }); } // Make all enemies jump for (var i = 0; i < enemies.length; i++) { var jumpEnemy = enemies[i]; // Store current Y position if not already stored if (jumpEnemy.baseY === 0) { jumpEnemy.baseY = jumpEnemy.y; } // Apply jump tween - go up then back down tween(jumpEnemy, { y: jumpEnemy.baseY - 100 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(jumpEnemy, { y: jumpEnemy.baseY }, { duration: 200, easing: tween.easeIn }); } }); } effetc(); } function playMelodySequenceWithIntervals(combination, intervals) { // Wait 1 second before starting melody playback to differentiate from invocation LK.setTimeout(function () { // Play notes sequentially using cumulative timing var cumulativeDelay = 0; for (var i = 0; i < combination.length; i++) { var element = combination[i]; // First note plays immediately (0ms), subsequent notes use intervals var delay = i === 0 ? 0 : cumulativeDelay; // Use setTimeout with closure to preserve the element value (function (noteElement, currentDelay) { LK.setTimeout(function () { // Play the appropriate elemental note sound with controlled volume if (noteElement === 'fire') { var fireSound = LK.getSound('Nota_Fire2'); fireSound.volume = 0.1; fireSound.play(); } else if (noteElement === 'water') { var waterSound = LK.getSound('Nota_Water2'); waterSound.volume = soundValue; waterSound.play(); } else if (noteElement === 'earth') { var earthSound = LK.getSound('Nota_Earth2'); earthSound.volume = soundValue; earthSound.play(); } else if (noteElement === 'wind') { var windSound = LK.getSound('Nota_Wind2'); windSound.volume = soundValue; windSound.play(); } else if (noteElement === 'light') { var lightSound = LK.getSound('Nota_Light2'); lightSound.volume = soundValue; lightSound.play(); } else { var summonSound = LK.getSound('summon2'); summonSound.volume = soundValue; summonSound.play(); } }, currentDelay); })(element, delay); // Add interval for next note (skip first note since it's 0) if (i < intervals.length && i > 0) { cumulativeDelay += intervals[i]; } else if (i === 0 && intervals.length > 1) { // For first note, set cumulative delay to the second interval cumulativeDelay = intervals[1]; } } }, 500); // 0.5 second delay before melody playback } function playMelodySequence(combination) { // Wait 1 second before starting melody playback to differentiate from invocation LK.setTimeout(function () { // Play each note in the combination using recorded timing intervals var cumulativeDelay = 0; for (var i = 0; i < combination.length; i++) { var element = combination[i]; var delay = cumulativeDelay; // Use setTimeout with closure to preserve the element value (function (noteElement, currentDelay) { LK.setTimeout(function () { // Play the appropriate elemental note sound with controlled volume if (noteElement === 'fire') { var fireSound = LK.getSound('Nota_Fire2'); fireSound.volume = 0.1; fireSound.play(); } else if (noteElement === 'water') { var waterSound = LK.getSound('Nota_Water2'); waterSound.volume = soundValue; waterSound.play(); } else if (noteElement === 'earth') { var earthSound = LK.getSound('Nota_Earth2'); earthSound.volume = soundValue; earthSound.play(); } else if (noteElement === 'wind') { var windSound = LK.getSound('Nota_Wind2'); windSound.volume = soundValue; windSound.play(); } else if (noteElement === 'light') { var lightSound = LK.getSound('Nota_Light2'); lightSound.volume = soundValue; lightSound.play(); } else { var summonSound = LK.getSound('summon2'); summonSound.volume = soundValue; summonSound.play(); } }, currentDelay); })(element, delay); // Update cumulative delay for next note if (i < noteTimingIntervals.length) { cumulativeDelay += noteTimingIntervals[i]; } else if (i > 0) { // Fallback to 200ms if no interval recorded cumulativeDelay += 200; } } }, 1000); // 1 second delay before melody playback } function updateElementListDisplay() { /*var listDisplay = 'Lista de Elementos: [' + elementList.map(function (el) { return el.toUpperCase(); }).join(', ') + '] (' + elementList.length + '/' + maxElementList + ')'; elementListText.setText(listDisplay);*/ } function resetCombination() { currentCombination = []; combinationTimer = 0; combinationCooldown = 0; temporaryNoteCounter = 0; // Reset helper counter // Don't reset timing intervals here - they should persist until new combination starts // Only reset when a new note is pressed // Update display only when actually clearing intervals } function showCombinationMessage(message) { // Remove from game layer if it exists there if (combinationMessageText.parent === game) { game.removeChild(combinationMessageText); } // Add to GUI layer to ensure it's always on top if (combinationMessageText.parent !== LK.gui) { LK.gui.addChild(combinationMessageText); } combinationMessageText.setText(message); combinationMessageText.alpha = 1.0; // Fade out message after 2 seconds tween(combinationMessageText, { alpha: 0 }, { duration: 2000 }); } function changeGroundTexture(assetName) { // Change ground texture for all tiles for (var g = 0; g < groundTiles.length; g++) { var oldTile = groundTiles[g]; // Find the correct index to insert the new tile behind all existing ground tiles var insertIndex = 0; // Count existing ground tiles to place new tile behind them for (var childIndex = 0; childIndex < game.children.length; childIndex++) { var child = game.children[childIndex]; // Check if this child is a ground tile by comparing properties if (child.anchor && child.anchor.x === 0.0 && child.anchor.y === 1.0 && child.y === 2732) { insertIndex = childIndex + 1; // Place after this ground tile } } var newTile = game.addChildAt(LK.getAsset(assetName, { anchorX: 0.0, anchorY: 1.0, x: oldTile.x, y: oldTile.y, width: oldTile.width, height: oldTile.height }), insertIndex); // Add tween to smoothly transition between textures newTile.alpha = 0; tween(newTile, { alpha: 1 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { oldTile.destroy(); } }); groundTiles[g] = newTile; } } game.down = function (x, y, obj) { // Check if comic system is active and handle comic progression if (comicActive) { nextComic(); return; } // Handle initial touch in game layer directly // Store initial touch position for debug initialTouchX = x; initialTouchY = y; currentTouchX = x; currentTouchY = y; if (touchDebugText && typeof touchDebugText.setText === 'function') { touchDebugText.setText(/*'Touch Info: Initial(' + Math.round(x) + ',' + Math.round(y) + ') Current(' + Math.round(x) + ',' + Math.round(y) + ')'*/''); } if (obj.event && obj.event.button === 2) { // Right mouse button isDragging = true; lastMouseX = x; obj.event.preventDefault(); // Prevent context menu } }; leftButton.down = function (x, y, obj) { // Check if we have mana (orpmidios) or allow movement regardless if (currentMana > 0 || true) { // Allow movement always for better UX // Start continuous camera movement if (!leftButton.isMoving) { leftButton.isMoving = true; leftButton.moveInterval = LK.setInterval(function () { if (leftButton.isMoving) { cameraX -= 16; // Double movement speed cameraX = Math.max(0, Math.min(cameraX, maxCameraX)); game.x = -cameraX; } }, 16); // 60fps movement } // Optional: consume mana when moving (uncomment if desired) // currentMana = Math.max(0, currentMana - 1); // updateManaDisplay(); } // Visual feedback tween(leftButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, onFinish: function onFinish() { tween(leftButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100 }); } }); }; leftButton.up = function (x, y, obj) { // Stop continuous movement if (leftButton.isMoving) { leftButton.isMoving = false; if (leftButton.moveInterval) { LK.clearInterval(leftButton.moveInterval); leftButton.moveInterval = null; } } }; rightButton.down = function (x, y, obj) { // Check if we have mana (orpmidios) or allow movement regardless if (currentMana > 0 || true) { // Allow movement always for better UX // Start continuous camera movement if (!rightButton.isMoving) { rightButton.isMoving = true; rightButton.moveInterval = LK.setInterval(function () { if (rightButton.isMoving) { cameraX += 16; // Double movement speed cameraX = Math.max(0, Math.min(cameraX, maxCameraX)); game.x = -cameraX; } }, 16); // 60fps movement } // Optional: consume mana when moving (uncomment if desired) // currentMana = Math.max(0, currentMana - 1); // updateManaDisplay(); } // Visual feedback tween(rightButton, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100, onFinish: function onFinish() { tween(rightButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 100 }); } }); }; rightButton.up = function (x, y, obj) { // Stop continuous movement if (rightButton.isMoving) { rightButton.isMoving = false; if (rightButton.moveInterval) { LK.clearInterval(rightButton.moveInterval); rightButton.moveInterval = null; } } }; game.up = function (x, y, obj) { if (obj.event && obj.event.button === 2) { // Right mouse button isDragging = false; } }; game.move = function (x, y, obj) { if (isDragging) { var deltaX = lastMouseX - x; cameraX += deltaX; cameraX = Math.max(0, Math.min(cameraX, maxCameraX)); game.x = -cameraX; lastMouseX = x; } // Update current touch position for debug (game coordinates) currentTouchX = x; currentTouchY = y; // Update debug text with both initial and current positions if (touchDebugText && typeof touchDebugText.setText === 'function') { /*touchDebugText.setText('Game Move - Initial(' + Math.round(initialTouchX) + ',' + Math.round(initialTouchY) + ') Current(' + Math.round(currentTouchX) + ',' + Math.round(currentTouchY) + ')');*/ } }; // Add GUI move handler for touch tracking LK.gui.topLeft.move = function (x, y, obj) { // Update current touch position for debug (GUI coordinates) currentTouchX = x; currentTouchY = y; // Update debug text with both initial and current positions if (touchDebugText && typeof touchDebugText.setText === 'function') { touchDebugText.setText(/*'GUI Move - Initial(' + Math.round(initialTouchX) + ',' + Math.round(initialTouchY) + ') Current(' + Math.round(currentTouchX) + ',' + Math.round(currentTouchY) + ')'*/''); } }; // Cloud system for background decoration var clouds = []; var cloudSpawnTimer = 0; function createCloud() { var cloud = new Nube(); cloud.x = -300; // Start further off-screen left to account for larger clouds // Create clouds randomly between y=0 and y=2000 with depth layering cloud.y = Math.random() * 2000; // Random position between y=0 and y=2000 cloud.startFloating(); // Start floating animation after position is set // Determine if cloud is background (behind game) or foreground (in front of game) var isBackground = Math.random() < 0.6; // 60% chance to be background // Pre-calculate values to avoid repeated operations var additionalScale, targetAlpha, targetTint; if (isBackground) { // Background clouds - behind game elements, more transparent additionalScale = 0.5 + Math.random() * 0.6; // Apply additional 50-110% scaling targetAlpha = 0.3 + Math.random() * 0.4; // Slightly more transparent for background targetTint = 0xE0E0E0; // Slightly darker for background effect // Add background clouds before other game elements game.addChildAt(cloud, Math.min(2, game.children.length)); // Add at safe index } else { // Foreground clouds - in front of game elements, more visible additionalScale = 0.8 + Math.random() * 0.8; // Apply additional 80-160% scaling targetAlpha = 0.4 + Math.random() * 0.5; // Random opacity between 40% and 90% targetTint = 0xF8F8F8; // Brighter for foreground effect // Add foreground clouds on top of game elements game.addChild(cloud); // Add normally to be in front } // Apply calculated values in batch to reduce property access cloud.scaleX *= additionalScale; cloud.scaleY *= additionalScale; cloud.alpha = targetAlpha; cloud.children[0].tint = targetTint; // Vary speed based on position for depth effect if (cloud.y < 600) { // Higher clouds move slower (distance effect) cloud.baseSpeed *= 0.5; } else if (cloud.y > 1400) { // Lower clouds move faster (proximity effect) cloud.baseSpeed *= 1.5; } clouds.push(cloud); } function updateClouds() { // Note: Cloud movement is now handled by the Nube class update method // Spawn new clouds periodically with cloud count limit for performance cloudSpawnTimer++; var maxClouds = 16; // Reduced from potentially unlimited to 16 for better performance if (cloudSpawnTimer >= 300 + Math.random() * 600 && clouds.length < maxClouds) { // Every 5-15 seconds, but only if under cloud limit createCloud(); cloudSpawnTimer = 0; } // Clean up any null references in clouds array for optimization for (var i = clouds.length - 1; i >= 0; i--) { if (!clouds[i] || !clouds[i].parent) { clouds.splice(i, 1); } } } // Create initial clouds for (var i = 0; i < 24; i++) { // Doubled number for better coverage var cloud = new Nube(); cloud.x = Math.random() * 4000; // Random initial position across screen // Create clouds randomly between y=0 and y=1000 with depth layering cloud.y = Math.random() * 2000; // Random position between y=0 and y=2000 cloud.startFloating(); // Start floating animation after position is set // Determine if cloud is background (behind game) or foreground (in front of game) var isBackground = Math.random() < 0.6; // 60% chance to be background if (isBackground) { // Background clouds - behind game elements, more transparent // Scale already randomized in Nube constructor, apply additional uniform background scaling var additionalScale = 0.5 + Math.random() * 0.6; // Apply additional 50-110% scaling cloud.scaleX *= additionalScale; cloud.scaleY *= additionalScale; cloud.alpha = 0.4 + Math.random() * 0.5; // Random opacity between 40% and 90% cloud.children[0].tint = 0xE0E0E0; // Slightly darker for background effect // Add background clouds before other game elements game.addChildAt(cloud, 0); // Add at index 0 to be behind everything } else { // Foreground clouds - in front of game elements, more visible // Scale already randomized in Nube constructor, apply additional uniform foreground scaling var additionalScale = 0.8 + Math.random() * 0.8; // Apply additional 80-160% scaling cloud.scaleX *= additionalScale; cloud.scaleY *= additionalScale; cloud.alpha = 0.4 + Math.random() * 0.5; // Random opacity between 40% and 90% cloud.children[0].tint = 0xF8F8F8; // Brighter for foreground effect // Add foreground clouds on top of game elements game.addChild(cloud); // Add normally to be in front } // Vary speed based on position for depth effect if (cloud.y < 600) { // Higher clouds move slower (distance effect) cloud.baseSpeed = cloud.baseSpeed * 0.5; } else if (cloud.y > 1400) { // Lower clouds move faster (proximity effect) cloud.baseSpeed = cloud.baseSpeed * 1.5; } clouds.push(cloud); } // Function to simulate music beat detection based on game timing function getGameMusicLevel() { // Simulate music beats based on game ticks and music timing // Create a beat pattern that varies over time var beatPattern = Math.sin(LK.ticks * 0.1) * 0.3 + 0.5; // Base sine wave var strongBeat = Math.sin(LK.ticks * 0.05) * 0.4; // Slower variation for stronger beats var randomVariation = (Math.random() - 0.5) * 0.2; // Add some randomness // Combine patterns to create realistic music-like variation var musicLevel = beatPattern + strongBeat + randomVariation; // Ensure the value stays within realistic bounds (0.0 to 1.0) musicLevel = Math.max(0, Math.min(1, musicLevel)); return musicLevel; } // Function to initialize element list (lista meteorito) function initializeElementList() { elementList = ['fire', 'water', 'earth', 'wind', 'light']; updateElementListDisplay(); } // Function to create random meteorite combination function crea_combinacion_meteorito() { for (var i = 0; i < 5; i++) { // Generate random integer between 0.49 and 5.49, then make it integer var randomNumber = Math.floor(Math.random() * 5) + 1; // This gives us 1-5 var element; if (randomNumber === 1) { element = 'fire'; } else if (randomNumber === 2) { element = 'water'; } else if (randomNumber === 3) { element = 'earth'; } else if (randomNumber === 4) { element = 'wind'; } else if (randomNumber === 5) { element = 'light'; } elementList.push(element); } updateElementListDisplay(); } // Initialize element list with corresponding notes at game start crea_combinacion_meteorito(); // Start background music with initial volume from slider musiclevel(); // Timer for apocalyptic note revelation (every 10 seconds = 600 frames at 60fps) var revelacionTimer = 0; game.update = function () { // Check if level is 0 and comic system should activate (only at level 0) if (nivel === 0 && !comicActive) { activateComicSystem(); } // Deactivate comic system if nivel is not -1 and comic is active if (nivel !== -1 && comicActive) { comicActive = false; clearComicImages(); fondoNegro.alpha = 0; // Hide black background pausa = false; } // Hide/show language button and text based on comic state if (comicActive) { languageButton.visible = false; languageButtonText.visible = false; } else { languageButton.visible = true; languageButtonText.visible = true; } // Music beat detection and character synchronization var audioLevel = getGameMusicLevel(); // Calculate sensitivity difference for debug display var sensitivityDifference = Math.max(0, audioLevel - musicBeatThreshold); // Update audio debug text in real-time if (audioDebugText && typeof audioDebugText.setText === 'function') { /*audioDebugText.setText('Audio: ' + audioLevel.toFixed(3) + ' | Threshold: ' + musicBeatThreshold.toFixed(2) + ' | Diff: ' + sensitivityDifference.toFixed(3));*/ } // Update revelacion timer and call function every 10 seconds if (pausa == false) { if (nivel >= 6) { revelacionTimer++; if (revelacionTimer >= 600) { // 10 seconds at 60fps revelacion_nota_apocalitica(); revelacionTimer = 0; } } } // Enhanced beat detection with multiple triggers var beatDetected = false; // Primary beat detection - only when audio level reaches 1 if (audioLevel >= 1.0 && LK.ticks - lastBeatTime > beatCooldown) { beatDetected = true; } // Remove fallback timer-based beat detection to prevent constant particle spam if (beatDetected) { // Beat detected - activate effect on all characters lastBeatTime = LK.ticks; // Activate effect on warriors for (var i = 0; i < warriors.length; i++) { warriors[i].beatEffectTimer = 120; // 2 seconds of sine wave effect } // Activate effect on enemies only if they're not golden statues for (var i = 0; i < enemies.length; i++) { if (!enemies[i].enemigo_oro) { enemies[i].beatEffectTimer = 120; // 2 seconds of sine wave effect } } // Activate particle effects on towers instead of scaling // Create particles around player tower in cone shape for (var p = 0; p < 24; p++) { // Calculate cone angle - wider at the top, narrower at bottom var angle = (Math.random() - 0.5) * Math.PI * 0.5; // 90 degree cone spread var distance = Math.random() * 300 + 100; // Random distance from center var startX = playerTower.x + Math.sin(angle) * (distance * 0.3); // Narrow at bottom var endX = playerTower.x + Math.sin(angle) * distance; // Wide at top var playerTowerCurrentScale = playerTower.scaleX; // Get current tower scale var scaledOffsetY = (1728 - 50) * playerTowerCurrentScale; // Scale the Y offset var particle = game.addChild(LK.getAsset('particula_torre_jugador', { anchorX: 0.5, anchorY: 0.5, x: startX + 100, y: playerTower.y - scaledOffsetY, width: 300 + Math.random() * 200, height: 300 + Math.random() * 200, rotation: Math.PI })); particle.alpha = 0.8; // Animate particles upward to the sky in cone shape and fade out tween(particle, { y: playerTower.y - 2000 - Math.random() * 500, x: endX + 100, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 3000 + Math.random() * 500, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Create particles around enemy tower in cone shape for (var p = 0; p < 24; p++) { // Calculate cone angle - wider at the top, narrower at bottom var angle = (Math.random() - 0.5) * Math.PI * 0.5; // 90 degree cone spread var distance = Math.random() * 300 + 100; // Random distance from center var startX = enemyTower.x + Math.sin(angle) * (distance * 0.3); // Narrow at bottom var endX = enemyTower.x + Math.sin(angle) * distance; // Wide at top var enemyTowerCurrentScale = enemyTower.scaleX; // Get current tower scale var scaledOffsetY = (1728 - 50) * enemyTowerCurrentScale; // Scale the Y offset var particle = game.addChild(LK.getAsset('particula_torre_enemiga', { anchorX: 0.5, anchorY: 0.5, x: startX, y: enemyTower.y - scaledOffsetY, width: 300 + Math.random() * 200, height: 300 + Math.random() * 200, rotation: Math.PI })); particle.alpha = 0.8; // Animate particles upward to the sky in cone shape and fade out tween(particle, { y: enemyTower.y - 2000 - Math.random() * 500, x: endX, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 3000 + Math.random() * 500, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } // Mana regeneration - every 10 seconds (600 frames at 60fps) if (pausa == false) { manaRegenTimer++; if (manaRegenTimer >= 600) { if (currentMana < maxMana) { currentMana++; updateManaDisplay(); } manaRegenTimer = 0; } } // Spawn enemies periodically enemySpawnTimer++; if (enemySpawnTimer >= tem * 60) { // Every 8.3 seconds spawnEnemy(); enemySpawnTimer = 0; } // Clean up off-screen warriors for (var i = warriors.length - 1; i >= 0; i--) { var warrior = warriors[i]; if (warrior.x > 4000) { warrior.destroy(); warriors.splice(i, 1); } } // Clean up off-screen enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.x < -200) { enemy.destroy(); enemies.splice(i, 1); } } // Clean up off-screen spores for (var i = esporas.length - 1; i >= 0; i--) { var espora = esporas[i]; if (espora.x < -500 || espora.x > 4500 || espora.y > 3500) { espora.destroy(); esporas.splice(i, 1); } } // Update cloud system updateClouds(); // Update temporary counter info text /*tempCounterInfoText.setText('Notas presionadas: ' + temporaryNoteCounter);*/ // Check if any fire tier 5 warriors exist var hasTier5FireWarrior = false; for (var w = 0; w < warriors.length; w++) { var warrior = warriors[w]; if (warrior.element === 'fire' && warrior.tier === 5) { hasTier5FireWarrior = true; break; } } // Activate fire mode if tier 5 fire warrior exists and mode is not active if (hasTier5FireWarrior && !globalFireModeActive) { globalFireModeActive = true; // Activate red visual effects tween(fireOverlay, { alpha: 0.2 }, { duration: 500, easing: tween.easeInOut }); // Apply red tint to towers tween(playerTower, { tint: 0xff6666 }, { duration: 500, easing: tween.easeInOut }); tween(enemyTower, { tint: 0xff6666 }, { duration: 500, easing: tween.easeInOut }); // Change ground texture to fire ground changeGroundTexture('tileground'); // Apply red tint to clouds for (var c = 0; c < clouds.length; c++) { // Store original tint if not already stored if (!clouds[c].children[0].originalTint) { clouds[c].children[0].originalTint = clouds[c].children[0].tint; } tween(clouds[c].children[0], { tint: 0xff8888 }, { duration: 500, easing: tween.easeInOut }); } } // Deactivate fire mode if no tier 5 fire warriors exist and mode is active if (!hasTier5FireWarrior && globalFireModeActive) { globalFireModeActive = false; // Fade out red overlay and tints tween(fireOverlay, { alpha: 0 }, { duration: 1000, easing: tween.easeOut }); // Remove red tint from towers tween(playerTower, { tint: 0xffffff }, { duration: 1000, easing: tween.easeOut }); tween(enemyTower, { tint: 0xffffff }, { duration: 1000, easing: tween.easeOut }); // Change ground texture back to normal changeGroundTexture('ground'); // Remove red tint from clouds for (var c = 0; c < clouds.length; c++) { tween(clouds[c].children[0], { tint: clouds[c].children[0].originalTint || 0xF0F0F0 }, { duration: 1000, easing: tween.easeOut }); } } // Process global fire mode effects only if active if (globalFireModeActive) { // Apply fire damage to all enemies every second if (LK.ticks % 60 === 0) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.takeBurnDamage) { enemy.takeBurnDamage(0, 1000, 6); // No initial damage, 1 second burn for 6 damage } } } } // Update apocalypse timer tiempoDeApocalipsis++; if (tiempoDeApocalipsis >= apocalipsisThreshold) { // 5 seconds of no note presses - trigger apocalypse music musicaDelApocalipsis(); tiempoDeApocalipsis = 0; // Reset timer after triggering } // Update combination system timers if (combinationTimer > 0) { combinationTimer--; if (combinationTimer <= 0 && currentCombination.length > 0) { // Time window expired, process current combination processCombination(); } } if (combinationCooldown > 0) { combinationCooldown--; if (combinationCooldown <= 0 && currentCombination.length > 0) { resetCombination(); // Re-enable buttons after timeout enableNoteButtons(); } } }; // Variable i for counting revealed apocalyptic notes var i = 0; // Comic system variables var comicActive = false; var currentComicIndex = 0; var comicImages = []; var maxComics = 11; function revelacion_nota_apocalitica() { // Only reveal if i is less than 5 (cannot have more than i=5) if (i >= 5) { return; // Exit if we've already revealed all 5 notes } // Get the element from elementList based on current counter i if (i < elementList.length) { var element = elementList[i]; // Get the corresponding note image for this element var noteImageId = element + 'Note'; // Update the apocalyptic note casilla with the element's note image if (casillas[i]) { // Remove current asset and add new one casillas[i].destroy(); // Create new note asset with the element's image var newCasilla = LK.getAsset(noteImageId, { width: 60, height: 60, anchorX: 0.5, anchorY: 0.5 }); newCasilla.x = 409 + i * 70; // Same positioning as original newCasilla.y = 220; LK.gui.topLeft.addChild(newCasilla); casillas[i] = newCasilla; } } // Increment counter by 1 each time function is called i++; } function ocultar_notas_apocalitica() { // Reset counter i to 0 i = 0; // Hide all apocalyptic notes by restoring default lock image for (var j = 0; j < casillas.length; j++) { if (casillas[j]) { // Remove current note image casillas[j].destroy(); // Create new default lock image var newCasilla = LK.getAsset('nota_apocalitica', { width: 60, height: 60, anchorX: 0.5, anchorY: 0.5 }); newCasilla.x = 409 + j * 70; // Same positioning as original newCasilla.y = 220; LK.gui.topLeft.addChild(newCasilla); casillas[j] = newCasilla; } } } function convertir_enemigos_esmeralda(enemy) { // Store enemy attributes before destruction var enemyImageName = 'enemyUnit'; // Default fallback // Extract the actual enemy image asset name based on enemy class and randomIndex if (enemy.clase_enemigo === 'aero' && enemy.randomIndex !== undefined) { if (enemy.randomIndex < enemigos_aereos.length) { enemyImageName = enemigos_aereos[enemy.randomIndex][1]; } } else if (enemy.clase_enemigo === 'cuerpo_a_cuerpo' && enemy.randomIndex !== undefined) { if (enemy.randomIndex < enemigos_cuerpo.length) { enemyImageName = enemigos_cuerpo[enemy.randomIndex][1]; } } else if (enemy.clase_enemigo === 'a_distancia' && enemy.randomIndex !== undefined) { if (enemy.randomIndex < enemigos_distancia.length) { enemyImageName = enemigos_distancia[enemy.randomIndex][1]; } } var enemyDamage = enemy.damage; var enemyHealth = enemy.health; var enemyMaxHealth = enemy.maxHealth; var enemySpeed = enemy.speed; var enemyAttackCooldown = enemy.attackCooldown || 80; var enemyPosition = { x: enemy.x, y: enemy.y }; var isFlying = false; // Enemies are ground units var isRanged = false; // Enemies are melee units var attackRange = 250; // Melee range // Instantly destroy the enemy enemy.destroy(); var index = enemies.indexOf(enemy); if (index > -1) { enemies.splice(index, 1); } // Create a new warrior with enemy attributes using Warrior class structure var convertedWarrior = new Container(); var warriorGraphics = convertedWarrior.attachAsset(enemyImageName, { anchorX: 0.5, anchorY: 0.5, width: 280, height: 280 }); // Apply mirror effect (flip horizontally) warriorGraphics.scaleX = -1; // Set inherited attributes from enemy convertedWarrior.element = 'converted'; convertedWarrior.tier = 1; convertedWarrior.health = enemyHealth; convertedWarrior.maxHealth = enemyMaxHealth; convertedWarrior.damage = enemyDamage; convertedWarrior.speed = 2.2; // Use standard warrior movement speed instead of enemy speed convertedWarrior.attackCooldown = 0; convertedWarrior.baseCooldown = enemyAttackCooldown; convertedWarrior.target = null; convertedWarrior.isBeingDestroyed = false; convertedWarrior.activar_movimiento = true; // Enable movement convertedWarrior.aumento_velocidad = 0; convertedWarrior.originalSpeed = 2.2; // Store original warrior speed // Physics properties convertedWarrior.velocityY = 0; convertedWarrior.onGround = true; convertedWarrior.gravity = 0.5; convertedWarrior.baseY = enemyPosition.y; convertedWarrior.beatEffectTimer = 0; // Set combat properties based on enemy type convertedWarrior.isFlying = isFlying; convertedWarrior.isRanged = isRanged; convertedWarrior.canAttack = true; // Enable attack ability convertedWarrior.ataque_jugador = true; // Enable player attack convertedWarrior.movimiento_enemigo = true; // Enable enemy targeting movement if (isRanged) { convertedWarrior.projectiles = []; } // Position the converted warrior convertedWarrior.x = enemyPosition.x; convertedWarrior.y = enemyPosition.y; // Health bar var healthBar = LK.getAsset('warriorHealthBar', { width: 120, height: 16 }); healthBar.anchor.set(0.5, 0.5); healthBar.y = -100; convertedWarrior.addChild(healthBar); convertedWarrior.healthBar = healthBar; // Stats text var statsText = new Text2('', { size: 48, fill: 0xFFFFFF, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); statsText.anchor.set(0.5, 0.5); statsText.y = 120; convertedWarrior.addChild(statsText); convertedWarrior.statsText = statsText; // Update stats display function convertedWarrior.updateStatsDisplay = function () { if (pausa == false) { var range = isRanged ? 600 : 250; var totalSpeed = convertedWarrior.speed + (convertedWarrior.aumento_velocidad || 0); var statsString = convertedWarrior.health + '-' + convertedWarrior.damage; convertedWarrior.statsText.setText(statsString); } }; // Initialize stats display convertedWarrior.updateStatsDisplay(); // Update function - full warrior behavior like normal warriors convertedWarrior.update = function () { if (pausa == false) { // Set base Y position for beat effect if (convertedWarrior.baseY === 0) { convertedWarrior.baseY = 2186; // Ground level } // Apply beat effect if active if (convertedWarrior.beatEffectTimer > 0) { convertedWarrior.beatEffectTimer--; // Get current audio level and calculate rotation strength based on sensitivity difference var audioLevel = getGameMusicLevel(); var sensitivityDifference = Math.max(0, audioLevel - musicBeatThreshold); if (sensitivityDifference > 0) { var rotationAmount = Math.sin((120 - convertedWarrior.beatEffectTimer) * 0.3) * sensitivityDifference * 0.785; warriorGraphics.rotation = rotationAmount; } else { warriorGraphics.rotation = 0; // No rotation when no difference } } else { warriorGraphics.rotation = 0; // Reset rotation when effect ends // Apply gravity only to non-flying units if (!convertedWarrior.isFlying && !convertedWarrior.onGround) { convertedWarrior.velocityY += convertedWarrior.gravity; convertedWarrior.y += convertedWarrior.velocityY; } // Ground collision for non-flying units var groundY = 2186; if (!convertedWarrior.isFlying && convertedWarrior.y >= groundY) { convertedWarrior.y = groundY; convertedWarrior.baseY = groundY; // Update base position convertedWarrior.velocityY = 0; convertedWarrior.onGround = true; } else if (!convertedWarrior.isFlying) { convertedWarrior.onGround = false; } } // Move towards enemy tower only if movement is enabled if (!convertedWarrior.target && convertedWarrior.activar_movimiento) { var totalSpeed = convertedWarrior.speed + (convertedWarrior.aumento_velocidad || 0); convertedWarrior.x += totalSpeed; } // Attack logic if (convertedWarrior.attackCooldown > 0) { convertedWarrior.attackCooldown--; } // Check for enemies to attack and combat engagement var inCombat = false; var attackRange = 250; // Melee range for converted warriors for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Use half width for warrior and enemy colliders but keep full height var warriorColliderWidth = 280 * 0.5; // Half of converted warrior width var enemyColliderWidth = 280 * 0.5; // Half of enemy width var distance = Math.sqrt(Math.pow(convertedWarrior.x - enemy.x, 2) + Math.pow(convertedWarrior.y - enemy.y, 2)); if (distance < attackRange) { // Mark as in combat to stop movement inCombat = true; // Attack if cooldown is ready if (convertedWarrior.attackCooldown <= 0 && convertedWarrior.canAttack && convertedWarrior.ataque_jugador) { convertedWarrior.attack(enemy); // Attack animation - flash red and scale up tween(warriorGraphics, { tint: 0xff0000, scaleX: -1.4, scaleY: 1.4 }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: -1.0, scaleY: 1.0 }, { duration: 300 }); } }); } break; } } // Check if warrior is at the tower x position - stop movement to focus on tower attack var atTowerPosition = Math.abs(convertedWarrior.x - enemyTower.x) <= 50; // Set activar_movimiento based on combat state if (inCombat || atTowerPosition) { // Stop movement when in combat range or at tower convertedWarrior.activar_movimiento = false; } else { // Allow movement when not in combat convertedWarrior.activar_movimiento = true; convertedWarrior.movimiento_enemigo = true; } // Apply movement based on activar_movimiento flag if (!convertedWarrior.activar_movimiento || atTowerPosition) { convertedWarrior.speed = 0; } else { // Resume normal movement with proper warrior speed convertedWarrior.speed = 2.2; // Use standard warrior movement speed } // Check collision with enemy tower - use separate x and y collision detection var towerColliderRadius = 700; // Fixed collider size, not scaled with tower var warriorColliderWidth = 280 * 0.5; // Half of warrior width var warriorColliderHeight = 280; // Full warrior height var towerDistance = Math.sqrt(Math.pow(convertedWarrior.x - enemyTower.x, 2) + Math.pow(convertedWarrior.y - enemyTower.y, 2)); // Use traditional distance-based collision for converted warriors var collisionDetected = towerDistance < towerColliderRadius; if (collisionDetected && !convertedWarrior.isBeingDestroyed) { // Deal damage to tower first - always attack regardless of cooldown when touching tower convertedWarrior.attackTower(); // Mark as being destroyed to prevent multiple collision detections convertedWarrior.isBeingDestroyed = true; // Remove from warriors array immediately var index = warriors.indexOf(convertedWarrior); if (index > -1) { warriors.splice(index, 1); } // Warrior self-destructs when touching the tower - immediate destruction convertedWarrior.destroy(); return; // Exit update to prevent further execution after destruction } // Check if warrior is off-screen and destroy if (convertedWarrior.x < -500 || convertedWarrior.x > 4500 || convertedWarrior.y > 3500) { convertedWarrior.destroy(); var index = warriors.indexOf(convertedWarrior); if (index > -1) { warriors.splice(index, 1); } return; // Exit update to prevent further execution } // Update health bar var healthPercent = convertedWarrior.health / convertedWarrior.maxHealth; convertedWarrior.healthBar.scaleX = healthPercent; convertedWarrior.healthBar.tint = healthPercent > 0.5 ? 0x00ff00 : healthPercent > 0.25 ? 0xffff00 : 0xff0000; } }; // Attack function convertedWarrior.attack = function (target) { convertedWarrior.attackCooldown = convertedWarrior.baseCooldown; if (target === enemyTower) { convertedWarrior.attackTower(); } else { target.takeDamage(convertedWarrior.damage); } }; // Attack tower function convertedWarrior.attackTower = function () { convertedWarrior.attackCooldown = convertedWarrior.baseCooldown; enemyTowerHealth -= convertedWarrior.damage; // Play tower damage sound var towerSounds = ['sonido_torre_1', 'sonido_torre_2', 'sonido_torre_3', 'sonido_torre_4']; var randomIndex = Math.floor(Math.random() * towerSounds.length); var sound = LK.getSound(towerSounds[randomIndex]); sound.volume = soundValue; sound.play(); if (enemyTowerHealth <= 0) { subir_nivel(); } updateTowerHealthBars(); }; // Take damage function convertedWarrior.takeDamage = function (damage) { convertedWarrior.health -= damage; convertedWarrior.updateStatsDisplay(); if (convertedWarrior.health <= 0) { convertedWarrior.isBeingDestroyed = true; var index = warriors.indexOf(convertedWarrior); if (index > -1) { warriors.splice(index, 1); } convertedWarrior.destroy(); } }; // Add to game and warriors array game.addChild(convertedWarrior); warriors.push(convertedWarrior); // Visual conversion effect tween(warriorGraphics, { tint: 0x00ff00, scaleX: -1.2, scaleY: 1.2 }, { duration: 300, onFinish: function onFinish() { tween(warriorGraphics, { tint: 0xffffff, scaleX: -1.0, scaleY: 1.0 }, { duration: 200 }); } }); effetc(); } function volterar(enemy) { console.log("volterar:", enemy.x, enemy.y); var enemyGraphics = enemy.children[0]; // Get the enemy graphics if (enemyGraphics) { // Check current mirror state and toggle if (enemyGraphics.scaleX > 0) { // Currently not mirrored, make it mirrored console.log("Enemy was not mirrored, now making it mirrored"); enemyGraphics.scaleX = -Math.abs(enemyGraphics.scaleX); } else { // Currently mirrored, make it not mirrored console.log("Enemy was mirrored, now making it not mirrored"); enemyGraphics.scaleX = Math.abs(enemyGraphics.scaleX); } } } function activateComicSystem() { // Only activate if we're at level 0 if (nivel !== 0) { return; } // Set nivel to -1 for comic mode nivel = -1; levelText.setText('Nivel: ' + nivel); // Pause the game pausa = true; comicActive = true; // Make black background visible when comic is active fondoNegro.alpha = 1.0; // Hide update message text when comic starts updateMessageText.visible = false; currentComicIndex = 0; // Show first comic showCurrentComic(); } function showCurrentComic() { // Only show comics if comic is active (nivel -1) if (!comicActive) { return; } // Update comic counter display /*numberComic.setText('Comic: ' + (currentComicIndex + 1) + '/' + maxComics);*/ // Clear any existing comic images clearComicImages(); if (currentComicIndex < maxComics) { // Detener sonido anterior si existe if (currentComicSound) { currentComicSound.stop(); } if (currentSoundTimer) { LK.clearTimeout(currentSoundTimer); } // Play comic sound when showing each comic with looping var comicSoundName = 'comic' + (currentComicIndex + 1) + '_sound'; var comicSound = LK.getSound(comicSoundName); if (comicSound && typeof comicSound.volume !== 'undefined') { // Configurar bucle manual con repetición cada 3 segundos var _loopComicSound = function loopComicSound() { if (currentComicSound && comicActive && currentComicIndex < maxComics) { currentComicSound.volume = soundValue; currentComicSound.play(); currentSoundTimer = LK.setTimeout(_loopComicSound, 3000); // Repetir cada 3 segundos } }; // Iniciar el bucle después de 3 segundos comicSound.volume = soundValue; comicSound.play(); // Guardar referencia del sonido actual currentComicSound = comicSound; currentSoundTimer = LK.setTimeout(_loopComicSound, 3000); } // Create current comic image var comicAssetName = 'comic' + (currentComicIndex + 1); var comicImage = LK.getAsset(comicAssetName, { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 1450, height: 1932, alpha: 0 }); comicImage.isComicImage = true; // Add click handler to comic image comicImage.down = function (x, y, obj) { nextComic(); }; // Add to GUI layer (covers full screen) in front of everything LK.gui.addChild(comicImage); comicImages.push(comicImage); // Make comic visible with fade in effect using tween tween(comicImage, { alpha: 1.0 }, { duration: 500, easing: tween.easeInOut }); } } function nextComic() { currentComicIndex++; if (currentComicIndex >= maxComics) { // All comics shown, end comic system endComicSystem(); } else { // Show next comic showCurrentComic(); } } function clearComicImages() { // Detener sonido de comic actual cuando se limpian las imágenes if (currentComicSound) { currentComicSound.stop(); currentComicSound = null; } if (currentSoundTimer) { LK.clearTimeout(currentSoundTimer); currentSoundTimer = null; } // Remove all existing comic images for (var i = comicImages.length - 1; i >= 0; i--) { var comicImg = comicImages[i]; if (comicImg && comicImg.parent) { comicImg.destroy(); } comicImages.splice(i, 1); } } function endComicSystem() { // Detener cualquier sonido de comic al terminar el sistema if (currentComicSound) { currentComicSound.stop(); currentComicSound = null; } if (currentSoundTimer) { LK.clearTimeout(currentSoundTimer); currentSoundTimer = null; } // Clear comic images clearComicImages(); // Hide comic counter /*numberComic.setText('Comic: 0/11');*/ // Hide black background when comic ends fondoNegro.alpha = 0; // Show update message text when comic ends updateMessageText.visible = true; // Advance to level 1 nivel = 1; levelText.setText('Nivel: ' + nivel); // Update unlocking system BEFORE updating other systems updateLevelUnlocks(); // Reset comic system comicActive = false; currentComicIndex = 0; // Unpause the game pausa = false; // Calculate new tower health for level 1 var newTowerHealth = 10 * nivel; playerTowerHealth = newTowerHealth; enemyTowerHealth = newTowerHealth; updateTowerHealthBars(); doblaje(); musiclevel(); if (nivel > nivel_llegado) { nivel_llegado = nivel; } actualizar_dificultad(); } function resetGame() { // Reset comic system variables comicActive = false; currentComicIndex = 0; clearComicImages(); // Reset all game variables to initial state warriors = []; enemies = []; meteoritos = []; esporas = []; globalFireModeActive = false; fireModeTimer = 0; playerTowerHealth = 10 * nivel; // Reset to level-appropriate health enemyTowerHealth = 10 * nivel; // Reset to level-appropriate health enemySpawnTimer = 0; Unidades_invocadas = 0; // Reset enemy counter enemy_spawn_cooldown = false; // Reset spawn cooldown isDragging = false; lastMouseX = 0; cameraX = 0; currentMana = 5; maxMana = 5; manaRegenTimer = 0; miniola_colision = false; currentCombination = []; combinationTimer = 0; combinationCooldown = 0; temporaryNoteCounter = 0; tiempoDeApocalipsis = 0; elementList = []; noteTimingIntervals = []; lastNoteTime = 0; isFirstNote = true; initialTouchX = 0; initialTouchY = 0; currentTouchX = 0; currentTouchY = 0; musicBeatThreshold = 0; lastBeatTime = 0; clouds = []; cloudSpawnTimer = 0; revelacionTimer = 0; i = 0; // Destroy all existing game objects for (var w = warriors.length - 1; w >= 0; w--) { var warrior = warriors[w]; warrior.isBeingDestroyed = true; warrior.destroy(); warriors.splice(w, 1); } for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; enemy.destroy(); enemies.splice(e, 1); } for (var m = meteoritos.length - 1; m >= 0; m--) { var meteorito = meteoritos[m]; meteorito.destroy(); meteoritos.splice(m, 1); } for (var s = esporas.length - 1; s >= 0; s--) { var espora = esporas[s]; espora.destroy(); esporas.splice(s, 1); } for (var c = clouds.length - 1; c >= 0; c--) { var cloud = clouds[c]; cloud.destroy(); clouds.splice(c, 1); } // Reset towers to full health and scale updateTowerHealthBars(); tween.stop(playerTower, { scaleX: true, scaleY: true }); tween.stop(enemyTower, { scaleX: true, scaleY: true }); tween(playerTower, { scaleX: 1.0, scaleY: 1.0, tint: 0xffffff }, { duration: 1000, easing: tween.easeInOut }); tween(enemyTower, { scaleX: 1.0, scaleY: 1.0, tint: 0xffffff }, { duration: 1000, easing: tween.easeInOut }); // Reset fire overlay tween(fireOverlay, { alpha: 0 }, { duration: 500 }); // Reset ground texture changeGroundTexture('ground'); // Reset clouds tint for (var c = 0; c < clouds.length; c++) { if (clouds[c] && clouds[c].children[0]) { tween(clouds[c].children[0], { tint: clouds[c].children[0].originalTint || 0xF0F0F0 }, { duration: 500 }); } } // Reset camera position cameraX = 0; game.x = 0; // Reset apocalyptic notes ocultar_notas_apocalitica(); // Reset mana display updateManaDisplay(); // Create new meteorite combination crea_combinacion_meteorito(); // Re-enable note buttons enableNoteButtons(); // Pause game pausa = true; currentMana = maxMana; updateManaDisplay(); actualizar_dificultad(); } function musicaDelApocalipsis() { // Don't play apocalyptic music if game is paused or apocalyptic notes are not visible if (pausa || !isApocalypticNotesVisible) { return; } // Play the meteorite element list with 0.5s intervals between notes var combinationForMelody = elementList.slice(); // Create a copy of elementList // Create intervals array with 0.5s (500ms) between each note var intervalsForMelody = []; for (var i = 0; i < combinationForMelody.length; i++) { if (i === 0) { intervalsForMelody.push(0); // First note plays immediately } else { intervalsForMelody.push(500); // 0.5s = 500ms delay between notes } } // Play the melody sequence with the meteorite combination playMelodySequenceWithIntervals(combinationForMelody, intervalsForMelody); // Show apocalypse message //showCombinationMessage('¡MÚSICA DEL APOCALIPSIS - MELODÍA DEL METEORITO!'); } // Add reset button functionality to pause button with long press var pauseButtonLongPressTimer = 0; var pauseButtonPressed = false; pauseButton.down = function (x, y, obj) { pauseButtonPressed = true; pauseButtonLongPressTimer = 0; // Start checking for long press var longPressCheck = LK.setInterval(function () { if (pauseButtonPressed) { pauseButtonLongPressTimer += 100; // Check every 100ms if (pauseButtonLongPressTimer >= 2000) { // 2 seconds = long press // Long press detected - reset game pauseButtonPressed = false; LK.clearInterval(longPressCheck); resetGame(); return; } } else { LK.clearInterval(longPressCheck); } }, 100); // Play settings click sound LK.getSound('settings_click').stop(); // Stop any currently playing instance var pauseSound = LK.getSound('settings_click'); pauseSound.volume = soundValue * 0.7; // Apply current soundValue with 70% modifier pauseSound.play(); // Visual feedback with bounce effect tween(pauseButton, { scaleX: 0.85, scaleY: 0.85, alpha: 0.6, rotation: Math.PI * 0.1 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(pauseButton, { scaleX: 1.05, scaleY: 1.05, alpha: 0.9, rotation: 0 }, { duration: 150, easing: tween.bounceOut, onFinish: function onFinish() { tween(pauseButton, { scaleX: 1.0, scaleY: 1.0, alpha: 0.8 }, { duration: 100 }); } }); } }); }; pauseButton.up = function (x, y, obj) { if (pauseButtonPressed && pauseButtonLongPressTimer < 2000) { // Short press - just toggle pause pausa = !pausa; // Update button text and appearance based on pause state if (pausa) { pauseButtonText.setText('||'); // Pause symbol pauseButton.tint = 0xff4444; // Red tint when paused } else { pauseButtonText.setText('▶'); // Play symbol pauseButton.tint = 0xffffff; // Green tint when playing } } pauseButtonPressed = false; }; function musiclevel() { // Stop all music before starting new music LK.stopMusic(); if (nivel == -1) { if (idioma) { LK.playMusic('music_menu_en', { volume: musicValue }); } else { LK.playMusic('music_menu_es', { volume: musicValue }); } } else if (nivel == 0) { LK.playMusic('musicId', { volume: musicValue }); } else { // Select random music from available tracks var musicTracks = ['Music1', 'Music2', 'Music3', 'Music4', 'Music5', 'Music7', 'Music8']; var randomIndex = Math.floor(Math.random() * musicTracks.length); var selectedMusic = musicTracks[randomIndex]; LK.playMusic(selectedMusic, { volume: musicValue }); } } function updateLevelUnlocks() { // Reset unlocked systems unlockedElements = []; unlockedCombinationElements = []; unlockedCombinations = []; isApocalypticNotesVisible = false; // Set max mana based on level if (nivel <= 0) { maxMana = 1; } else if (nivel == 1) { maxMana = 1; unlockedElements = ['fire']; unlockedCombinationElements = ['fire']; unlockedCombinations = [1]; // Single combinations only } else if (nivel == 2) { maxMana = 2; unlockedElements = ['fire', 'water']; unlockedCombinationElements = ['fire', 'water']; unlockedCombinations = [1]; } else if (nivel == 3) { maxMana = 3; unlockedElements = ['fire', 'water', 'earth']; unlockedCombinationElements = ['fire', 'water', 'earth']; unlockedCombinations = [1]; } else if (nivel == 4) { maxMana = 4; unlockedElements = ['fire', 'water', 'earth', 'wind']; unlockedCombinationElements = ['fire', 'water', 'earth', 'wind']; unlockedCombinations = [1]; } else if (nivel == 5) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; unlockedCombinations = [1]; } else if (nivel == 6) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; unlockedCombinations = [1]; isApocalypticNotesVisible = true; } else if (nivel == 7) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire']; // Only fire combinations allowed unlockedCombinations = [1, 2]; // Single and double combinations isApocalypticNotesVisible = true; } else if (nivel == 8) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water']; // Fire and water combinations allowed unlockedCombinations = [1, 2]; // Single and double combinations isApocalypticNotesVisible = true; } else if (nivel == 9) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth']; // Fire, water, earth combinations allowed unlockedCombinations = [1, 2]; isApocalypticNotesVisible = true; } else if (nivel == 10) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind']; // Fire, water, earth, wind combinations allowed unlockedCombinations = [1, 2]; isApocalypticNotesVisible = true; } else if (nivel == 11) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; // All combinations allowed unlockedCombinations = [1, 2]; isApocalypticNotesVisible = true; } else if (nivel == 12) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3]; // Single, double, and triple combinations isApocalypticNotesVisible = true; } else if (nivel == 13) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3]; isApocalypticNotesVisible = true; } else if (nivel == 14) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3]; isApocalypticNotesVisible = true; } else if (nivel == 15) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3]; isApocalypticNotesVisible = true; } else if (nivel == 16) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3]; isApocalypticNotesVisible = true; } else if (nivel == 17) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4]; // Single through quadruple combinations isApocalypticNotesVisible = true; } else if (nivel == 18) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4]; isApocalypticNotesVisible = true; } else if (nivel == 19) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4]; isApocalypticNotesVisible = true; } else if (nivel == 20) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4]; isApocalypticNotesVisible = true; } else if (nivel == 21) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4]; isApocalypticNotesVisible = true; } else if (nivel == 22) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4, 5]; // All combinations including quintuple isApocalypticNotesVisible = true; } else if (nivel == 23) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4, 5]; isApocalypticNotesVisible = true; } else if (nivel == 24) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4, 5]; isApocalypticNotesVisible = true; } else if (nivel == 25) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4, 5]; isApocalypticNotesVisible = true; } else if (nivel >= 26) { maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; // All note buttons available unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; // All elements allowed for combinations unlockedCombinations = [1, 2, 3, 4, 5]; isApocalypticNotesVisible = true; } else { // Fallback for any other levels - everything unlocked maxMana = 5; unlockedElements = ['fire', 'water', 'earth', 'wind', 'light']; unlockedCombinationElements = ['fire', 'water', 'earth', 'wind', 'light']; unlockedCombinations = [1, 2, 3, 4, 5]; // All combinations including quintuple isApocalypticNotesVisible = true; } // Ensure current mana doesn't exceed new max currentMana = Math.min(currentMana, maxMana); // Update visual elements updateNoteVisibility(); updateApocalypticNotesVisibility(); updateManaDisplay(); } function updateNoteVisibility() { // Update note button functionality based on unlocked elements for (var i = 0; i < notes.length; i++) { var note = notes[i]; var element = note.element; if (unlockedElements.indexOf(element) === -1) { // Element not unlocked - disable but keep original graphics note.alpha = 0.5; note.isDisabled = true; // Create silver note overlay if it doesn't exist if (!note.silverNote) { var silverNote = LK.getAsset('nota_plateada', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); silverNote.x = note.x; silverNote.y = note.y; silverNote.alpha = 0.8; LK.gui.topLeft.addChild(silverNote); note.silverNote = silverNote; } } else { // Element unlocked - enable note and destroy silver note note.alpha = 1.0; note.isDisabled = false; // Destroy silver note overlay if it exists if (note.silverNote) { note.silverNote.destroy(); note.silverNote = null; } } } } function updateApocalypticNotesVisibility() { // Show/hide apocalyptic notes based on level for (var i = 0; i < casillas.length; i++) { if (casillas[i]) { casillas[i].visible = isApocalypticNotesVisible; } } } function destroy_mapa() { // Destroy all warriors for (var i = warriors.length - 1; i >= 0; i--) { var warrior = warriors[i]; warrior.isBeingDestroyed = true; warrior.destroy(); warriors.splice(i, 1); } // Destroy all enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.destroy(); enemies.splice(i, 1); } // Destroy all walls created by earth warriors for (var childIndex = game.children.length - 1; childIndex >= 0; childIndex--) { var child = game.children[childIndex]; if (child && child.isWall) { // Clean up health bar if it exists if (child.healthBar) { child.healthBar.destroy(); child.healthBar = null; } child.destroy(); } } // Set mana to maximum currentMana = maxMana; updateManaDisplay(); } function showFinalComic() { // Pause the game pausa = true; // Show black background fondoNegro.alpha = 1.0; // Create final comic image var finalComicImage = LK.getAsset('comic_final', { anchorX: 0.0, anchorY: 0.0, x: 0, y: 0, width: 2048, height: 2732, alpha: 0 }); // Add click handler to advance to level 33 and end game finalComicImage.down = function (x, y, obj) { // Advance to level 33 nivel = 33; // Hide comic and black background finalComicImage.destroy(); fondoNegro.alpha = 0; // Show you win LK.showYouWin(); }; // Add to GUI layer to cover full screen LK.gui.addChild(finalComicImage); // Fade in the comic image tween(finalComicImage, { alpha: 1.0 }, { duration: 500, easing: tween.easeInOut }); } function activar_menu() { // Make f_m image visible fmImage.visible = true; // Make jugar button visible jugarButton.visible = true; } ;
===================================================================
--- original.js
+++ change.js
@@ -4076,9 +4076,9 @@
// Array de datos de enemigos para 30 niveles
// Formato: [nivel, vida, daño, velocidad_spawn_en_segundos]
var enemigos = [[1, 5, 2, 8], [2, 7, 2, 6], [3, 10, 2, 8], [4, 4, 10, 8], [5, 10, 2, 4], [6, 10, 2, 3], [7, 20, 2, 8], [8, 40, 3, 8], [9, 8, 10, 8], [10, 10, 20, 8], [11, 10, 10, 3], [12, 80, 2, 8], [13, 90, 8, 16], [14, 60, 3, 4], [15, 40, 5, 3], [16, 60, 6, 8], [17, 80, 4, 4],
// Nota: nivel 17 agregado ya que falta en los datos
-[18, 120, 4, 6], [19, 100, 7, 4], [20, 100, 10, 4], [21, 80, 9, 3], [22, 400, 2, 16], [23, 100, 9, 4], [24, 100, 4, 3], [25, 800, 11, 16], [26, 200, 9, 4], [27, 200, 100, 8], [28, 200, 10, 16], [29, 200, 40, 4], [30, 200, 5, 6], [31, 1000, 15, 10]];
+[18, 120, 4, 6], [19, 100, 7, 4], [20, 100, 10, 4], [21, 80, 9, 3], [22, 400, 2, 16], [23, 100, 9, 4], [24, 100, 4, 3], [25, 800, 11, 16], [26, 200, 9, 4], [27, 200, 100, 8], [28, 200, 10, 16], [29, 200, 40, 4], [30, 200, 5, 6], [31, 1, 1, 10]];
// Array de probabilidades para las clases de enemigos
// Formato: [nivel, probabilidad_cuerpo_a_cuerpo, probabilidad_aero, probabilidad_a_distancia]
var probabilidades = [[1, 100, 0, 0], [2, 100, 0, 0], [3, 80, 0, 20], [4, 50, 50, 0], [5, 60, 25, 15], [6, 50, 30, 20], [7, 45, 30, 25], [8, 40, 30, 30], [9, 35, 32, 33], [10, 30, 60, 10], [11, 25, 37, 38], [12, 20, 40, 40], [13, 20, 40, 40], [14, 60, 10, 30], [15, 80, 5, 5], [16, 34, 33, 33], [17, 90, 3, 7], [18, 70, 5, 25], [19, 55, 44, 10], [20, 30, 40, 30], [21, 65, 5, 30], [22, 70, 15, 15], [23, 25, 5, 70], [24, 80, 5, 15], [25, 20, 70, 10], [26, 78, 20, 2], [27, 100, 0, 0], [28, 0, 0, 100], [29, 0, 100, 0], [30, 34, 33, 33], [31, 60, 10, 30]];
var enemigos_aereos = [[4, 'enemyUnit4'], [8, 'enemyUnit8'], [11, 'enemyUnit11'], [14, 'enemyUnit14'], [17, 'enemyUnit17'], [20, 'enemyUnit20'], [23, 'enemyUnit23'], [26, 'enemyUnit26'], [29, 'enemyUnit29']];
Generame un guerrero azteca con patrones, estilo pixelar, ademas sera un El lagarto azul de Gorgona humanoide. Va tener una armadura roja con efetos de llamitas pequeñas. In-Game asset. 2d. High contrast. No shadows
Genérame una Rana de dardo venenosa guerra azteca con eso patrones estilo pixelar, con una apariencia maligna. In-Game asset. 2d. High contrast. No shadows
geerame una esmeralda pixelar. In-Game asset. 2d. High contrast. No shadows
Generame una boton con dentro de forma de una nota musical con efectos de agua.. In-Game asset. 2d. High contrast. No shadows
Generame una boton con dentro de forma de una nota musical con efectos de fuego .. In-Game asset. 2d. High contrast. No shadows
Generame una boton con dentro de forma de una nota musical con efectos de energia. In-Game asset. 2d. High contrast. No shadows
Generame una boton con dentro de forma de una nota musical con efectos de viento. In-Game asset. 2d. High contrast. No shadows
Generame una boton con dentro de forma de una nota musical con efectos de tierra. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero voaldor Colibrí esmeralda del Chiribiquete estilo azteca con patrones, estilo pixelar. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero meduza cone fectos de agua, que cura como mago, estilo pixelar, ambeintado a lo azteca. In-Game asset. 2d. High contrast. No shadows
Generame un jaguar guerrero con efectos de energia estilo magico, pixelar, con ambientacion azteca. In-Game asset. 2d. High contrast. No shadows
Proyectil agua pixelar. In-Game asset. 2d. High contrast. No shadows
Luz oscura particulas, moradas. In-Game asset. 2d. High contrast. No shadows
Particula de luz. In-Game asset. 2d. High contrast. No shadows
Generame una piedras corrupta
Genérame una MONO TITÍ guerra azteca con eso patrones estilo pixelar, con una apariencia maligna.. In-Game asset. 2d. High contrast. No shadows
Genérame un Tucan guerra azteca con eso patrones estilo pixelar, con una apariencia maligna. In-Game asset. 2d. High contrast. No shadows
Un signo de más en verde. In-Game asset. 2d. High contrast. No shadows
Un proyectil de electricidad pixelar. In-Game asset. 2d. High contrast. No shadows
Generame una explosion de este proyectil de forma circular
Creame una nubes pixelar. In-Game asset. 2d. High contrast. No shadows
Una tuerca pixelar como boton. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, ademas sera un Tortuga de ciénaga colombiana humanoide. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, ademas sera una iguana humanoide, con efectos de fuego. In-Game asset. 2d. High contrast. No shadows
Agregale efectos de fuego pero en un fondo de alto contraste, mejor dicho solo pono mas rojo y llmas en la espada
Agregale lava y fuego a esta textrua
Creame una explosion de fuego pixelar. In-Game asset. 2d. High contrast. No shadows
agregale un poquito de ver y azul sin perder la identidad de l aimagen, solo cuadrar colores
Generame un guerrero azteca con patrones, estilo pixelar, además será una Pez loro, con efectos de AGUA. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será una Cangrejo violinista, con efectos de AGUA. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será una Delfin Rosado con efectos de AGUA. In-Game asset. 2d. High contrast. No shadows
Generame unm meteorito pixelar elemental con todos los elementos. In-Game asset. 2d. High contrast. No shadows
particulas rosadas, de poder. In-Game asset. 2d. High contrast. No shadows
Haz este candando con los 5 ewlementos, fuego tierra, agua, aire, energia
Creame un cielo pixelar hermoso, sin sol ni nubes, ni montañas, nia rboles. In-Game asset. 2d. High contrast. No shadows
Generame una montañas pixelar en fondo blanco. In-Game asset. 2d. High contrast. No shadows
Generame una montañas de selva pixelar en fondo blanco, cercanas. In-Game asset. 2d. High contrast. No shadows
Generame un muro pixelar de tierra isometrico con aptornes aztecas. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será un animal Tapir, con efectos de tierra, cargando un gran escudo o muro.. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será un animal Anaconda verde, con efectos de tierra, cargando un gran escudo o muro.. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será un animal manati con efectos de tierra, cargando un enrome muro. gigante. In-Game asset. 2d. High contrast. No shadows
uan flor pixelar para plantar, sin matera sola una hermosa flor. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será un Cóndor de los Andes, unidad voladora con alas, con efectos de viento. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será una Mariposa alas de vidrio (Greta oto), unidad voladora con alas, con efectos de viento. Tiene que ser un animal.. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será Murciélago frugívoro, unidad voladora con alas, con efectos de viento. Tiene que ser un animal.. In-Game asset. 2d. High contrast. No shadows
Generame un guerrero azteca con patrones, estilo pixelar, además será Tucán toco , unidad voladora con alas, con efectos de viento. Tiene que ser un animal.. In-Game asset. 2d. High contrast. No shadows
Gotas de agua pixelar. In-Game asset. 2d. High contrast. No shadows
Genrame un muro con mas detalle que se vea superior es decir un nivel mas fuerte.
Generame una particula de espora pixela rt. In-Game asset. 2d. High contrast. No shadows
z de sueño pixelar. In-Game asset. 2d. High contrast. No shadows
Generame una lanza pixelar. In-Game asset. 2d. High contrast. No shadows
Creame un tornado pixelar en un fondo azul, para elimianrlo despues. In-Game asset. 2d. High contrast. No shadows
Hazme el rayo de color amarillo
Creame un boton de este personaje extilo pixelar cuadrado
POnlo trizte el perosnaje y gris el boton
Jugar
Pon una casa
deja todo naranaja
Genérame una Jaguar guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. In-Game asset. 2d. High contrast. No shadows
Genérame una Oso de Anteojos guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. In-Game asset. 2d. High contrast. No shadows
Genérame una Pecari de collar guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. In-Game asset. 2d. High contrast. No shadows
Genérame una Puma guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Tayra guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Nutria Neotropical guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Caiman llanero guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Capibara guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Zorro Cangrejero guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Demonio de Tasmnia guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. POderes, super rojo y rabioso, como un ejfe final. Animal. No tiene arams solo una gran y poderosa mordida. In-Game asset. 2d. High contrast. No shadows
Genérame una olibrí Esmeralda Andina guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Zopilote Rey guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Paujil guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Halcon Murcielago guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Buho de anteojos guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Mariposa monarca guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Chicharra guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una paloma guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad aerea con alas. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Rana de Cristal guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con un arco o cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Iguana Verde guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con un arco o cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Boa de arcoiris guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con un arco o cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Ciempies gigante amazonico guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con un arco o cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Escorpion Colombiano guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con un arco o cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Araña guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con un arco o cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Pez leon guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Serpiente coral guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame una Rana de dardo venenosa guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad a distancia, con cerbatana. Animal. In-Game asset. 2d. High contrast. No shadows
Genérame un hipopotamo guerrero azteca con eso patrones, estilo pixelar, con una apariencia maligna, ojos rojos, rabioso. Unidad cuerpo a cuerpo. Animal.. In-Game asset. 2d. High contrast. No shadows
Flecha maligna, de color rojo, pixelar. In-Game asset. 2d. High contrast. No shadows
Luna llena blanca, pixelar. In-Game asset. 2d. High contrast. No shadows
sol pixelart. In-Game asset. 2d. High contrast. No shadows
Un cielo noche pixelar 16:04 con estrellas. In-Game asset. 2d. High contrast. No shadows
Hazme un boton pixelar azteca, que diga Fin. In-Game asset. 2d. High contrast. No shadows
Nota_Fire
Sound effect
tower_damage
Sound effect
combat
Sound effect
enemy_death
Sound effect
combatir_1
Sound effect
Music
Music
combatir_2
Sound effect
combatir_3
Sound effect
combatir_4
Sound effect
attack
Sound effect
Nota_Water
Sound effect
Nota_Earth
Sound effect
Nota_Wind
Sound effect
Nota_Light
Sound effect
sonido_torre_2
Sound effect
sonido_torre_3
Sound effect
sonido_torre_4
Sound effect
sonido_torre_1
Sound effect
sonido_proyectil_agua_1
Sound effect
sonido_proyectil_agua_2
Sound effect
sonido_proyectil_agua_3
Sound effect
sonido_proyectil_agua_4
Sound effect
sonido_proyectil_viento_1
Sound effect
sonido_proyectil_viento_2
Sound effect
sonido_proyectil_viento_3
Sound effect
sonido_proyectil_viento_4
Sound effect
sonido_proyectil_energia_1
Sound effect
sonido_proyectil_energia_2
Sound effect
sonido_proyectil_energia_3
Sound effect
sonido_proyectil_energia_4
Sound effect
settings_click
Sound effect
burn_sound
Sound effect
musicId
Music
Level_1_en
Sound effect
Level_1_es
Sound effect
Level_2_en
Sound effect
Level_3_en
Sound effect
Level_4_en
Sound effect
Level_5_en
Sound effect
Level_6_en
Sound effect
Level_7_en
Sound effect
Level_9_en
Sound effect
Level_10_en
Sound effect
Level_11_en
Sound effect
Level_12_en
Sound effect
Level_13_en
Sound effect
Level_14_en
Sound effect
Level_15_en
Sound effect
Level_16_en
Sound effect
Level_17_en
Sound effect
Level_18_en
Sound effect
Level_19_en
Sound effect
Level_20_en
Sound effect
Level_21_en
Sound effect
Level_22_en
Sound effect
Level_23_en
Sound effect
Level_24_en
Sound effect
Level_25_en
Sound effect
Level_26_en
Sound effect
Level_2_es
Sound effect
Level_3_es
Sound effect
Level_4_es
Sound effect
Level_5_es
Sound effect
Level_6_es
Sound effect
Level_7_es
Sound effect
Level_8_es
Sound effect
Level_9_es
Sound effect
Level_10_es
Sound effect
Level_11_es
Sound effect
Level_12_es
Sound effect
Level_13_es
Sound effect
Level_14_es
Sound effect
Level_15_es
Sound effect
Level_16_es
Sound effect
Level_17_es
Sound effect
Level_18_es
Sound effect
Level_19_es
Sound effect
Level_20_es
Sound effect
Level_21_es
Sound effect
Level_22_es
Sound effect
Level_23_es
Sound effect
Level_24_es
Sound effect
Level_25_es
Sound effect
Level_26_es
Sound effect
Level_8_en
Sound effect
ganar
Sound effect
comic1_sound
Sound effect
comic2_sound
Sound effect
comic3_sound
Sound effect
comic4_sound
Sound effect
comic5_sound
Sound effect
comic6_sound
Sound effect
comic7_sound
Sound effect
comic8_sound
Sound effect
comic9_sound
Sound effect
comic10_sound
Sound effect
comic11_sound
Sound effect
music_menu_en
Music
music_menu_es
Music
Music2
Music
Music3
Music
Music4
Music
Music5
Music
Music8
Music
Music7
Music
Nota_Earth2
Sound effect
Nota_Fire2
Sound effect
Nota_Light2
Sound effect
Nota_Water2
Sound effect
Nota_Wind2
Sound effect
explosion_meteorito
Sound effect
muerte_guerrero
Sound effect
MusicVictoria
Music