User prompt
el hielo debe ser capaz de ser colocado en cualquier lugar del mapa, y no tiene un efecto de explosion, al ser colocado el circulo se queda quieto, no tiene ninguna animación, y haz que la ballesta puede atacar a las torres pero para ello debe de estar tocando la barra marron del medio del mapa, y hazlo saber al cpu para que lo use tambien de forma ofensiva.
User prompt
creemos una nueva carta, será un hechizo, se llama "hielo", hará 50 de daño, y aparece de forma instantanea donde ha sido colocada, genera un circulo de 400 de radio donde todos los que lo hayan tocado se quedarán paralizado durante 5 segundos, el radio tiene un efecto azul. ah y sumale 100 de hp al montapuerco ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
quiero añadir una nueva carta, será una wincondition, se llamará "montapuercos", tiene 750 hp, y tiene una velocidad de 2.25, es terrestre, y solo ataca a estructuras y torres, hace 150 de daño por golpe, y tiene una velocidad de ataque de 120 ticks, cuesta 4 de elexir, tiene un alcance de 60 unidades (cuerpo a cuerpo) y su icono está un 100% debajo de la descarga. tiene su propio asset, creaselo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que la ballesta sea capaz de dañar a la torre enemiga desde su propio campo si tiene el rango de ataque necesario. aumentale el daño a la ballesta a 15 de daño por disparo.
Remix started
Copy Tower Defense Royale
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AreaDamageBullet = Container.expand(function (startX, startY, target, damage, isFromPlayer, areaRadius) { var self = Container.call(this); self.x = startX; self.y = startY; self.target = target; self.damage = damage; self.speed = 6; self.isFromPlayer = isFromPlayer; self.areaRadius = areaRadius || 100; // Default area damage radius var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); bulletGraphics.tint = isFromPlayer ? 0xFF9800 : 0xFF5722; // Orange tint for area damage bullets bulletGraphics.scaleX = 1.3; bulletGraphics.scaleY = 1.3; self.update = function () { if (!self.target || self.target.isDead) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 30) { // Apply direct damage to the target first self.target.takeDamage(self.damage); // Then area damage explosion self.explode(); self.destroy(); return; } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; }; self.explode = function () { // Visual explosion effect (removed screen flash to prevent bugs) LK.getSound('explosion').play(); // Create visual circle around the hit target var explosionCircle = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 13, scaleY: 13 }); explosionCircle.x = self.target.x; explosionCircle.y = self.target.y; explosionCircle.tint = 0xFF9800; explosionCircle.alpha = 0.6; game.addChild(explosionCircle); // Animate the circle to fade out tween(explosionCircle, { alpha: 0, scaleX: 15, scaleY: 15 }, { duration: 500, onFinish: function onFinish() { explosionCircle.destroy(); } }); // Get all possible targets for area damage var allTargets = []; if (self.isFromPlayer) { allTargets = allTargets.concat(enemyUnits, enemyTowers); } else { allTargets = allTargets.concat(playerUnits, playerTowers); } // Apply damage to all units within area radius (100 units as requested) for (var i = 0; i < allTargets.length; i++) { var targetUnit = allTargets[i]; if (!targetUnit.isDead && targetUnit !== self.target) { // Exclude the initial target from area damage var distance = Math.sqrt(Math.pow(self.target.x - targetUnit.x, 2) + Math.pow(self.target.y - targetUnit.y, 2)); if (distance <= 100) { targetUnit.takeDamage(140); // Flash each affected unit tween(targetUnit, { tint: 0xFF9800 }, { duration: 300, onFinish: function onFinish() { tween(targetUnit, { tint: 0xFFFFFF }, { duration: 200 }); } }); } } } }; return self; }); var Ballesta = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'ballesta'; self.maxHealth = 900; self.currentHealth = 900; self.isDead = false; self.damage = 15; self.range = 2500; self.attackSpeed = 18; // Every 0.3 seconds (60fps * 0.3 = 18 ticks) self.lastAttackTime = 0; self.target = null; self.isAttacking = false; // Spawn delay properties self.spawnDelay = 240; // 4 seconds (60 * 4 = 240 ticks) self.spawnTimer = self.spawnDelay; self.isSpawning = true; var ballestaGraphics = self.attachAsset('ballesta', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = 0.25; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Find closest enemy units first var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { // Ballesta cannot attack aerial units - only ground units if (targetUnits[i].isAerial) { continue; } var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } // Find closest enemy towers if no units in range or if towers are in range var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } self.target = closestTarget; }; self.attack = function () { if (!self.target || self.target.isDead) { return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.range && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; var bullet = new Bullet(self.x, self.y, self.target, self.damage, self.isPlayer); bullet.speed = 16; // 100% faster than default speed of 8 bullets.push(bullet); game.addChild(bullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't attack while stunned } // Lose 4% of total health per second (60 ticks = 1 second) if (LK.ticks % 15 === 0) { // Every 15 ticks = 4 times per second = 1% each time = 4% per second self.currentHealth -= self.maxHealth * 0.01; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar without flash effect for natural decay var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; } self.findTarget(); self.attack(); }; return self; }); var Bruja = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'bruja'; self.isAerial = false; // Ground unit self.maxHealth = 550; self.currentHealth = 550; self.damage = 130; self.attackSpeed = 90; // 90 ticks self.range = 500; // Detection range self.attackRange = 425; // Attack range self.speed = 1.25; self.target = null; self.lastAttackTime = 0; self.isDead = false; self.isAttacking = false; // Skeleton summoning properties self.skeletonSummonTimer = 180; // 3 seconds initial delay (60 ticks = 1 second) self.skeletonSummonInterval = 600; // 10 seconds interval (60 * 10 = 600 ticks) self.hasSummonedFirst = false; // Track if first summon has happened // Spawn delay properties self.spawnDelay = 60; // 1 second self.spawnTimer = self.spawnDelay; self.isSpawning = true; var brujaGraphics = self.attachAsset('bruja', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarScale = 0.2; var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = healthBarScale; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Find closest enemy units first var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { // Bruja can attack both air and ground units var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } // Find closest enemy towers if no units in range if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.attackRange) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.attackRange && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; // Create purple area damage bullet with 75 units radius var brujaAreaBullet = new BrujaAreaBullet(self.x, self.y, self.target, self.damage, self.isPlayer, 75); bullets.push(brujaAreaBullet); game.addChild(brujaAreaBullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.summonSkeletons = function () { // Generate 4 skeletons in formation: up, down, left, right at 75 units distance var skeletonPositions = [{ x: self.x, y: self.y - 75 }, // Up { x: self.x, y: self.y + 75 }, // Down { x: self.x - 75, y: self.y }, // Left { x: self.x + 75, y: self.y } // Right ]; for (var i = 0; i < skeletonPositions.length; i++) { var pos = skeletonPositions[i]; var skeleton = new Skeleton(self.isPlayer); skeleton.x = pos.x; skeleton.y = pos.y; // Remove spawn delay for Bruja-generated skeletons so they can move and attack immediately skeleton.spawnTimer = 0; skeleton.isSpawning = false; skeleton.spawnTimerText.setText(''); // Add skeleton to appropriate unit array if (self.isPlayer) { playerUnits.push(skeleton); } else { enemyUnits.push(skeleton); } game.addChild(skeleton); } // Play deploy sound for skeleton summoning LK.getSound('deploy').play(); }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't move or attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't move or attack while stunned } // Handle skeleton summoning self.skeletonSummonTimer--; if (!self.hasSummonedFirst && self.skeletonSummonTimer <= 0) { // First summon after 3 seconds self.summonSkeletons(); self.hasSummonedFirst = true; self.skeletonSummonTimer = self.skeletonSummonInterval; // Set to 10 second interval } else if (self.hasSummonedFirst && self.skeletonSummonTimer <= 0) { // Subsequent summons every 10 seconds self.summonSkeletons(); self.skeletonSummonTimer = self.skeletonSummonInterval; // Reset to 10 second interval } self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var BrujaAreaBullet = Container.expand(function (startX, startY, target, damage, isFromPlayer, areaRadius) { var self = Container.call(this); self.x = startX; self.y = startY; self.target = target; self.damage = damage; self.speed = 6; self.isFromPlayer = isFromPlayer; self.areaRadius = areaRadius || 75; // 75 units radius as requested var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); bulletGraphics.tint = 0x9C27B0; // Purple color for bruja bullets bulletGraphics.scaleX = 1.2; bulletGraphics.scaleY = 1.2; self.update = function () { if (!self.target || self.target.isDead) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 30) { // Apply direct damage to the target first self.target.takeDamage(self.damage); // Then area damage explosion self.explode(); self.destroy(); return; } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; }; self.explode = function () { // Play explosion sound LK.getSound('explosion').play(); // Create blue visual circle around the hit target with 75 units radius var explosionCircle = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 10, // Scale to make it 75 units radius (15 * 10 / 2 = 75) scaleY: 10 }); explosionCircle.x = self.target.x; explosionCircle.y = self.target.y; explosionCircle.tint = 0x0080FF; // Blue color as requested explosionCircle.alpha = 0.6; game.addChild(explosionCircle); // Animate the circle to fade out tween(explosionCircle, { alpha: 0, scaleX: 12, scaleY: 12 }, { duration: 500, onFinish: function onFinish() { explosionCircle.destroy(); } }); // Get all possible targets for area damage var allTargets = []; if (self.isFromPlayer) { allTargets = allTargets.concat(enemyUnits, enemyTowers); } else { allTargets = allTargets.concat(playerUnits, playerTowers); } // Apply damage to all units within area radius (75 units as requested) for (var i = 0; i < allTargets.length; i++) { var targetUnit = allTargets[i]; if (!targetUnit.isDead && targetUnit !== self.target) { // Exclude the initial target from area damage var distance = Math.sqrt(Math.pow(self.target.x - targetUnit.x, 2) + Math.pow(self.target.y - targetUnit.y, 2)); if (distance <= self.areaRadius) { targetUnit.takeDamage(self.damage); // Flash each affected unit with blue color tween(targetUnit, { tint: 0x0080FF }, { duration: 300, onFinish: function onFinish() { tween(targetUnit, { tint: 0xFFFFFF }, { duration: 200 }); } }); } } } }; return self; }); var Bullet = Container.expand(function (startX, startY, target, damage, isFromPlayer) { var self = Container.call(this); self.x = startX; self.y = startY; self.target = target; self.damage = damage; self.speed = 8; self.isFromPlayer = isFromPlayer; var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); bulletGraphics.tint = isFromPlayer ? 0x4CAF50 : 0xF44336; self.update = function () { if (!self.target || self.target.isDead) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.target.takeDamage(self.damage); self.destroy(); return; } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; }; return self; }); var Cannon = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'cannon'; self.maxHealth = 600; self.currentHealth = 600; self.isDead = false; self.damage = 90; self.range = 500; self.attackSpeed = 50; self.lastAttackTime = 0; self.target = null; self.isAttacking = false; // Spawn delay properties self.spawnDelay = 60; // 1 second self.spawnTimer = self.spawnDelay; self.isSpawning = true; var cannonGraphics = self.attachAsset('cannon', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = 0.25; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Find closest enemy units first var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { // Cannon cannot attack aerial units if (targetUnits[i].isAerial) { continue; } var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } self.target = closestTarget; }; self.attack = function () { if (!self.target || self.target.isDead) { return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.range && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; var bullet = new Bullet(self.x, self.y, self.target, self.damage, self.isPlayer); bullets.push(bullet); game.addChild(bullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't attack while stunned } // Lose 3% of total health per second (60 ticks = 1 second) if (LK.ticks % 20 === 0) { // Every 20 ticks = 3 times per second = 1% each time = 3% per second self.currentHealth -= self.maxHealth * 0.01; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar without flash effect for natural decay var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; } self.findTarget(); self.attack(); }; return self; }); var Card = Container.expand(function (cardType) { var self = Container.call(this); self.cardType = cardType; self.cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'montapuercos' ? 4 : 3; var cardBg = self.attachAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5 }); var cardIcon = LK.getAsset(cardType === 'discharge' ? 'descarga_descardina' : cardType === 'maquinaVoladora' ? 'flying_machine' : cardType === 'bruja' ? 'bruja' : cardType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); self.addChild(cardIcon); var costText = new Text2(self.cost.toString(), { size: 30, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.x = 120; costText.y = -40; self.addChild(costText); return self; }); var Discharge = Container.expand(function (targetX, targetY, isFromPlayer) { var self = Container.call(this); self.targetX = targetX; self.targetY = targetY; self.isFromPlayer = isFromPlayer; self.damage = 200; self.stunDuration = 60; // 1 second stun (60 ticks) // Discharge appears instantly at target location self.x = targetX; self.y = targetY; // Define explode method BEFORE calling it self.explode = function () { // Visual explosion effect - blue circle of 150 units radius var explosionCircle = LK.getAsset('dischargeExplosion', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, // Scale to make it 150 units radius (250 * 1.2 / 2 = 150) scaleY: 1.2 }); explosionCircle.x = self.targetX; explosionCircle.y = self.targetY; explosionCircle.tint = 0x0080FF; // Blue color explosionCircle.alpha = 0.9; game.addChild(explosionCircle); // Animate explosion tween(explosionCircle, { alpha: 0, scaleX: 1.8, scaleY: 1.8 }, { duration: 800, onFinish: function onFinish() { explosionCircle.destroy(); } }); // Play explosion sound LK.getSound('explosion').play(); // Get all possible targets for damage var allTargets = []; if (self.isFromPlayer) { allTargets = allTargets.concat(enemyUnits, enemyTowers); } else { allTargets = allTargets.concat(playerUnits, playerTowers); } // Apply damage and stun to all units within explosion radius of 150 units var explosionRadius = 150; // 150 units as requested for (var i = 0; i < allTargets.length; i++) { var targetUnit = allTargets[i]; if (!targetUnit.isDead) { var distance = Math.sqrt(Math.pow(self.targetX - targetUnit.x, 2) + Math.pow(self.targetY - targetUnit.y, 2)); if (distance <= explosionRadius) { // Apply 200 damage targetUnit.takeDamage(self.damage); // Apply stun effect (immobilize for 1 second) targetUnit.stunTimer = self.stunDuration; targetUnit.isStunned = true; // Visual effect for stunned units (blue tint) tween(targetUnit, { tint: 0x0080FF }, { duration: 1000, onFinish: function onFinish() { tween(targetUnit, { tint: 0xFFFFFF }, { duration: 200 }); } }); } } } // Destroy discharge immediately after explosion self.destroy(); }; // Create instant explosion effect - NOW we can call explode since it's defined self.explode(); return self; }); var Fireball = Container.expand(function (targetX, targetY, isFromPlayer) { var self = Container.call(this); self.targetX = targetX; self.targetY = targetY; self.isFromPlayer = isFromPlayer; self.speed = 13; self.damage = 415; self.explosionRadius = 150; // Start from king tower position if (isFromPlayer) { self.x = 1024; // Player king tower x self.y = 2600; // Player king tower y } else { self.x = 1024; // Enemy king tower x self.y = 200; // Enemy king tower y } var fireballGraphics = self.attachAsset('fireball', { anchorX: 0.5, anchorY: 0.5 }); fireballGraphics.tint = 0xFF6600; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 30) { // Explode at target location self.explode(); self.destroy(); return; } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; }; self.explode = function () { // Visual explosion effect var explosionCircle = LK.getAsset('fireballExplosion', { anchorX: 0.5, anchorY: 0.5 }); explosionCircle.x = self.targetX; explosionCircle.y = self.targetY; explosionCircle.tint = 0xFF3300; explosionCircle.alpha = 0.8; game.addChild(explosionCircle); // Animate explosion tween(explosionCircle, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, onFinish: function onFinish() { explosionCircle.destroy(); } }); // Play explosion sound LK.getSound('explosion').play(); // Get all possible targets for damage var allTargets = []; if (self.isFromPlayer) { allTargets = allTargets.concat(enemyUnits, enemyTowers); } else { allTargets = allTargets.concat(playerUnits, playerTowers); } // Apply damage and knockback to all units within explosion radius for (var i = 0; i < allTargets.length; i++) { var targetUnit = allTargets[i]; if (!targetUnit.isDead) { var distance = Math.sqrt(Math.pow(self.targetX - targetUnit.x, 2) + Math.pow(self.targetY - targetUnit.y, 2)); if (distance <= self.explosionRadius) { // Check if target is a tower var isTower = false; if (self.isFromPlayer) { isTower = enemyTowers.indexOf(targetUnit) !== -1; } else { isTower = playerTowers.indexOf(targetUnit) !== -1; } // Apply damage - 50% less to towers var damageToApply = isTower ? self.damage * 0.5 : self.damage; targetUnit.takeDamage(damageToApply); // Apply knockback only to non-tower units if (!isTower) { var knockbackDistance = 100; var knockbackDx = targetUnit.x - self.targetX; var knockbackDy = targetUnit.y - self.targetY; var knockbackLength = Math.sqrt(knockbackDx * knockbackDx + knockbackDy * knockbackDy); if (knockbackLength > 0) { var normalizedDx = knockbackDx / knockbackLength; var normalizedDy = knockbackDy / knockbackLength; var newX = targetUnit.x + normalizedDx * knockbackDistance; var newY = targetUnit.y + normalizedDy * knockbackDistance; // Animate knockback tween(targetUnit, { x: newX, y: newY }, { duration: 300, easing: tween.easeOut }); } } // Flash effect on hit units tween(targetUnit, { tint: 0xFF6600 }, { duration: 300, onFinish: function onFinish() { tween(targetUnit, { tint: 0xFFFFFF }, { duration: 200 }); } }); } } } }; return self; }); var MaquinaVoladora = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'maquinaVoladora'; self.isAerial = true; // Mark as aerial unit self.maxHealth = 500; self.currentHealth = 500; self.damage = 85; self.attackSpeed = 90; // 90 ticks self.range = 500; // Detection range self.attackRange = 475; // Attack range self.speed = 1.5; self.target = null; self.lastAttackTime = 0; self.isDead = false; self.isAttacking = false; // Spawn delay properties self.spawnDelay = 60; // 1 second self.spawnTimer = self.spawnDelay; self.isSpawning = true; var maquinaGraphics = self.attachAsset('flying_machine', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarScale = 0.2; var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = healthBarScale; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Find closest enemy units first var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } // Find closest enemy towers if no units in range if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.attackRange) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.attackRange && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; // Create purple bullet var bullet = new Bullet(self.x, self.y, self.target, self.damage, self.isPlayer); bullet.children[0].tint = 0x9C27B0; // Purple color bullets.push(bullet); game.addChild(bullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't move or attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't move or attack while stunned } self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var Megaesbirro = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'megaesbirro'; self.isAerial = true; // Mark as aerial unit self.maxHealth = 650; // 500 + 150 self.currentHealth = 650; self.damage = 135; // 85 + 50 self.attackSpeed = 90; // 90 ticks self.range = 500; // Detection range self.attackRange = 150; // Attack range self.speed = 1.75; self.target = null; self.lastAttackTime = 0; self.isDead = false; self.isAttacking = false; // Spawn delay properties self.spawnDelay = 60; // 1 second self.spawnTimer = self.spawnDelay; self.isSpawning = true; var megaesbirroGraphics = self.attachAsset('megaesbirro', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarScale = 0.2; var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = healthBarScale; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Find closest enemy units first var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } // Find closest enemy towers if no units in range if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.attackRange) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.attackRange && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; // Create purple bullet var bullet = new Bullet(self.x, self.y, self.target, self.damage, self.isPlayer); bullet.children[0].tint = 0x9C27B0; // Purple color bullets.push(bullet); game.addChild(bullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't move or attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't move or attack while stunned } self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var Minion = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'minion'; self.isAerial = true; // Mark as aerial unit self.maxHealth = 400; self.currentHealth = 400; self.damage = 85; self.attackSpeed = 90; // 90 ticks self.range = 500; // Detection range self.attackRange = 150; // Attack range self.speed = 1.75; self.target = null; self.lastAttackTime = 0; self.isDead = false; self.isAttacking = false; // Spawn delay properties self.spawnDelay = 60; // 1 second self.spawnTimer = self.spawnDelay; self.isSpawning = true; var minionGraphics = self.attachAsset('minion', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarScale = 0.2; var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = healthBarScale; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Find closest enemy units first var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } // Find closest enemy towers if no units in range if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.attackRange) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.attackRange && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; // Create purple bullet var bullet = new Bullet(self.x, self.y, self.target, self.damage, self.isPlayer); bullet.children[0].tint = 0x9C27B0; // Purple color bullets.push(bullet); game.addChild(bullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't move or attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't move or attack while stunned } self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var Montapuercos = Container.expand(function (isPlayer) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = 'montapuercos'; self.isAerial = false; // Ground unit self.maxHealth = 750; self.currentHealth = 750; self.damage = 150; self.attackSpeed = 120; // 120 ticks self.range = 60; // Melee range self.speed = 2.25; self.target = null; self.lastAttackTime = 0; self.isDead = false; self.isAttacking = false; // Spawn delay properties self.spawnDelay = 60; // 1 second self.spawnTimer = self.spawnDelay; self.isSpawning = true; var montapuercosGraphics = self.attachAsset('montapuercos', { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarScale = 0.25; var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = healthBarScale; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; } var closestDistance = Infinity; var closestTarget = null; // Montapuercos only attacks structures and towers (wincondition behavior) // First check for structures (cannons, ballesta) within detection range var targetStructures = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetStructures.length; i++) { if (!targetStructures[i].isDead && (targetStructures[i].unitType === 'cannon' || targetStructures[i].unitType === 'ballesta')) { var distance = Math.sqrt(Math.pow(self.x - targetStructures[i].x, 2) + Math.pow(self.y - targetStructures[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetStructures[i]; } } } // If no structures found, target closest tower if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > self.range) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); if (distance <= self.range && LK.ticks - self.lastAttackTime > self.attackSpeed) { self.isAttacking = true; // Melee attack - direct damage self.target.takeDamage(self.damage); // Visual effect for melee strike LK.effects.flashObject(self, 0xFFFFFF, 200); // Flash target red when hit tween(self.target, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(self.target, { tint: 0xFFFFFF }, { duration: 100 }); } }); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't move or attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't move or attack while stunned } self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var Tower = Container.expand(function (isPlayer, isKing) { var self = Container.call(this); self.isPlayer = isPlayer; self.isKing = isKing; self.maxHealth = isKing ? 3700 : 2400; self.currentHealth = self.maxHealth; self.isDead = false; self.range = isKing ? 800 : 900; self.damage = 75; self.attackSpeed = 67; // 50% slower than 45 ticks (45 * 1.5 = 67.5, rounded to 67) self.lastAttackTime = 0; var towerGraphics = self.attachAsset(isKing ? 'kingTower' : 'tower', { anchorX: 0.5, anchorY: 1 }); towerGraphics.tint = self.isPlayer ? 0x4CAF50 : 0xF44336; // Health display var healthText = new Text2(self.currentHealth.toString(), { size: 40, fill: 0xFFFFFF }); healthText.anchor.set(0.5, 0.5); healthText.y = -75; self.addChild(healthText); self.healthText = healthText; self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; towersDestroyed++; LK.effects.flashObject(self, 0xFF0000, 1000); LK.getSound('towerDestroy').play(); if (self.isPlayer) { enemyTowersDestroyed++; } else { playerTowersDestroyed++; } } self.healthText.setText(self.currentHealth.toString()); LK.effects.flashObject(self, 0xFF0000, 300); }; self.attack = function () { if (self.isDead) { return; } // King towers cannot attack until at least one Princess tower is destroyed if (self.isKing) { var teamTowers = self.isPlayer ? playerTowers : enemyTowers; var princessTowersAlive = 0; for (var i = 0; i < teamTowers.length; i++) { if (!teamTowers[i].isKing && !teamTowers[i].isDead) { princessTowersAlive++; } } if (princessTowersAlive === 2) { return; // King tower cannot attack while both Princess towers are alive } } var targetUnits = self.isPlayer ? enemyUnits : playerUnits; var closestTarget = null; var closestDistance = Infinity; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } if (closestTarget && LK.ticks - self.lastAttackTime > self.attackSpeed) { var bullet = new Bullet(self.x, self.y - 50, closestTarget, self.damage, self.isPlayer); bullets.push(bullet); game.addChild(bullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { self.attack(); }; return self; }); var Unit = Container.expand(function (isPlayer, unitType, health, damage, attackSpeed, range, speed) { var self = Container.call(this); self.isPlayer = isPlayer; self.unitType = unitType; self.maxHealth = health; self.currentHealth = health; self.damage = damage; self.attackSpeed = attackSpeed; self.range = range; self.speed = speed; self.target = null; self.lastAttackTime = 0; self.isDead = false; self.isAttacking = false; // Track if unit has started attacking current target // Spawn delay properties self.spawnDelay = unitType === 'giant' ? 180 : 60; // 3 seconds for giant, 1 second for others (60 ticks = 1 second) self.spawnTimer = self.spawnDelay; self.isSpawning = true; var unitGraphics = self.attachAsset(unitType, { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarScale = unitType === 'giant' ? 0.4 : 0.2; var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Health bar var healthBar = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: healthBarScale, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; self.healthBarMaxScale = healthBarScale; // Spawn timer text self.spawnTimerText = new Text2('', { size: 30, fill: 0xFFFFFF }); self.spawnTimerText.anchor.set(0.5, 0.5); self.spawnTimerText.y = 30; self.addChild(self.spawnTimerText); self.takeDamage = function (damage) { self.currentHealth -= damage; if (self.currentHealth <= 0) { self.currentHealth = 0; self.isDead = true; } // Update health bar var healthPercent = self.currentHealth / self.maxHealth; self.healthBar.scaleX = self.healthBarMaxScale * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { // Reset target if current target is dead or invalid if (self.target && self.target.isDead) { self.target = null; self.isAttacking = false; // Reset attacking state when target dies } // If already attacking a target, don't change targets if (self.isAttacking && self.target && !self.target.isDead) { return; // Keep current target while attacking } var closestDistance = Infinity; var closestTarget = null; // Giant (wincondition unit) targets structures within detection range, then towers if (self.unitType === 'giant') { // First check for structures (cannons) within detection range (538) var targetStructures = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetStructures.length; i++) { if (!targetStructures[i].isDead && targetStructures[i].unitType === 'cannon') { var distance = Math.sqrt(Math.pow(self.x - targetStructures[i].x, 2) + Math.pow(self.y - targetStructures[i].y, 2)); if (distance <= 538 && distance < closestDistance) { closestDistance = distance; closestTarget = targetStructures[i]; } } } // If no structures in detection range, target closest tower if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } } else { // Normal units: First priority - Find closest enemy units and structures (they are more immediate threats) var targetUnits = self.isPlayer ? enemyUnits : playerUnits; for (var i = 0; i < targetUnits.length; i++) { if (!targetUnits[i].isDead) { // Skip aerial units if this unit cannot attack air (archers, wizards, and towers can attack air) if (targetUnits[i].isAerial && self.unitType !== 'archer' && self.unitType !== 'wizard') { continue; } var distance = Math.sqrt(Math.pow(self.x - targetUnits[i].x, 2) + Math.pow(self.y - targetUnits[i].y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestTarget = targetUnits[i]; } } } // Second priority: Find closest enemy towers if no units in range if (!closestTarget) { var targetTowers = self.isPlayer ? enemyTowers : playerTowers; for (var i = 0; i < targetTowers.length; i++) { if (!targetTowers[i].isDead) { var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2)); if (distance < closestDistance) { closestDistance = distance; closestTarget = targetTowers[i]; } } } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Use proper attack range for movement positioning var attackRange = self.attackRange || (self.unitType === 'knight' ? 168 : self.unitType === 'giant' ? 60 : self.range); if (distance > attackRange) { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); // Use different ranges for detection vs attack var attackRange = self.attackRange || (self.unitType === 'knight' ? 168 : self.unitType === 'giant' ? 60 : self.range); // Use attackRange property if defined, otherwise use unit-specific ranges if (distance <= attackRange && LK.ticks - self.lastAttackTime > self.attackSpeed) { // Set attacking flag when first attack begins self.isAttacking = true; // Knight, Giant, and Skeleton use melee attack - direct damage without bullet if (self.unitType === 'knight' || self.unitType === 'giant' || self.unitType === 'skeleton') { self.target.takeDamage(self.damage); // Visual effect for melee strike LK.effects.flashObject(self, 0xFFFFFF, 200); // Flash target red when hit by Giant if (self.unitType === 'giant') { tween(self.target, { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(self.target, { tint: 0xFFFFFF }, { duration: 100 }); } }); } // Knight and Skeleton no longer apply knockback to units } else { // Other units (like archers, wizard) still use bullets var bullet = new Bullet(self.x, self.y, self.target, self.damage, self.isPlayer); bullets.push(bullet); game.addChild(bullet); } self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; self.update = function () { if (self.isDead) { return; } // Handle spawn delay if (self.isSpawning) { self.spawnTimer--; if (self.spawnTimer <= 0) { self.isSpawning = false; self.spawnTimerText.setText(''); } else { // Show remaining time in seconds var secondsLeft = Math.ceil(self.spawnTimer / 60); self.spawnTimerText.setText(secondsLeft.toString()); } return; // Don't move or attack while spawning } // Handle stun effect if (self.stunTimer > 0) { self.stunTimer--; if (self.stunTimer <= 0) { self.isStunned = false; } return; // Don't move or attack while stunned } // Always find targets every frame to ensure units never ignore enemies self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var Wizard = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'wizard', 550, 140, 100, 530, 1.25); // Override the attack function to use area damage self.attack = function () { if (!self.target || self.target.isDead) { return; } // Verify target is from opposing team before attacking var isValidTarget = false; if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) { isValidTarget = true; } if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) { isValidTarget = true; } if (!isValidTarget) { self.target = null; return; } var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2)); // Wizard uses ranged attack with area damage projectile (use detection range for attacking) if (distance <= self.range && LK.ticks - self.lastAttackTime > self.attackSpeed) { // Set attacking flag when first attack begins self.isAttacking = true; // Create area damage bullet with 100 unit area radius var areaBullet = new AreaDamageBullet(self.x, self.y, self.target, self.damage, self.isPlayer, 100); bullets.push(areaBullet); game.addChild(areaBullet); self.lastAttackTime = LK.ticks; LK.getSound('attack').play(); } }; return self; }); var Skeleton = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'skeleton', 25, 45, 60, 500, 2); // Skeleton: 25 HP, 45 damage, 60 tick attack speed, 500 detection range, speed 2 // Attacks ground units and structures only (same targeting as other units) // Override attack range to be 150 instead of detection range self.attackRange = 150; return self; }); var Knight = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'knight', 825, 160, 60, 538, 1.75); // Detection range set to 538 (same as archer for consistent detection) // Attack range remains at melee distance in attack function return self; }); var Giant = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'giant', 4000, 175, 90, 538, 0.85); // Giant is a wincondition unit that only attacks structures and towers // Detection range set to 538 (same as archer), attack range remains at 60 in attack function return self; }); var Archer = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'archer', 400, 65, 90, 525, 1.5); // Range reduced and stats adjusted return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ /* CARD AND TOWER STATS: TOWERS: - Princess Towers: 2400 HP, 75 damage, attacks every 1.1s (67 ticks), range 800 units - King Tower: 3700 HP, 75 damage, attacks every 1.1s (67 ticks), range 800 units * King tower cannot attack until at least one Princess tower is destroyed CARDS: - Archers: 3 elixir cost, 425 HP each, 75 damage, attacks every 1.5s (90 ticks), range 567 units, speed 1.5 * Deploys 2 archers, can attack air and ground targets - Knight: 3 elixir cost, 825 HP, 160 damage, attacks every 1s (60 ticks), range 168 units, speed 2 * Single melee unit, ground targets only - Giant: 5 elixir cost, 4000 HP, 175 damage, attacks every 1.5s (90 ticks), range 300 units, speed 1 * Wincondition unit that only attacks structures and towers, ground unit UNIT SPEEDS: - Knight: speed 2 (medium) - Archer: speed 1.5 (medium-slow) - Giant: speed 1 (slow) ELIXIR SYSTEM: - Maximum: 10 elixir - Regeneration: 1 elixir every 2.0 seconds (120 ticks at 60fps) - Starting amount: 5 elixir GAME RULES: - Match duration: 2 minutes (120 seconds) - Victory: Most towers destroyed wins - Tiebreaker: Remaining tower health - Draw: Equal towers destroyed and equal remaining health */ // Game state variables var gameStarted = false; var gameTime = 250; // 250 seconds total duration var elixir = 5; // Starting elixir var maxElixir = 10; var elixirRegenRate = 114; // Ticks for 1.9 seconds at 60fps (1.9 * 60 = 114) var doubleElixirRegenRate = 57; // Half the time for double speed (1.9 / 2 = 0.95 seconds = 57 ticks) var lastElixirRegen = 0; var aiElixir = 5; // AI starting elixir var aiMaxElixir = 10; var aiElixirRegenRate = 120; // Ticks for 2 seconds at 60fps var doubleAiElixirRegenRate = 60; // Half the time for double speed (2 / 2 = 1 second = 60 ticks) var lastAiElixirRegen = 0; var doubleElixirActive = false; var doubleElixirMessageShown = false; var doubleElixirMessageText = null; var doubleElixirMessageTimer = 0; var playerUnits = []; var enemyUnits = []; var bullets = []; var playerTowers = []; var enemyTowers = []; var playerTowersDestroyed = 0; var enemyTowersDestroyed = 0; var towersDestroyed = 0; var gameEnded = false; var aiLastDeploy = 0; var aiDeployInterval = 240; // 4 seconds // Card and UI variables var draggedCard = null; var menuElements = []; var deckMenuElements = []; var deckCards = []; var archerCard, knightCard, giantCard, wizardCard, cannonCard; var timerText, elixirText; var battlefieldLine; var showingDeckSelection = false; var selectedDeck = ['archer', 'knight', 'giant', 'wizard', 'cannon']; // Default deck (minimum 5 cards) var availableCards = ['archer', 'knight', 'giant', 'wizard', 'cannon', 'skeleton', 'skeletonArmy', 'fireball', 'minion', 'maquinaVoladora', 'megaesbirro', 'discharge', 'bruja', 'ballesta', 'montapuercos']; // Card system variables var activeCards = []; // Currently displayed 4 cards var cardQueue = []; // Queue of remaining cards var maxActiveCards = 4; // Create main menu function createMenu() { // Title var titleText = new Text2('CLASH ROYALE', { size: 120, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; game.addChild(titleText); menuElements.push(titleText); // Play button background var playButton = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.8, x: 1024, y: 1400 }); playButton.tint = 0x4CAF50; game.addChild(playButton); menuElements.push(playButton); // Play button text var playText = new Text2('JUGAR', { size: 80, fill: 0xFFFFFF }); playText.anchor.set(0.5, 0.5); playText.x = 1024; playText.y = 1400; game.addChild(playText); menuElements.push(playText); // Mazo button background var mazoButton = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 0.8, x: 1024, y: 1600 }); mazoButton.tint = 0x9C27B0; game.addChild(mazoButton); menuElements.push(mazoButton); // Mazo button text var mazoText = new Text2('MAZO', { size: 80, fill: 0xFFFFFF }); mazoText.anchor.set(0.5, 0.5); mazoText.x = 1024; mazoText.y = 1600; game.addChild(mazoText); menuElements.push(mazoText); // Instructions var instructionsText = new Text2('Arrastra las cartas al campo de batalla', { size: 40, fill: 0xFFFFFF }); instructionsText.anchor.set(0.5, 0.5); instructionsText.x = 1024; instructionsText.y = 1800; game.addChild(instructionsText); menuElements.push(instructionsText); // Make play button clickable playButton.down = function (x, y, obj) { startGame(); }; playText.down = function (x, y, obj) { startGame(); }; // Make mazo button clickable mazoButton.down = function (x, y, obj) { showDeckSelection(); }; mazoText.down = function (x, y, obj) { showDeckSelection(); }; } function showDeckSelection() { // Hide main menu for (var i = 0; i < menuElements.length; i++) { menuElements[i].alpha = 0.3; } showingDeckSelection = true; createDeckMenu(); } function createDeckMenu() { // Title var deckTitle = new Text2('SELECCIONA TU MAZO', { size: 80, fill: 0xFFD700 }); deckTitle.anchor.set(0.5, 0.5); deckTitle.x = 1024; deckTitle.y = 600; game.addChild(deckTitle); deckMenuElements.push(deckTitle); // Instructions var instructions = new Text2('Toca las cartas para añadir/quitar del mazo (min 5, max 8)', { size: 35, fill: 0xFFFFFF }); instructions.anchor.set(0.5, 0.5); instructions.x = 1024; instructions.y = 700; game.addChild(instructions); deckMenuElements.push(instructions); // Available cards display var cardStartX = 300; var cardSpacing = 350; for (var i = 0; i < availableCards.length; i++) { var cardType = availableCards[i]; var cardContainer = new Container(); cardContainer.x = cardStartX + i * cardSpacing; // Position skeleton card 100px below archer, skeleton army 100px below knight, fireball 100px below giant, minion 100px below wizard, maquina voladora 100px below skeleton, discharge 100px below cannon if (cardType === 'skeleton') { var archerIndex = availableCards.indexOf('archer'); cardContainer.x = cardStartX + archerIndex * cardSpacing; cardContainer.y = 1000; // 100px below archer } else if (cardType === 'skeletonArmy') { var knightIndex = availableCards.indexOf('knight'); cardContainer.x = cardStartX + knightIndex * cardSpacing; cardContainer.y = 1000; // 100px below knight } else if (cardType === 'fireball') { var giantIndex = availableCards.indexOf('giant'); cardContainer.x = cardStartX + giantIndex * cardSpacing; cardContainer.y = 1000; // 100px below giant } else if (cardType === 'minion') { var wizardIndex = availableCards.indexOf('wizard'); cardContainer.x = cardStartX + wizardIndex * cardSpacing; cardContainer.y = 1000; // 100px below wizard } else if (cardType === 'maquinaVoladora') { var skeletonIndex = availableCards.indexOf('skeleton'); var archerIndex = availableCards.indexOf('archer'); cardContainer.x = cardStartX + archerIndex * cardSpacing; cardContainer.y = 1100; // 100px below skeleton (200px total below archer) } else if (cardType === 'megaesbirro') { var skeletonArmyIndex = availableCards.indexOf('skeletonArmy'); var knightIndex = availableCards.indexOf('knight'); cardContainer.x = cardStartX + knightIndex * cardSpacing; cardContainer.y = 1100; // 100px below skeleton army (200px total below knight) } else if (cardType === 'discharge') { var cannonIndex = availableCards.indexOf('cannon'); cardContainer.x = cardStartX + cannonIndex * cardSpacing; cardContainer.y = 1000; // 100px below cannon } else if (cardType === 'bruja') { var fireballIndex = availableCards.indexOf('fireball'); cardContainer.x = cardStartX + fireballIndex * cardSpacing; cardContainer.y = 1100; // 100px below fireball } else if (cardType === 'ballesta') { var minionIndex = availableCards.indexOf('minion'); var wizardIndex = availableCards.indexOf('wizard'); cardContainer.x = cardStartX + wizardIndex * cardSpacing; cardContainer.y = 1100; // 100px below minion (200px total below wizard) } else if (cardType === 'montapuercos') { var dischargeIndex = availableCards.indexOf('discharge'); var cannonIndex = availableCards.indexOf('cannon'); cardContainer.x = cardStartX + cannonIndex * cardSpacing; cardContainer.y = 1100; // 100px below discharge (200px total below cannon) } else { cardContainer.y = 900; } // Card background var cardBg = cardContainer.attachAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5 }); // Check if card is in selected deck var isSelected = selectedDeck.indexOf(cardType) !== -1; cardBg.tint = isSelected ? 0x4CAF50 : 0x607d8b; // Create special cardslot container for bruja below fireball if (cardType === 'fireball') { // Create bruja cardslot container var brujaCardSlot = new Container(); brujaCardSlot.x = 0; brujaCardSlot.y = 100; // 100px below fireball // Bruja cardslot background var brujaCardBg = brujaCardSlot.attachAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5 }); brujaCardBg.tint = selectedDeck.indexOf('bruja') !== -1 ? 0x4CAF50 : 0x607d8b; // Bruja icon var brujaIcon = LK.getAsset('bruja', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); brujaCardSlot.addChild(brujaIcon); // Bruja cost var brujaCost = new Text2('5', { size: 25, fill: 0xFFFFFF }); brujaCost.anchor.set(0.5, 0.5); brujaCost.x = 120; brujaCost.y = -40; brujaCardSlot.addChild(brujaCost); // Selection indicator for bruja if (selectedDeck.indexOf('bruja') !== -1) { var brujaCheckmark = new Text2('✓', { size: 40, fill: 0xFFFFFF }); brujaCheckmark.anchor.set(0.5, 0.5); brujaCheckmark.x = 0; brujaCheckmark.y = 80; brujaCardSlot.addChild(brujaCheckmark); } // Make bruja cardslot clickable brujaCardSlot.cardType = 'bruja'; brujaCardSlot.down = function (x, y, obj) { toggleCardInDeck('bruja'); // Return true to stop event propagation to parent containers return true; }; cardContainer.addChild(brujaCardSlot); deckMenuElements.push(brujaCardSlot); } // Card icon var cardIcon = LK.getAsset(cardType === 'discharge' ? 'descarga_descardina' : cardType === 'maquinaVoladora' ? 'flying_machine' : cardType === 'bruja' ? 'bruja' : cardType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); cardContainer.addChild(cardIcon); // Card cost var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : cardType === 'montapuercos' ? 4 : 3; var costText = new Text2(cost.toString(), { size: 25, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0.5); costText.x = 120; costText.y = -40; cardContainer.addChild(costText); // Selection indicator if (isSelected) { var checkmark = new Text2('✓', { size: 40, fill: 0xFFFFFF }); checkmark.anchor.set(0.5, 0.5); checkmark.x = 0; checkmark.y = 80; cardContainer.addChild(checkmark); } // Make card clickable cardContainer.cardType = cardType; cardContainer.down = function (x, y, obj) { // For fireball, check if click is in the bruja area to prevent double selection if (this.cardType === 'fireball' && y > 50) { // Click is in the lower area where bruja cardslot is, ignore it return true; } toggleCardInDeck(this.cardType); }; game.addChild(cardContainer); deckMenuElements.push(cardContainer); } // Selected deck display var deckTitle2 = new Text2('MAZO ACTUAL (' + selectedDeck.length + '/8)', { size: 60, fill: 0x4CAF50 }); deckTitle2.anchor.set(0.5, 0.5); deckTitle2.x = 1024; deckTitle2.y = 1200; game.addChild(deckTitle2); deckMenuElements.push(deckTitle2); // Display selected cards var selectedStartX = 424; var selectedSpacing = 240; for (var i = 0; i < selectedDeck.length && i < 8; i++) { var cardType = selectedDeck[i]; var selectedCardContainer = new Container(); selectedCardContainer.x = selectedStartX + i * selectedSpacing; selectedCardContainer.y = 1400; // Small card background var selectedCardBg = selectedCardContainer.attachAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); selectedCardBg.tint = 0x2196F3; // Small card icon var selectedCardIcon = LK.getAsset(cardType === 'discharge' ? 'descarga_descardina' : cardType === 'maquinaVoladora' ? 'flying_machine' : cardType === 'bruja' ? 'bruja' : cardType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); selectedCardContainer.addChild(selectedCardIcon); game.addChild(selectedCardContainer); deckMenuElements.push(selectedCardContainer); } // Back button var backButton = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 0.6, x: 1024, y: 1650 }); backButton.tint = 0xFF9800; game.addChild(backButton); deckMenuElements.push(backButton); var backText = new Text2('VOLVER', { size: 50, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backText.x = 1024; backText.y = 1650; game.addChild(backText); deckMenuElements.push(backText); // Make back button clickable backButton.down = function (x, y, obj) { hideDeckSelection(); }; backText.down = function (x, y, obj) { hideDeckSelection(); }; } function toggleCardInDeck(cardType) { var cardIndex = selectedDeck.indexOf(cardType); if (cardIndex !== -1) { // Only allow removal if deck has more than 5 cards if (selectedDeck.length > 5) { selectedDeck.splice(cardIndex, 1); } } else if (selectedDeck.length < 8) { // Add card to deck if not full (max 8 cards) selectedDeck.push(cardType); } // Refresh deck menu refreshDeckMenu(); } function refreshDeckMenu() { // Remove current deck menu elements for (var i = 0; i < deckMenuElements.length; i++) { deckMenuElements[i].destroy(); } deckMenuElements = []; // Recreate deck menu createDeckMenu(); } function hideDeckSelection() { // Remove deck menu elements for (var i = 0; i < deckMenuElements.length; i++) { deckMenuElements[i].destroy(); } deckMenuElements = []; // Show main menu again for (var i = 0; i < menuElements.length; i++) { menuElements[i].alpha = 1; } showingDeckSelection = false; } function startGame() { // Remove menu elements for (var i = 0; i < menuElements.length; i++) { menuElements[i].destroy(); } menuElements = []; // Start the actual game gameStarted = true; initializeGame(); } function initializeGame() { // Generate AI's randomized deck for this match generateAIDeck(); // Create battlefield divider battlefieldLine = game.addChild(LK.getAsset('battlefield', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366 })); battlefieldLine.tint = 0x000000; battlefieldLine.alpha = 0.3; // Create towers // Player towers (bottom) var playerLeftTower = game.addChild(new Tower(true, false)); playerLeftTower.x = 512; playerLeftTower.y = 2400; playerTowers.push(playerLeftTower); var playerRightTower = game.addChild(new Tower(true, false)); playerRightTower.x = 1536; playerRightTower.y = 2400; playerTowers.push(playerRightTower); var playerKingTower = game.addChild(new Tower(true, true)); playerKingTower.x = 1024; playerKingTower.y = 2600; playerTowers.push(playerKingTower); // Enemy towers (top) var enemyLeftTower = game.addChild(new Tower(false, false)); enemyLeftTower.x = 512; enemyLeftTower.y = 400; enemyTowers.push(enemyLeftTower); var enemyRightTower = game.addChild(new Tower(false, false)); enemyRightTower.x = 1536; enemyRightTower.y = 400; enemyTowers.push(enemyRightTower); var enemyKingTower = game.addChild(new Tower(false, true)); enemyKingTower.x = 1024; enemyKingTower.y = 200; enemyTowers.push(enemyKingTower); // UI Elements timerText = new Text2(gameTime.toString(), { size: 60, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); elixirText = new Text2(elixir + "/" + maxElixir, { size: 40, fill: 0xE91E63 }); elixirText.anchor.set(0, 1); elixirText.x = 50; LK.gui.bottomLeft.addChild(elixirText); // Initialize card system activeCards = []; cardQueue = []; deckCards = []; // Setup active cards (first 4) and queue (remaining) for (var i = 0; i < selectedDeck.length; i++) { if (i < maxActiveCards) { activeCards.push(selectedDeck[i]); } else { cardQueue.push(selectedDeck[i]); } } // Create visual cards for active cards only var cardSpacing = 300; var startX = -(maxActiveCards - 1) * cardSpacing / 2; for (var i = 0; i < activeCards.length; i++) { var cardType = activeCards[i]; var newCard = LK.gui.bottom.addChild(new Card(cardType)); newCard.x = startX + i * cardSpacing; newCard.y = -100; newCard.cardType = cardType; // Store card type for event handling newCard.cardIndex = i; // Store index for replacement deckCards.push(newCard); } // Store references for backward compatibility if (activeCards.indexOf('archer') !== -1) { archerCard = deckCards[activeCards.indexOf('archer')]; } if (activeCards.indexOf('knight') !== -1) { knightCard = deckCards[activeCards.indexOf('knight')]; } if (activeCards.indexOf('giant') !== -1) { giantCard = deckCards[activeCards.indexOf('giant')]; } if (activeCards.indexOf('wizard') !== -1) { wizardCard = deckCards[activeCards.indexOf('wizard')]; } if (activeCards.indexOf('cannon') !== -1) { cannonCard = deckCards[activeCards.indexOf('cannon')]; } // Setup event handlers setupEventHandlers(); } function rotateCard(usedCardIndex) { if (cardQueue.length === 0) { return; } // No cards in queue to rotate // Get the next card from queue var nextCard = cardQueue.shift(); // Remove first card from queue var usedCard = activeCards[usedCardIndex]; // Get the used card // Add used card to end of queue cardQueue.push(usedCard); // Replace active card with next card activeCards[usedCardIndex] = nextCard; // Update visual card var cardToUpdate = deckCards[usedCardIndex]; if (cardToUpdate) { // Store position and index var cardX = cardToUpdate.x; var cardY = cardToUpdate.y; var cardIndex = cardToUpdate.cardIndex; // Remove old card cardToUpdate.destroy(); // Create new card with next card type var newCard = LK.gui.bottom.addChild(new Card(nextCard)); newCard.x = cardX; newCard.y = cardY; newCard.cardType = nextCard; newCard.cardIndex = cardIndex; // Update references deckCards[usedCardIndex] = newCard; // Update event handler for new card var cardType = newCard.cardType; var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : 3; newCard.down = function (x, y, obj) { if (elixir >= cost) { draggedCard = cardType; } }; } } function deployUnit(cardType, x, y, isPlayer) { var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : cardType === 'montapuercos' ? 4 : 3; if (isPlayer && elixir < cost) { return false; } if (!isPlayer && aiElixir < cost) { return false; } // AI also needs elixir // Check if position is in correct half (except for spells) if (cardType !== 'fireball') { if (isPlayer && y < 1366) { return false; } // Player can only deploy in bottom half if (!isPlayer && y > 1366) { return false; } // AI can only deploy in top half } var unit = null; if (cardType === 'archer') { // Deploy 2 archers var archer1 = new Archer(isPlayer); var archer2 = new Archer(isPlayer); archer1.x = x - 40; // Left archer archer1.y = y; archer2.x = x + 40; // Right archer archer2.y = y; if (isPlayer) { playerUnits.push(archer1); playerUnits.push(archer2); elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { enemyUnits.push(archer1); enemyUnits.push(archer2); aiElixir -= cost; // Deduct AI elixir } game.addChild(archer1); game.addChild(archer2); LK.getSound('deploy').play(); return true; } else if (cardType === 'knight') { unit = new Knight(isPlayer); } else if (cardType === 'giant') { unit = new Giant(isPlayer); } else if (cardType === 'wizard') { unit = new Wizard(isPlayer); } else if (cardType === 'skeleton') { // Deploy 3 skeletons in formation: front, back-right, back-left var skeleton1 = new Skeleton(isPlayer); // Front skeleton var skeleton2 = new Skeleton(isPlayer); // Back-right skeleton var skeleton3 = new Skeleton(isPlayer); // Back-left skeleton skeleton1.x = x; // Front skeleton at deployment position skeleton1.y = y; skeleton2.x = x + 60; // Back-right skeleton skeleton2.y = isPlayer ? y + 60 : y - 60; // Adjust based on player side skeleton3.x = x - 60; // Back-left skeleton skeleton3.y = isPlayer ? y + 60 : y - 60; // Adjust based on player side if (isPlayer) { playerUnits.push(skeleton1); playerUnits.push(skeleton2); playerUnits.push(skeleton3); elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { enemyUnits.push(skeleton1); enemyUnits.push(skeleton2); enemyUnits.push(skeleton3); aiElixir -= cost; // Deduct AI elixir } game.addChild(skeleton1); game.addChild(skeleton2); game.addChild(skeleton3); LK.getSound('deploy').play(); return true; } else if (cardType === 'skeletonArmy') { // Deploy 12 skeletons in a 3x4 formation var skeletons = []; for (var row = 0; row < 3; row++) { for (var col = 0; col < 4; col++) { var skeleton = new Skeleton(isPlayer); skeleton.x = x + (col - 1.5) * 40; // Spread horizontally skeleton.y = y + (row - 1) * 40; // Spread vertically skeletons.push(skeleton); game.addChild(skeleton); } } if (isPlayer) { for (var s = 0; s < skeletons.length; s++) { playerUnits.push(skeletons[s]); } elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { for (var s = 0; s < skeletons.length; s++) { enemyUnits.push(skeletons[s]); } aiElixir -= cost; // Deduct AI elixir } LK.getSound('deploy').play(); return true; } else if (cardType === 'fireball') { // Fireball is a spell - can be deployed anywhere on the map var fireball = new Fireball(x, y, isPlayer); bullets.push(fireball); // Add to bullets array for cleanup game.addChild(fireball); if (isPlayer) { elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { aiElixir -= cost; // Deduct AI elixir } LK.getSound('deploy').play(); return true; } else if (cardType === 'discharge') { // Discharge is a spell - can be deployed anywhere on the map, appears instantly var discharge = new Discharge(x, y, isPlayer); // Don't add to bullets array since it destroys itself immediately game.addChild(discharge); if (isPlayer) { elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { aiElixir -= cost; // Deduct AI elixir } LK.getSound('deploy').play(); return true; } else if (cardType === 'minion') { // Deploy 3 minions in formation like skeletons: front, back-right, back-left var minion1 = new Minion(isPlayer); // Front minion var minion2 = new Minion(isPlayer); // Back-right minion var minion3 = new Minion(isPlayer); // Back-left minion minion1.x = x; // Front minion at deployment position minion1.y = y; minion2.x = x + 60; // Back-right minion minion2.y = isPlayer ? y + 60 : y - 60; // Adjust based on player side minion3.x = x - 60; // Back-left minion minion3.y = isPlayer ? y + 60 : y - 60; // Adjust based on player side if (isPlayer) { playerUnits.push(minion1); playerUnits.push(minion2); playerUnits.push(minion3); elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { enemyUnits.push(minion1); enemyUnits.push(minion2); enemyUnits.push(minion3); aiElixir -= cost; // Deduct AI elixir } game.addChild(minion1); game.addChild(minion2); game.addChild(minion3); LK.getSound('deploy').play(); return true; } else if (cardType === 'maquinaVoladora') { // Deploy single maquina voladora unit = new MaquinaVoladora(isPlayer); } else if (cardType === 'megaesbirro') { // Deploy single megaesbirro unit = new Megaesbirro(isPlayer); } else if (cardType === 'bruja') { // Deploy single bruja unit = new Bruja(isPlayer); } else if (cardType === 'ballesta') { unit = new Ballesta(isPlayer); } else if (cardType === 'montapuercos') { unit = new Montapuercos(isPlayer); } else if (cardType === 'cannon') { unit = new Cannon(isPlayer); } if (unit) { unit.x = x; unit.y = y; if (isPlayer) { playerUnits.push(unit); elixir -= cost; elixirText.setText(elixir + "/" + maxElixir); // Rotate card if player used it var cardIndex = activeCards.indexOf(cardType); if (cardIndex !== -1) { rotateCard(cardIndex); } } else { enemyUnits.push(unit); aiElixir -= cost; // Deduct AI elixir } game.addChild(unit); LK.getSound('deploy').play(); return true; } return false; } // AI deployment variables var aiLastUsedCard = null; var aiCardCooldown = {}; var aiDeck = []; // AI's randomized deck for the match // Generate AI deck with required constraints function generateAIDeck() { aiDeck = []; // Required cards (must have) var requiredCards = { aerial: 'archer', // Card that can attack aerial units wincondition: Math.random() < 0.5 ? 'giant' : 'montapuercos', // Wincondition card (randomly choose between giant and montapuercos) spell: Math.random() < 0.5 ? 'fireball' : 'discharge', // Spell card (randomly choose between fireball and discharge) structure: Math.random() < 0.5 ? 'cannon' : 'ballesta' // Structure card (randomly choose between cannon and ballesta) }; // Add required cards to AI deck aiDeck.push(requiredCards.aerial); aiDeck.push(requiredCards.wincondition); aiDeck.push(requiredCards.spell); aiDeck.push(requiredCards.structure); // Remaining card pool (excluding already added required cards) var remainingCards = ['knight', 'wizard', 'bruja', 'skeleton', 'skeletonArmy', 'minion', 'maquinaVoladora', 'megaesbirro', 'ballesta', 'montapuercos']; // Fill remaining 4 slots with random cards from remaining pool while (aiDeck.length < 8) { var randomIndex = Math.floor(Math.random() * remainingCards.length); var randomCard = remainingCards[randomIndex]; aiDeck.push(randomCard); // Don't remove from remainingCards to allow duplicates } // Shuffle the deck to randomize order for (var i = aiDeck.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = aiDeck[i]; aiDeck[i] = aiDeck[j]; aiDeck[j] = temp; } } // AI deployment function function aiDeploy() { // Check if AI is being attacked (player units in AI territory) var isUnderAttack = false; var playerUnitsInAITerritory = 0; for (var i = 0; i < playerUnits.length; i++) { if (playerUnits[i].y < 1366) { // Player units in AI half isUnderAttack = true; playerUnitsInAITerritory++; } } // Check if AI towers are taking damage var aiTowersUnderAttack = false; for (var i = 0; i < enemyTowers.length; i++) { if (enemyTowers[i].currentHealth < enemyTowers[i].maxHealth) { aiTowersUnderAttack = true; break; } } if (isUnderAttack || aiTowersUnderAttack) { // DEFENSE MODE: Deploy defensive units quickly if being attacked if (LK.ticks - aiLastDeploy > 60 && aiElixir >= 3) { // Deploy faster when defending var defensiveCards = []; // Check if player has used wincondition (giant) or if towers are damaged var playerHasWincondition = false; for (var i = 0; i < playerUnits.length; i++) { if (playerUnits[i].unitType === 'giant') { playerHasWincondition = true; break; } } if (aiElixir >= 3) { // Use cards from AI's deck for defense for (var d = 0; d < aiDeck.length; d++) { var deckCard = aiDeck[d]; var cardCost = deckCard === 'giant' ? 5 : deckCard === 'wizard' ? 5 : deckCard === 'bruja' ? 5 : deckCard === 'skeleton' ? 1 : deckCard === 'skeletonArmy' ? 3 : deckCard === 'fireball' ? 4 : deckCard === 'discharge' ? 2 : deckCard === 'minion' ? 3 : deckCard === 'maquinaVoladora' ? 4 : deckCard === 'megaesbirro' ? 3 : deckCard === 'ballesta' ? 6 : deckCard === 'montapuercos' ? 4 : 3; if (aiElixir >= cardCost) { // Add defensive units (not spells for defense) if (deckCard !== 'fireball' && deckCard !== 'discharge') { defensiveCards.push(deckCard); } } } } // Check for spell usage opportunities (discharge and fireball) var spellCards = []; for (var s = 0; s < aiDeck.length; s++) { var spellCard = aiDeck[s]; if (spellCard === 'discharge' || spellCard === 'fireball') { var spellCost = spellCard === 'discharge' ? 2 : 4; if (aiElixir >= spellCost) { spellCards.push(spellCard); } } } if (spellCards.length > 0 && playerUnits.length > 0) { // Find player unit with most enemies nearby for spell targeting var bestTarget = null; var maxNearbyUnits = 0; for (var p = 0; p < playerUnits.length; p++) { var playerUnit = playerUnits[p]; if (!playerUnit.isDead) { var nearbyCount = 0; // Count nearby player units for (var n = 0; n < playerUnits.length; n++) { var otherUnit = playerUnits[n]; if (!otherUnit.isDead && otherUnit !== playerUnit) { var distance = Math.sqrt(Math.pow(playerUnit.x - otherUnit.x, 2) + Math.pow(playerUnit.y - otherUnit.y, 2)); if (distance <= 200) { // Within spell effect range nearbyCount++; } } } if (nearbyCount > maxNearbyUnits) { maxNearbyUnits = nearbyCount; bestTarget = playerUnit; } } } // Use spell if we found a good target (at least 1 nearby unit or any unit if no clusters) if (bestTarget && (maxNearbyUnits > 0 || playerUnits.length > 0)) { var chosenSpell = spellCards[Math.floor(Math.random() * spellCards.length)]; if (deployUnit(chosenSpell, bestTarget.x, bestTarget.y, false)) { aiLastDeploy = LK.ticks; aiDeployInterval = 120 + Math.random() * 60; return; // Exit early after using spell } } } if (defensiveCards.length > 0) { var randomCard = defensiveCards[Math.floor(Math.random() * defensiveCards.length)]; // Deploy near threatened area var deployX = 300 + Math.random() * 1448; var deployY = 800 + Math.random() * 400; // Deploy in middle-upper area if (deployUnit(randomCard, deployX, deployY, false)) { aiLastDeploy = LK.ticks; aiDeployInterval = 120 + Math.random() * 60; // Quick deploy interval when defending } } } } else { // ATTACK MODE: Use wincondition strategy when not under attack // Check for spell usage opportunities in attack mode if (aiElixir >= 2 && playerUnits.length > 0 && Math.random() < 0.3) { // 30% chance to use spells in attack var attackSpellCards = []; for (var s = 0; s < aiDeck.length; s++) { var spellCard = aiDeck[s]; if (spellCard === 'discharge' || spellCard === 'fireball') { var spellCost = spellCard === 'discharge' ? 2 : 4; if (aiElixir >= spellCost) { attackSpellCards.push(spellCard); } } } if (attackSpellCards.length > 0) { // Target strongest or most clustered player units var bestAttackTarget = null; var maxValue = 0; for (var p = 0; p < playerUnits.length; p++) { var playerUnit = playerUnits[p]; if (!playerUnit.isDead) { // Calculate value based on unit health + nearby units var unitValue = playerUnit.currentHealth; // Count nearby player units for cluster bonus for (var n = 0; n < playerUnits.length; n++) { var otherUnit = playerUnits[n]; if (!otherUnit.isDead && otherUnit !== playerUnit) { var distance = Math.sqrt(Math.pow(playerUnit.x - otherUnit.x, 2) + Math.pow(playerUnit.y - otherUnit.y, 2)); if (distance <= 200) { unitValue += otherUnit.currentHealth * 0.5; // Bonus for clustered units } } } if (unitValue > maxValue) { maxValue = unitValue; bestAttackTarget = playerUnit; } } } if (bestAttackTarget) { var chosenAttackSpell = attackSpellCards[Math.floor(Math.random() * attackSpellCards.length)]; if (deployUnit(chosenAttackSpell, bestAttackTarget.x, bestAttackTarget.y, false)) { aiLastDeploy = LK.ticks; aiDeployInterval = 180 + Math.random() * 120; return; // Exit after using spell } } } } // Check if AI already has giants on the field var giantsOnField = 0; for (var i = 0; i < enemyUnits.length; i++) { if (enemyUnits[i].unitType === 'giant' && !enemyUnits[i].isDead) { giantsOnField++; } } if (aiElixir >= 8 && LK.ticks - aiLastDeploy > aiDeployInterval && giantsOnField < 2) { // Big push with wincondition + support (max 2 giants on field) if (aiElixir >= 8 && Math.random() < 0.9) { // 90% chance for big attack when having 8+ elixir // Find wincondition card in AI deck var winconditionCard = null; for (var w = 0; w < aiDeck.length; w++) { if (aiDeck[w] === 'giant' || aiDeck[w] === 'montapuercos') { winconditionCard = aiDeck[w]; break; } } if (winconditionCard) { // Deploy wincondition first var deployX = 300 + Math.random() * 1448; var deployY = 200 + Math.random() * 600; if (deployUnit(winconditionCard, deployX, deployY, false)) { var winconditionCost = winconditionCard === 'giant' ? 5 : winconditionCard === 'montapuercos' ? 4 : 3; aiElixir -= winconditionCost; // Wait a bit then deploy support unit behind wincondition LK.setTimeout(function () { if (aiElixir >= 3) { var supportCards = []; // Get support cards from AI deck for (var s = 0; s < aiDeck.length; s++) { var supportCard = aiDeck[s]; var supportCost = supportCard === 'giant' ? 5 : supportCard === 'wizard' ? 5 : supportCard === 'skeleton' ? 1 : supportCard === 'skeletonArmy' ? 3 : supportCard === 'fireball' ? 4 : supportCard === 'discharge' ? 2 : supportCard === 'minion' ? 3 : supportCard === 'montapuercos' ? 4 : 3; if (aiElixir >= supportCost && supportCard !== 'fireball' && supportCard !== 'discharge' && supportCard !== winconditionCard) { supportCards.push(supportCard); } } if (supportCards.length > 0) { var chosenSupport = supportCards[Math.floor(Math.random() * supportCards.length)]; var supportX = deployX + (Math.random() - 0.5) * 200; // Near wincondition var supportY = deployY + 100 + Math.random() * 200; // Behind wincondition deployUnit(chosenSupport, supportX, supportY, false); } } }, 1000); // 1 second delay aiLastDeploy = LK.ticks; aiDeployInterval = 300 + Math.random() * 180; // Longer interval after big push } } } } else if (aiElixir >= 5 && aiElixir < 8 && LK.ticks - aiLastDeploy > aiDeployInterval && giantsOnField < 2) { // Single wincondition deploy when having 5-7 elixir (more likely to save) if (Math.random() < 0.4) { // 40% chance to use wincondition (reduced from 60% to encourage saving) var winconditionCard = null; for (var w = 0; w < aiDeck.length; w++) { if (aiDeck[w] === 'giant' || aiDeck[w] === 'montapuercos') { winconditionCard = aiDeck[w]; break; } } if (winconditionCard) { var deployX = 300 + Math.random() * 1448; var deployY = 200 + Math.random() * 600; if (deployUnit(winconditionCard, deployX, deployY, false)) { aiLastDeploy = LK.ticks; aiDeployInterval = 240 + Math.random() * 120; } } } else { // Regular unit deployment (less likely when saving) if (aiElixir >= 3 && Math.random() < 0.3) { var availableCards = []; for (var a = 0; a < aiDeck.length; a++) { var deckCard = aiDeck[a]; var cardCost = deckCard === 'giant' ? 5 : deckCard === 'wizard' ? 5 : deckCard === 'bruja' ? 5 : deckCard === 'skeleton' ? 1 : deckCard === 'skeletonArmy' ? 3 : deckCard === 'fireball' ? 4 : deckCard === 'discharge' ? 2 : deckCard === 'minion' ? 3 : deckCard === 'maquinaVoladora' ? 4 : deckCard === 'megaesbirro' ? 3 : deckCard === 'ballesta' ? 6 : 3; if (aiElixir >= cardCost && deckCard !== 'fireball' && deckCard !== 'discharge') { availableCards.push(deckCard); } } if (availableCards.length > 0) { var randomCard = availableCards[Math.floor(Math.random() * availableCards.length)]; var deployX = 300 + Math.random() * 1448; var deployY = 200 + Math.random() * 1000; if (deployUnit(randomCard, deployX, deployY, false)) { aiLastDeploy = LK.ticks; aiDeployInterval = 180 + Math.random() * 120; } } } } } else if (aiElixir >= 3 && aiElixir < 5 && LK.ticks - aiLastDeploy > aiDeployInterval * 3) { // Wait even longer when having low elixir (3-4), prioritize saving for wincondition // Only deploy if waiting for too long or in emergency if (Math.random() < 0.15) { // Only 15% chance to deploy regular units when saving (reduced from 30%) var lowCostCards = []; for (var l = 0; l < aiDeck.length; l++) { var deckCard = aiDeck[l]; var cardCost = deckCard === 'giant' ? 5 : deckCard === 'wizard' ? 5 : deckCard === 'bruja' ? 5 : deckCard === 'skeleton' ? 1 : deckCard === 'skeletonArmy' ? 3 : deckCard === 'fireball' ? 4 : deckCard === 'discharge' ? 2 : deckCard === 'minion' ? 3 : deckCard === 'maquinaVoladora' ? 4 : deckCard === 'megaesbirro' ? 3 : deckCard === 'ballesta' ? 6 : 3; if (aiElixir >= cardCost && deckCard !== 'fireball' && deckCard !== 'discharge' && cardCost <= 4) { lowCostCards.push(deckCard); } } if (lowCostCards.length > 0) { var randomCard = lowCostCards[Math.floor(Math.random() * lowCostCards.length)]; var deployX = 300 + Math.random() * 1448; var deployY = 200 + Math.random() * 1000; if (deployUnit(randomCard, deployX, deployY, false)) { aiLastDeploy = LK.ticks; aiDeployInterval = 240 + Math.random() * 120; } } } } } } function checkGameEnd() { if (gameTime <= 0 && !gameEnded) { gameEnded = true; if (playerTowersDestroyed > enemyTowersDestroyed) { LK.showGameOver(); } else if (enemyTowersDestroyed > playerTowersDestroyed) { LK.showYouWin(); } else { // Tie-breaker: check tower health var playerTotalHealth = 0; var enemyTotalHealth = 0; for (var i = 0; i < playerTowers.length; i++) { playerTotalHealth += playerTowers[i].currentHealth; } for (var i = 0; i < enemyTowers.length; i++) { enemyTotalHealth += enemyTowers[i].currentHealth; } if (playerTotalHealth > enemyTotalHealth) { LK.showYouWin(); } else if (enemyTotalHealth > playerTotalHealth) { LK.showGameOver(); } else { LK.showGameOver(); // Draw counts as loss } } } } function setupEventHandlers() { // Event handlers for active deck cards only for (var i = 0; i < deckCards.length; i++) { var card = deckCards[i]; var cardType = card.cardType; var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : cardType === 'montapuercos' ? 4 : 3; // Create closure to capture cardType and cost (function (type, cardCost, index) { card.down = function (x, y, obj) { if (elixir >= cardCost) { draggedCard = type; } }; })(cardType, cost, i); } game.down = function (x, y, obj) { // Only handle game events if game has started if (!gameStarted) { return; } // This will handle deployment when clicking on the battlefield }; game.up = function (x, y, obj) { // Only handle game events if game has started if (!gameStarted) { return; } if (draggedCard) { // Convert coordinates to game space var gamePos = { x: x, y: y }; if (obj && obj.parent) { var globalPos = obj.parent.toGlobal ? obj.parent.toGlobal(obj.position) : { x: x, y: y }; gamePos = game.toLocal ? game.toLocal(globalPos) : { x: x, y: y }; } if (deployUnit(draggedCard, gamePos.x, gamePos.y, true)) { // Unit deployed successfully } draggedCard = null; } }; } // Initialize menu createMenu(); game.update = function () { // Only run game logic if game has started if (!gameStarted) { return; } if (gameEnded) { return; } // Update timer if (LK.ticks % 60 === 0 && gameTime > 0) { gameTime--; timerText.setText(gameTime.toString()); } // Check for double elixir activation at 120 seconds remaining (130 seconds elapsed) if (gameTime === 130 && !doubleElixirActive) { doubleElixirActive = true; doubleElixirMessageShown = false; // Create double elixir message doubleElixirMessageText = new Text2('¡ELIXIR DOBLE ACTIVADO!', { size: 80, fill: 0xFFD700 }); doubleElixirMessageText.anchor.set(0.5, 0.5); doubleElixirMessageText.x = 1024; doubleElixirMessageText.y = 300; game.addChild(doubleElixirMessageText); // Set message timer for 3 seconds doubleElixirMessageTimer = 180; // 3 seconds at 60fps // Flash effect for the message tween(doubleElixirMessageText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, onFinish: function onFinish() { tween(doubleElixirMessageText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 500 }); } }); } // Handle double elixir message timer if (doubleElixirMessageTimer > 0) { doubleElixirMessageTimer--; if (doubleElixirMessageTimer <= 0 && doubleElixirMessageText) { doubleElixirMessageText.destroy(); doubleElixirMessageText = null; } } // Regenerate elixir (double speed after 120 seconds passed) var currentElixirRegenRate = doubleElixirActive ? doubleElixirRegenRate : elixirRegenRate; if (LK.ticks - lastElixirRegen >= currentElixirRegenRate && elixir < maxElixir) { elixir++; elixirText.setText(elixir + "/" + maxElixir); lastElixirRegen = LK.ticks; } // Regenerate AI elixir (double speed after 120 seconds passed) var currentAiElixirRegenRate = doubleElixirActive ? doubleAiElixirRegenRate : aiElixirRegenRate; if (LK.ticks - lastAiElixirRegen >= currentAiElixirRegenRate && aiElixir < aiMaxElixir) { aiElixir++; lastAiElixirRegen = LK.ticks; } // AI deployment aiDeploy(); // Clean up dead units for (var i = playerUnits.length - 1; i >= 0; i--) { if (playerUnits[i].isDead) { playerUnits[i].destroy(); playerUnits.splice(i, 1); } } for (var i = enemyUnits.length - 1; i >= 0; i--) { if (enemyUnits[i].isDead) { enemyUnits[i].destroy(); enemyUnits.splice(i, 1); } } // Clean up bullets for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i].destroyed || bullets[i].y < -100 || bullets[i].y > 2800) { bullets[i].destroy(); bullets.splice(i, 1); } } // Check for immediate game end conditions var allPlayerTowersDead = true; var allEnemyTowersDead = true; for (var i = 0; i < playerTowers.length; i++) { if (!playerTowers[i].isDead) { allPlayerTowersDead = false; break; } } for (var i = 0; i < enemyTowers.length; i++) { if (!enemyTowers[i].isDead) { allEnemyTowersDead = false; break; } } if (allPlayerTowersDead && !gameEnded) { gameEnded = true; LK.showGameOver(); } else if (allEnemyTowersDead && !gameEnded) { gameEnded = true; LK.showYouWin(); } checkGameEnd(); };
===================================================================
--- original.js
+++ change.js
@@ -786,9 +786,9 @@
});
var Card = Container.expand(function (cardType) {
var self = Container.call(this);
self.cardType = cardType;
- self.cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : 3;
+ self.cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'montapuercos' ? 4 : 3;
var cardBg = self.attachAsset('cardSlot', {
anchorX: 0.5,
anchorY: 0.5
});
@@ -1563,8 +1563,204 @@
self.attack();
};
return self;
});
+var Montapuercos = Container.expand(function (isPlayer) {
+ var self = Container.call(this);
+ self.isPlayer = isPlayer;
+ self.unitType = 'montapuercos';
+ self.isAerial = false; // Ground unit
+ self.maxHealth = 750;
+ self.currentHealth = 750;
+ self.damage = 150;
+ self.attackSpeed = 120; // 120 ticks
+ self.range = 60; // Melee range
+ self.speed = 2.25;
+ self.target = null;
+ self.lastAttackTime = 0;
+ self.isDead = false;
+ self.isAttacking = false;
+ // Spawn delay properties
+ self.spawnDelay = 60; // 1 second
+ self.spawnTimer = self.spawnDelay;
+ self.isSpawning = true;
+ var montapuercosGraphics = self.attachAsset('montapuercos', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Health bar background
+ var healthBarScale = 0.25;
+ var healthBarBg = LK.getAsset('cardSlot', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: healthBarScale,
+ scaleY: 0.1,
+ y: -50
+ });
+ healthBarBg.tint = 0x000000;
+ self.addChild(healthBarBg);
+ // Health bar
+ var healthBar = LK.getAsset('cardSlot', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: healthBarScale,
+ scaleY: 0.1,
+ y: -50
+ });
+ healthBar.tint = self.isPlayer ? 0x2196F3 : 0xF44336;
+ self.addChild(healthBar);
+ self.healthBar = healthBar;
+ self.healthBarMaxScale = healthBarScale;
+ // Spawn timer text
+ self.spawnTimerText = new Text2('', {
+ size: 30,
+ fill: 0xFFFFFF
+ });
+ self.spawnTimerText.anchor.set(0.5, 0.5);
+ self.spawnTimerText.y = 30;
+ self.addChild(self.spawnTimerText);
+ self.takeDamage = function (damage) {
+ self.currentHealth -= damage;
+ if (self.currentHealth <= 0) {
+ self.currentHealth = 0;
+ self.isDead = true;
+ }
+ // Update health bar
+ var healthPercent = self.currentHealth / self.maxHealth;
+ self.healthBar.scaleX = self.healthBarMaxScale * healthPercent;
+ // Flash red when taking damage
+ LK.effects.flashObject(self, 0xFF0000, 300);
+ };
+ self.findTarget = function () {
+ // Reset target if current target is dead or invalid
+ if (self.target && self.target.isDead) {
+ self.target = null;
+ self.isAttacking = false;
+ }
+ // If already attacking a target, don't change targets
+ if (self.isAttacking && self.target && !self.target.isDead) {
+ return;
+ }
+ var closestDistance = Infinity;
+ var closestTarget = null;
+ // Montapuercos only attacks structures and towers (wincondition behavior)
+ // First check for structures (cannons, ballesta) within detection range
+ var targetStructures = self.isPlayer ? enemyUnits : playerUnits;
+ for (var i = 0; i < targetStructures.length; i++) {
+ if (!targetStructures[i].isDead && (targetStructures[i].unitType === 'cannon' || targetStructures[i].unitType === 'ballesta')) {
+ var distance = Math.sqrt(Math.pow(self.x - targetStructures[i].x, 2) + Math.pow(self.y - targetStructures[i].y, 2));
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestTarget = targetStructures[i];
+ }
+ }
+ }
+ // If no structures found, target closest tower
+ if (!closestTarget) {
+ var targetTowers = self.isPlayer ? enemyTowers : playerTowers;
+ for (var i = 0; i < targetTowers.length; i++) {
+ if (!targetTowers[i].isDead) {
+ var distance = Math.sqrt(Math.pow(self.x - targetTowers[i].x, 2) + Math.pow(self.y - targetTowers[i].y, 2));
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestTarget = targetTowers[i];
+ }
+ }
+ }
+ }
+ self.target = closestTarget;
+ };
+ self.moveToTarget = function () {
+ if (!self.target || self.target.isDead) {
+ return;
+ }
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > self.range) {
+ var moveX = dx / distance * self.speed;
+ var moveY = dy / distance * self.speed;
+ self.x += moveX;
+ self.y += moveY;
+ }
+ };
+ self.attack = function () {
+ if (!self.target || self.target.isDead) {
+ return;
+ }
+ // Verify target is from opposing team before attacking
+ var isValidTarget = false;
+ if (self.isPlayer && enemyUnits.indexOf(self.target) !== -1) {
+ isValidTarget = true;
+ }
+ if (self.isPlayer && enemyTowers.indexOf(self.target) !== -1) {
+ isValidTarget = true;
+ }
+ if (!self.isPlayer && playerUnits.indexOf(self.target) !== -1) {
+ isValidTarget = true;
+ }
+ if (!self.isPlayer && playerTowers.indexOf(self.target) !== -1) {
+ isValidTarget = true;
+ }
+ if (!isValidTarget) {
+ self.target = null;
+ return;
+ }
+ var distance = Math.sqrt(Math.pow(self.x - self.target.x, 2) + Math.pow(self.y - self.target.y, 2));
+ if (distance <= self.range && LK.ticks - self.lastAttackTime > self.attackSpeed) {
+ self.isAttacking = true;
+ // Melee attack - direct damage
+ self.target.takeDamage(self.damage);
+ // Visual effect for melee strike
+ LK.effects.flashObject(self, 0xFFFFFF, 200);
+ // Flash target red when hit
+ tween(self.target, {
+ tint: 0xFF0000
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ tween(self.target, {
+ tint: 0xFFFFFF
+ }, {
+ duration: 100
+ });
+ }
+ });
+ self.lastAttackTime = LK.ticks;
+ LK.getSound('attack').play();
+ }
+ };
+ self.update = function () {
+ if (self.isDead) {
+ return;
+ }
+ // Handle spawn delay
+ if (self.isSpawning) {
+ self.spawnTimer--;
+ if (self.spawnTimer <= 0) {
+ self.isSpawning = false;
+ self.spawnTimerText.setText('');
+ } else {
+ // Show remaining time in seconds
+ var secondsLeft = Math.ceil(self.spawnTimer / 60);
+ self.spawnTimerText.setText(secondsLeft.toString());
+ }
+ return; // Don't move or attack while spawning
+ }
+ // Handle stun effect
+ if (self.stunTimer > 0) {
+ self.stunTimer--;
+ if (self.stunTimer <= 0) {
+ self.isStunned = false;
+ }
+ return; // Don't move or attack while stunned
+ }
+ self.findTarget();
+ self.moveToTarget();
+ self.attack();
+ };
+ return self;
+});
var Tower = Container.expand(function (isPlayer, isKing) {
var self = Container.call(this);
self.isPlayer = isPlayer;
self.isKing = isKing;
@@ -2030,9 +2226,9 @@
var timerText, elixirText;
var battlefieldLine;
var showingDeckSelection = false;
var selectedDeck = ['archer', 'knight', 'giant', 'wizard', 'cannon']; // Default deck (minimum 5 cards)
-var availableCards = ['archer', 'knight', 'giant', 'wizard', 'cannon', 'skeleton', 'skeletonArmy', 'fireball', 'minion', 'maquinaVoladora', 'megaesbirro', 'discharge', 'bruja', 'ballesta'];
+var availableCards = ['archer', 'knight', 'giant', 'wizard', 'cannon', 'skeleton', 'skeletonArmy', 'fireball', 'minion', 'maquinaVoladora', 'megaesbirro', 'discharge', 'bruja', 'ballesta', 'montapuercos'];
// Card system variables
var activeCards = []; // Currently displayed 4 cards
var cardQueue = []; // Queue of remaining cards
var maxActiveCards = 4;
@@ -2192,8 +2388,13 @@
var minionIndex = availableCards.indexOf('minion');
var wizardIndex = availableCards.indexOf('wizard');
cardContainer.x = cardStartX + wizardIndex * cardSpacing;
cardContainer.y = 1100; // 100px below minion (200px total below wizard)
+ } else if (cardType === 'montapuercos') {
+ var dischargeIndex = availableCards.indexOf('discharge');
+ var cannonIndex = availableCards.indexOf('cannon');
+ cardContainer.x = cardStartX + cannonIndex * cardSpacing;
+ cardContainer.y = 1100; // 100px below discharge (200px total below cannon)
} else {
cardContainer.y = 900;
}
// Card background
@@ -2262,9 +2463,9 @@
scaleY: 0.6
});
cardContainer.addChild(cardIcon);
// Card cost
- var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : 3;
+ var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : cardType === 'montapuercos' ? 4 : 3;
var costText = new Text2(cost.toString(), {
size: 25,
fill: 0xFFFFFF
});
@@ -2541,9 +2742,9 @@
};
}
}
function deployUnit(cardType, x, y, isPlayer) {
- var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : 3;
+ var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : cardType === 'montapuercos' ? 4 : 3;
if (isPlayer && elixir < cost) {
return false;
}
if (!isPlayer && aiElixir < cost) {
@@ -2735,8 +2936,10 @@
// Deploy single bruja
unit = new Bruja(isPlayer);
} else if (cardType === 'ballesta') {
unit = new Ballesta(isPlayer);
+ } else if (cardType === 'montapuercos') {
+ unit = new Montapuercos(isPlayer);
} else if (cardType === 'cannon') {
unit = new Cannon(isPlayer);
}
if (unit) {
@@ -2771,10 +2974,10 @@
// Required cards (must have)
var requiredCards = {
aerial: 'archer',
// Card that can attack aerial units
- wincondition: 'giant',
- // Wincondition card
+ wincondition: Math.random() < 0.5 ? 'giant' : 'montapuercos',
+ // Wincondition card (randomly choose between giant and montapuercos)
spell: Math.random() < 0.5 ? 'fireball' : 'discharge',
// Spell card (randomly choose between fireball and discharge)
structure: Math.random() < 0.5 ? 'cannon' : 'ballesta' // Structure card (randomly choose between cannon and ballesta)
};
@@ -2783,9 +2986,9 @@
aiDeck.push(requiredCards.wincondition);
aiDeck.push(requiredCards.spell);
aiDeck.push(requiredCards.structure);
// Remaining card pool (excluding already added required cards)
- var remainingCards = ['knight', 'wizard', 'bruja', 'skeleton', 'skeletonArmy', 'minion', 'maquinaVoladora', 'megaesbirro', 'ballesta'];
+ var remainingCards = ['knight', 'wizard', 'bruja', 'skeleton', 'skeletonArmy', 'minion', 'maquinaVoladora', 'megaesbirro', 'ballesta', 'montapuercos'];
// Fill remaining 4 slots with random cards from remaining pool
while (aiDeck.length < 8) {
var randomIndex = Math.floor(Math.random() * remainingCards.length);
var randomCard = remainingCards[randomIndex];
@@ -2836,9 +3039,9 @@
if (aiElixir >= 3) {
// Use cards from AI's deck for defense
for (var d = 0; d < aiDeck.length; d++) {
var deckCard = aiDeck[d];
- var cardCost = deckCard === 'giant' ? 5 : deckCard === 'wizard' ? 5 : deckCard === 'bruja' ? 5 : deckCard === 'skeleton' ? 1 : deckCard === 'skeletonArmy' ? 3 : deckCard === 'fireball' ? 4 : deckCard === 'discharge' ? 2 : deckCard === 'minion' ? 3 : deckCard === 'maquinaVoladora' ? 4 : deckCard === 'megaesbirro' ? 3 : deckCard === 'ballesta' ? 6 : 3;
+ var cardCost = deckCard === 'giant' ? 5 : deckCard === 'wizard' ? 5 : deckCard === 'bruja' ? 5 : deckCard === 'skeleton' ? 1 : deckCard === 'skeletonArmy' ? 3 : deckCard === 'fireball' ? 4 : deckCard === 'discharge' ? 2 : deckCard === 'minion' ? 3 : deckCard === 'maquinaVoladora' ? 4 : deckCard === 'megaesbirro' ? 3 : deckCard === 'ballesta' ? 6 : deckCard === 'montapuercos' ? 4 : 3;
if (aiElixir >= cardCost) {
// Add defensive units (not spells for defense)
if (deckCard !== 'fireball' && deckCard !== 'discharge') {
defensiveCards.push(deckCard);
@@ -2966,28 +3169,28 @@
// 90% chance for big attack when having 8+ elixir
// Find wincondition card in AI deck
var winconditionCard = null;
for (var w = 0; w < aiDeck.length; w++) {
- if (aiDeck[w] === 'giant') {
- winconditionCard = 'giant';
+ if (aiDeck[w] === 'giant' || aiDeck[w] === 'montapuercos') {
+ winconditionCard = aiDeck[w];
break;
}
}
if (winconditionCard) {
// Deploy wincondition first
var deployX = 300 + Math.random() * 1448;
var deployY = 200 + Math.random() * 600;
if (deployUnit(winconditionCard, deployX, deployY, false)) {
- var winconditionCost = winconditionCard === 'giant' ? 5 : 3;
+ var winconditionCost = winconditionCard === 'giant' ? 5 : winconditionCard === 'montapuercos' ? 4 : 3;
aiElixir -= winconditionCost;
// Wait a bit then deploy support unit behind wincondition
LK.setTimeout(function () {
if (aiElixir >= 3) {
var supportCards = [];
// Get support cards from AI deck
for (var s = 0; s < aiDeck.length; s++) {
var supportCard = aiDeck[s];
- var supportCost = supportCard === 'giant' ? 5 : supportCard === 'wizard' ? 5 : supportCard === 'skeleton' ? 1 : supportCard === 'skeletonArmy' ? 3 : supportCard === 'fireball' ? 4 : supportCard === 'discharge' ? 2 : supportCard === 'minion' ? 3 : 3;
+ var supportCost = supportCard === 'giant' ? 5 : supportCard === 'wizard' ? 5 : supportCard === 'skeleton' ? 1 : supportCard === 'skeletonArmy' ? 3 : supportCard === 'fireball' ? 4 : supportCard === 'discharge' ? 2 : supportCard === 'minion' ? 3 : supportCard === 'montapuercos' ? 4 : 3;
if (aiElixir >= supportCost && supportCard !== 'fireball' && supportCard !== 'discharge' && supportCard !== winconditionCard) {
supportCards.push(supportCard);
}
}
@@ -3009,10 +3212,10 @@
if (Math.random() < 0.4) {
// 40% chance to use wincondition (reduced from 60% to encourage saving)
var winconditionCard = null;
for (var w = 0; w < aiDeck.length; w++) {
- if (aiDeck[w] === 'giant') {
- winconditionCard = 'giant';
+ if (aiDeck[w] === 'giant' || aiDeck[w] === 'montapuercos') {
+ winconditionCard = aiDeck[w];
break;
}
}
if (winconditionCard) {
@@ -3102,9 +3305,9 @@
// Event handlers for active deck cards only
for (var i = 0; i < deckCards.length; i++) {
var card = deckCards[i];
var cardType = card.cardType;
- var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : 3;
+ var cost = cardType === 'giant' ? 5 : cardType === 'wizard' ? 5 : cardType === 'bruja' ? 5 : cardType === 'skeleton' ? 1 : cardType === 'skeletonArmy' ? 3 : cardType === 'fireball' ? 4 : cardType === 'discharge' ? 2 : cardType === 'minion' ? 3 : cardType === 'maquinaVoladora' ? 4 : cardType === 'megaesbirro' ? 3 : cardType === 'ballesta' ? 6 : cardType === 'montapuercos' ? 4 : 3;
// Create closure to capture cardType and cost
(function (type, cardCost, index) {
card.down = function (x, y, obj) {
if (elixir >= cardCost) {