User prompt
por alguna razon, el caballero sigue detectando a la arquera como si fuera solo una, asi que cuando mata a una ignora a la otra, haz que tambien la detecte, que el juego la considere como otra. por cierto, subele el rango en el que las cartas se detectan las unas a las otras para atacarse en un 40%
User prompt
haz que el caballero tenga 825 de hp y bajale el daño a 160. haz que la arquera sean 2, con colocar la carta de la arquera aparecen 2, una a la izquierda y otra a la derecha. y haz que las arqueras tengan 425 hp y 75 de daño. bajale el daño a la torre de princesa y a la torre del rey a 75, ademas, haz que ataquen un 50% mas lento.
User prompt
la torre de en medio no debe poder atacar hasta que al menos una torre de su equipo haya sido destruido. por cierto, informame del daño y vida de las cartas y torres
User prompt
el jugador no puede colocar cartas... arreglalo
User prompt
por alguna razon el jugador no puede colocar cartas. por cierto, haz que las torres tengan un 300% mas de rango, y que el caballero tenga un 50% mas de rango y las arqueras un 200% mas de rango
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var globalPos = obj.parent.toGlobal(obj.position);' Line Number: 442
Code edit (1 edits merged)
Please save this source code
User prompt
Tower Defense Royale
Initial prompt
Vas a crear un juego inspirado en “Clash Royale”. Para que lo entiendas, aquí tienes una explicación completa de cómo funciona el juego original y luego cómo debe ser nuestra versión simplificada. 1) Qué es Clash Royale (explicación para alguien que nunca lo ha visto) Es un juego de estrategia en tiempo real para dispositivos móviles donde dos jugadores luchan en una arena vertical dividida en dos mitades. Cada jugador tiene torres defensivas que debe proteger y un mazo de cartas con unidades que puede desplegar en su mitad del campo. El objetivo original es derribar las torres del enemigo antes de que él derribe las tuyas. Las partidas duran unos minutos y el jugador que derriba más torres gana. Cada carta tiene un coste de elixir, un recurso que se regenera con el tiempo. Hay distintos tipos de cartas: unidades que caminan hacia el enemigo, hechizos, edificaciones, etc. En el juego original, hay un río en medio de la arena, pero en nuestra versión simplificada NO habrá río ni puentes. El camino será directo. 2) Objetivo de nuestra versión simplificada Crear un juego PvE (jugador contra bot) basado en Clash Royale. La partida dura exactamente 2 minutos. Gana quien derribe más torres enemigas. Si hay empate en torres derribadas, gana quien tenga más vida total en sus torres. Si también hay empate en vida, se declara empate. No hay cofres, clanes, recompensas, menús de progreso ni personalización de cartas. Solo se juega la partida. 3) Arena de batalla Arena rectangular, simétrica, sin obstáculos ni río. Línea central invisible divide las mitades de los jugadores (solo para limitar dónde se pueden desplegar cartas). Dimensiones aproximadas: ancho 36 unidades, alto 54 unidades, centro en (0,0). Mitad jugador humano: y ∈ [-27,0) Mitad bot: y ∈ (0,27] 4) Torres Cada lado tiene 3 torres: 2 torres laterales (princesa) y 1 torre central (rey). Posición de torres Jugador humano (abajo) Princesa izquierda: (-10, -22) Princesa derecha: (10, -22) Rey central: (0, -25) Bot (arriba) Princesa izquierda: (-10, 22) Princesa derecha: (10, 22) Rey central: (0, 25) Stats torres Princesa: vida 2400, daño 90, ataque cada 0,8 s, alcance 7,5, objetivos tierra y aire Rey: vida 3700, daño 120, ataque cada 1 s, alcance 7,5, objetivos tierra y aire 5) Cartas disponibles Solo hay 2 cartas en este prototipo: Arqueras y Caballero. Carta 1: Arqueras Coste: 3 elixir Invoca 2 arqueras Vida: 340 Daño: 110 Ataque cada 1,2 s Alcance: 5 unidades Objetivos: tierra y aire Movimiento: medio Comportamiento: caminan hacia torres enemigas y atacan enemigos en rango. Carta 2: Caballero Coste: 3 elixir Invoca 1 caballero Vida: 1600 Daño: 170 Ataque cuerpo a cuerpo (corto) Movimiento: medio-lento Objetivos: tierra Comportamiento: avanza hacia la torre más cercana y ataca cualquier enemigo en el camino. 6) Elixir Barra de elixir igual que Clash Royale Máximo: 10 Se regenera 1 punto cada 2,8 s Ambos jugadores (humano y bot) comparten misma velocidad de generación 7) Mecánica de la partida El jugador puede colocar cartas en su mitad del campo. El bot usa IA básica: coloca cartas aleatoriamente (Arqueras o Caballero) en su mitad si tiene elixir suficiente, cada 4–6 segundos aproximadamente. Las unidades avanzan automáticamente hacia la torre enemiga más cercana. Las torres atacan automáticamente cualquier unidad enemiga que entre en su rango. 8) Condición de victoria Cronómetro llega a 0:00 → gana el jugador con más torres derribadas. Empate en torres → gana quien tenga más vida restante en sus torres. Empate también en vida → se declara empate. 9) Simplificaciones Solo partidas rápidas contra bot. Solo dos cartas disponibles. No hay río, cofres, clanes, personalización, progresión ni menús complejos. como no se puede hacer un servidor online, haremos que el jugador siempre juegue contra un bot, cpu
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ 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 Card = Container.expand(function (cardType) { var self = Container.call(this); self.cardType = cardType; self.cost = 3; var cardBg = self.attachAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5 }); var cardIcon = LK.getAsset(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 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 = 200; self.damage = 150; self.attackSpeed = 45; 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; 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; var unitGraphics = self.attachAsset(unitType, { anchorX: 0.5, anchorY: 0.5 }); // Health bar background var healthBarBg = LK.getAsset('cardSlot', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, 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.2, scaleY: 0.1, y: -50 }); healthBar.tint = self.isPlayer ? 0x4CAF50 : 0xF44336; self.addChild(healthBar); self.healthBar = healthBar; 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 = 0.2 * healthPercent; // Flash red when taking damage LK.effects.flashObject(self, 0xFF0000, 300); }; self.findTarget = function () { var closestDistance = Infinity; var closestTarget = null; // Find closest enemy tower 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]; } } } // Also check for enemy units in range 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]; } } } self.target = closestTarget; }; self.moveToTarget = function () { if (!self.target || self.target.isDead) { self.findTarget(); 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; 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) { // Create bullet 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; self.findTarget(); self.moveToTarget(); self.attack(); }; return self; }); var Knight = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'knight', 800, 180, 60, 80, 2); return self; }); var Archer = Unit.expand(function (isPlayer) { var self = Unit.call(this, isPlayer, 'archer', 300, 120, 90, 180, 1.5); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ var gameTime = 120; // 2 minutes in seconds var elixir = 5; // Starting elixir var maxElixir = 10; var elixirRegenRate = 168; // Ticks for 2.8 seconds at 60fps var lastElixirRegen = 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 // Create battlefield divider var 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 var timerText = new Text2(gameTime.toString(), { size: 60, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); var elixirText = new Text2(elixir + "/" + maxElixir, { size: 40, fill: 0xE91E63 }); elixirText.anchor.set(0, 1); elixirText.x = 50; LK.gui.bottomLeft.addChild(elixirText); // Cards var archerCard = LK.gui.bottom.addChild(new Card('archer')); archerCard.x = -200; archerCard.y = -100; var knightCard = LK.gui.bottom.addChild(new Card('knight')); knightCard.x = 200; knightCard.y = -100; // Dragging variables var draggedCard = null; var dragOffset = { x: 0, y: 0 }; function deployUnit(cardType, x, y, isPlayer) { if (isPlayer && elixir < 3) return false; // Check if position is in correct half 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') { unit = new Archer(isPlayer); } else if (cardType === 'knight') { unit = new Knight(isPlayer); } if (unit) { unit.x = x; unit.y = y; if (isPlayer) { playerUnits.push(unit); elixir -= 3; elixirText.setText(elixir + "/" + maxElixir); } else { enemyUnits.push(unit); } game.addChild(unit); LK.getSound('deploy').play(); return true; } return false; } function aiDeploy() { if (LK.ticks - aiLastDeploy > aiDeployInterval && Math.random() < 0.7) { var cardTypes = ['archer', 'knight']; var randomCard = cardTypes[Math.floor(Math.random() * cardTypes.length)]; var randomX = 300 + Math.random() * 1448; var randomY = 200 + Math.random() * 1000; if (deployUnit(randomCard, randomX, randomY, false)) { aiLastDeploy = LK.ticks; // Randomize next deploy interval (4-6 seconds) 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 } } } } // Event handlers game.down = function (x, y, obj) { // Convert GUI coordinates to game coordinates for cards var globalPos = obj.parent.toGlobal(obj.position); var gamePos = game.toLocal(globalPos); if (gamePos.y > 2200) { // Bottom area where cards are if (gamePos.x > 824 - 200 - 150 && gamePos.x < 824 - 200 + 150) { // Archer card clicked if (elixir >= 3) { draggedCard = 'archer'; } } else if (gamePos.x > 824 + 200 - 150 && gamePos.x < 824 + 200 + 150) { // Knight card clicked if (elixir >= 3) { draggedCard = 'knight'; } } } }; game.up = function (x, y, obj) { if (draggedCard) { if (deployUnit(draggedCard, x, y, true)) { // Unit deployed successfully } draggedCard = null; } }; game.update = function () { if (gameEnded) return; // Update timer if (LK.ticks % 60 === 0 && gameTime > 0) { gameTime--; timerText.setText(gameTime.toString()); } // Regenerate elixir if (LK.ticks - lastElixirRegen >= elixirRegenRate && elixir < maxElixir) { elixir++; elixirText.setText(elixir + "/" + maxElixir); lastElixirRegen = 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
@@ -1,6 +1,499 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+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 Card = Container.expand(function (cardType) {
+ var self = Container.call(this);
+ self.cardType = cardType;
+ self.cost = 3;
+ var cardBg = self.attachAsset('cardSlot', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ var cardIcon = LK.getAsset(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 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 = 200;
+ self.damage = 150;
+ self.attackSpeed = 45;
+ 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;
+ 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;
+ var unitGraphics = self.attachAsset(unitType, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Health bar background
+ var healthBarBg = LK.getAsset('cardSlot', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.2,
+ 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.2,
+ scaleY: 0.1,
+ y: -50
+ });
+ healthBar.tint = self.isPlayer ? 0x4CAF50 : 0xF44336;
+ self.addChild(healthBar);
+ self.healthBar = healthBar;
+ 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 = 0.2 * healthPercent;
+ // Flash red when taking damage
+ LK.effects.flashObject(self, 0xFF0000, 300);
+ };
+ self.findTarget = function () {
+ var closestDistance = Infinity;
+ var closestTarget = null;
+ // Find closest enemy tower
+ 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];
+ }
+ }
+ }
+ // Also check for enemy units in range
+ 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];
+ }
+ }
+ }
+ self.target = closestTarget;
+ };
+ self.moveToTarget = function () {
+ if (!self.target || self.target.isDead) {
+ self.findTarget();
+ 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;
+ 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) {
+ // Create bullet
+ 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;
+ self.findTarget();
+ self.moveToTarget();
+ self.attack();
+ };
+ return self;
+});
+var Knight = Unit.expand(function (isPlayer) {
+ var self = Unit.call(this, isPlayer, 'knight', 800, 180, 60, 80, 2);
+ return self;
+});
+var Archer = Unit.expand(function (isPlayer) {
+ var self = Unit.call(this, isPlayer, 'archer', 300, 120, 90, 180, 1.5);
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x228B22
+});
+
+/****
+* Game Code
+****/
+var gameTime = 120; // 2 minutes in seconds
+var elixir = 5; // Starting elixir
+var maxElixir = 10;
+var elixirRegenRate = 168; // Ticks for 2.8 seconds at 60fps
+var lastElixirRegen = 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
+// Create battlefield divider
+var 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
+var timerText = new Text2(gameTime.toString(), {
+ size: 60,
+ fill: 0xFFFFFF
+});
+timerText.anchor.set(0.5, 0);
+LK.gui.top.addChild(timerText);
+var elixirText = new Text2(elixir + "/" + maxElixir, {
+ size: 40,
+ fill: 0xE91E63
+});
+elixirText.anchor.set(0, 1);
+elixirText.x = 50;
+LK.gui.bottomLeft.addChild(elixirText);
+// Cards
+var archerCard = LK.gui.bottom.addChild(new Card('archer'));
+archerCard.x = -200;
+archerCard.y = -100;
+var knightCard = LK.gui.bottom.addChild(new Card('knight'));
+knightCard.x = 200;
+knightCard.y = -100;
+// Dragging variables
+var draggedCard = null;
+var dragOffset = {
+ x: 0,
+ y: 0
+};
+function deployUnit(cardType, x, y, isPlayer) {
+ if (isPlayer && elixir < 3) return false;
+ // Check if position is in correct half
+ 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') {
+ unit = new Archer(isPlayer);
+ } else if (cardType === 'knight') {
+ unit = new Knight(isPlayer);
+ }
+ if (unit) {
+ unit.x = x;
+ unit.y = y;
+ if (isPlayer) {
+ playerUnits.push(unit);
+ elixir -= 3;
+ elixirText.setText(elixir + "/" + maxElixir);
+ } else {
+ enemyUnits.push(unit);
+ }
+ game.addChild(unit);
+ LK.getSound('deploy').play();
+ return true;
+ }
+ return false;
+}
+function aiDeploy() {
+ if (LK.ticks - aiLastDeploy > aiDeployInterval && Math.random() < 0.7) {
+ var cardTypes = ['archer', 'knight'];
+ var randomCard = cardTypes[Math.floor(Math.random() * cardTypes.length)];
+ var randomX = 300 + Math.random() * 1448;
+ var randomY = 200 + Math.random() * 1000;
+ if (deployUnit(randomCard, randomX, randomY, false)) {
+ aiLastDeploy = LK.ticks;
+ // Randomize next deploy interval (4-6 seconds)
+ 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
+ }
+ }
+ }
+}
+// Event handlers
+game.down = function (x, y, obj) {
+ // Convert GUI coordinates to game coordinates for cards
+ var globalPos = obj.parent.toGlobal(obj.position);
+ var gamePos = game.toLocal(globalPos);
+ if (gamePos.y > 2200) {
+ // Bottom area where cards are
+ if (gamePos.x > 824 - 200 - 150 && gamePos.x < 824 - 200 + 150) {
+ // Archer card clicked
+ if (elixir >= 3) {
+ draggedCard = 'archer';
+ }
+ } else if (gamePos.x > 824 + 200 - 150 && gamePos.x < 824 + 200 + 150) {
+ // Knight card clicked
+ if (elixir >= 3) {
+ draggedCard = 'knight';
+ }
+ }
+ }
+};
+game.up = function (x, y, obj) {
+ if (draggedCard) {
+ if (deployUnit(draggedCard, x, y, true)) {
+ // Unit deployed successfully
+ }
+ draggedCard = null;
+ }
+};
+game.update = function () {
+ if (gameEnded) return;
+ // Update timer
+ if (LK.ticks % 60 === 0 && gameTime > 0) {
+ gameTime--;
+ timerText.setText(gameTime.toString());
+ }
+ // Regenerate elixir
+ if (LK.ticks - lastElixirRegen >= elixirRegenRate && elixir < maxElixir) {
+ elixir++;
+ elixirText.setText(elixir + "/" + maxElixir);
+ lastElixirRegen = 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();
+};
\ No newline at end of file