User prompt
cuando ganas la wave 13 ganas el juego y te devuelve a el menu y te agrega 1 victoria ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
que el fondo del menu sea mitad cielo mitad infierto y que ocupe toda la pantalla
User prompt
mas arriba
User prompt
pone la image llamada icon_menu arriba de el titulo del menu
User prompt
pone la image llamada icon_menu arriba de el titulo del menu
User prompt
cambia el texto tower defense por: Divine Defense: Battle for Souls
User prompt
haz un menu
User prompt
que no se pueda poner torres arriba de las torres para comprar
User prompt
en la wave 13, la ultima, van a salir 6 milei, 6 trump. 6 devil y 10 demon_fast
User prompt
cuando llegan los diablos al final se reinicia el juego, osea las torres que pusimos desapareceny volvemos a tener la plata con la que iniciamos
User prompt
en la wave 2 aparezcan 2 demon_fast junto a los otros y en la wave 3 aparezca trump, en la 5 milei , en la 6 el diablo con 7 demon_fast y en la 7 milei trump y el diablo con 3 demon fast
User prompt
cuando llegan los diablos a el final se reinicia la oleada
User prompt
mas abajo los textos
User prompt
separame las torres y subilas un poquito
User prompt
mas grande los textos de cuanto cuesta cada torre y los textos en negro
User prompt
mas grande los textos
User prompt
en la tienda diga cuanto cuesta cada torre
User prompt
pero pone cuanto dinero cuesta cada torre y cuanto dinero tengo yo
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'fill')' in or related to this line: 'buddhaCostText.style.fill = 0xFFD700; // Gold for affordable' Line Number: 473
User prompt
pero una interfaz con el dinero que cuesta cada torre
User prompt
quiero un boton de tienda para comprar a las torres
Code edit (1 edits merged)
Please save this source code
User prompt
Divine Defense: Battle for Souls
Initial prompt
tower defense buda, cristo y angeles vs el diablo, milei y trump
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { victories: 0, rebirthLevel: 0, endlessRecord: 0, leaderboard: [], currentPlayerName: "" }); /**** * Classes ****/ var Enemy = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'basic'; self.health = 50; self.maxHealth = 50; self.speed = 1; self.reward = 10; self.pathIndex = 0; self.pathProgress = 0; if (self.type === 'basic') { self.health = 50; self.maxHealth = 50; self.speed = 1; self.reward = 25; self.graphics = self.attachAsset('demon_basic', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'fast') { self.health = 30; self.maxHealth = 30; self.speed = 2; self.reward = 25; self.graphics = self.attachAsset('demon_fast', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'devil') { self.health = 200; self.maxHealth = 200; self.speed = 0.5; self.reward = 50; self.graphics = self.attachAsset('boss_devil', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'milei') { self.health = 150; self.maxHealth = 150; self.speed = 1.2; self.reward = 50; self.graphics = self.attachAsset('boss_milei', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'trump') { self.health = 180; self.maxHealth = 180; self.speed = 0.8; self.reward = 50; self.graphics = self.attachAsset('boss_trump', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'cervero') { self.health = 666; self.maxHealth = 666; self.speed = 0.3; self.reward = 1000; self.graphics = self.attachAsset('cervero_boss_final', { anchorX: 0.5, anchorY: 0.5 }); } self.takeDamage = function (damage) { self.health -= damage; // Flash red when hit var originalTint = self.graphics.tint; self.graphics.tint = 0xFF0000; LK.setTimeout(function () { if (self.graphics) { self.graphics.tint = originalTint; } }, 100); LK.getSound('enemy_hit').play(); if (self.health <= 0) { self.die(); } }; self.die = function () { // Award coins coins += self.reward; updateUI(); // Remove from enemies array var index = enemies.indexOf(self); if (index > -1) { enemies.splice(index, 1); } // Destroy the enemy self.destroy(); }; self.update = function () { // Move along path if (self.pathIndex < gamePath.length) { var targetPoint = gamePath[self.pathIndex]; var dx = targetPoint.x - self.x; var dy = targetPoint.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.pathIndex++; if (self.pathIndex >= gamePath.length) { // Reached end, update leaderboard if in endless mode if (endlessMode) { updateLeaderboard(wave); } // Reset entire game // Remove all towers for (var i = towers.length - 1; i >= 0; i--) { towers[i].destroy(); towers.splice(i, 1); } // Remove all enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); enemies.splice(i, 1); } // Remove all projectiles for (var i = projectiles.length - 1; i >= 0; i--) { projectiles[i].destroy(); projectiles.splice(i, 1); } // Reset all game variables to starting state coins = 100; wave = 1; enemiesSpawned = 0; spawnTimer = 0; waveComplete = false; enemiesInWave = 5; selectedTower = null; towerInfoPanel.visible = false; towerSelectPanel.visible = false; updateUI(); // Remove this enemy self.destroy(); return; } } else { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } } }; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); self.speed = 8; self.damage = 20; self.target = null; self.graphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (!self.target || enemies.indexOf(self.target) === -1) { // Target is gone, remove projectile var index = projectiles.indexOf(self); if (index > -1) { projectiles.splice(index, 1); } self.destroy(); return; } // Move towards target 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) { // Hit target self.target.takeDamage(self.damage); var index = projectiles.indexOf(self); if (index > -1) { projectiles.splice(index, 1); } self.destroy(); } else { var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; return self; }); var Tower = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'buddha'; self.level = 1; self.damage = 20; self.range = 150; self.fireRate = 60; // frames between shots self.lastShot = 0; self.cost = 50; self.lastClickTime = 0; self.clickCount = 0; self.seraphine = null; // Set tower specific properties if (self.type === 'buddha') { self.damage = 15; self.range = 120; self.fireRate = 90; self.cost = 50; self.graphics = self.attachAsset('buddha_tower', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'cristo') { self.damage = 30; self.range = 200; self.fireRate = 120; self.cost = 75; self.graphics = self.attachAsset('cristo_tower', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'angel') { self.damage = 25; self.range = 180; self.fireRate = 45; self.cost = 60; self.graphics = self.attachAsset('angel_tower', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.type === 'seraphine') { self.damage = 100; self.range = 250; self.fireRate = 30; self.cost = 1111; self.graphics = self.attachAsset('seraphine', { anchorX: 0.5, anchorY: 0.5 }); } // Apply rebirth damage multiplier self.damage = Math.floor(self.damage * getDamageMultiplier()); self.canShoot = function () { return LK.ticks - self.lastShot >= self.fireRate; }; self.findTarget = function () { var closestEnemy = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2)); if (distance <= self.range && distance < closestDistance) { closestDistance = distance; closestEnemy = enemy; } } return closestEnemy; }; self.shoot = function (target) { if (!self.canShoot()) return; var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.target = target; projectile.damage = self.damage; projectiles.push(projectile); game.addChild(projectile); self.lastShot = LK.ticks; LK.getSound('tower_shoot').play(); }; self.update = function () { var target = self.findTarget(); if (target) { self.shoot(target); } }; self.down = function (x, y, obj) { var currentTime = Date.now(); var timeDiff = currentTime - self.lastClickTime; // Check for double click (within 500ms) if (timeDiff < 500 && self.clickCount === 1) { // Double click detected - delete tower and refund half cost var refund = Math.floor(self.cost / 2); coins += refund; updateUI(); // Remove from towers array var index = towers.indexOf(self); if (index > -1) { towers.splice(index, 1); } // Clear selection if this tower was selected if (selectedTower === self) { selectedTower = null; towerInfoPanel.visible = false; } // Destroy seraphine if it exists (only for seraphine tower type) if (self.seraphine && self.type === 'seraphine') { self.seraphine.destroy(); } // Destroy the tower self.destroy(); return; } // Single click logic if (selectedTower === self) { selectedTower = null; towerInfoPanel.visible = false; } else { selectedTower = self; showTowerInfo(); } // Update click tracking self.lastClickTime = currentTime; self.clickCount = timeDiff < 500 ? self.clickCount + 1 : 1; // Reset click count after delay LK.setTimeout(function () { if (self.clickCount > 0) { self.clickCount = 0; } }, 500); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ // Game variables var towers = []; var enemies = []; var projectiles = []; var selectedTower = null; var selectedTowerType = null; // Start with no tower selected var coins = 100; var lives = 20; var wave = 1; var enemiesSpawned = 0; var enemiesInWave = 5; var spawnTimer = 0; var spawnDelay = 60; var waveComplete = false; var gameStarted = false; var showMenu = true; var endlessMode = false; var puertaInf = null; var escalerasCiel = null; // Rebirth system function getDamageMultiplier() { var rebirthLevel = storage.rebirthLevel || 0; if (rebirthLevel === 0) return 1; if (rebirthLevel === 1) return 1.05; // 5% more damage if (rebirthLevel === 2) return 1.15; // 15% more damage if (rebirthLevel === 3) return 6; // 6x damage return 6; // Cap at 6x } function getRebirthRequirement() { var rebirthLevel = storage.rebirthLevel || 0; if (rebirthLevel === 0) return 15; if (rebirthLevel === 1) return 30; if (rebirthLevel === 2) return 100; return -1; // No more rebirths } function canRebirth() { var required = getRebirthRequirement(); return required !== -1 && (storage.victories || 0) >= required; } // Game path var gamePath = [{ x: 0, y: 400 }, { x: 300, y: 400 }, { x: 300, y: 200 }, { x: 600, y: 200 }, { x: 600, y: 500 }, { x: 900, y: 500 }, { x: 900, y: 300 }, { x: 1200, y: 300 }, { x: 1200, y: 600 }, { x: 1500, y: 600 }, { x: 1500, y: 100 }, { x: 2048, y: 100 }]; // Draw path markers function createPath() { for (var i = 0; i < gamePath.length - 1; i++) { var marker = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); marker.x = gamePath[i].x; marker.y = gamePath[i].y; game.addChild(marker); } // Add puerta_inf at enemy spawn point (start of path) puertaInf = LK.getAsset('puerta_inf', { anchorX: 0.5, anchorY: 0.5 }); puertaInf.x = gamePath[0].x + 100; puertaInf.y = gamePath[0].y; puertaInf.visible = false; // Hide initially since menu is showing game.addChild(puertaInf); // Add escaleras_ciel at enemy destination (end of path) escalerasCiel = LK.getAsset('escaleras_ciel', { anchorX: 0.5, anchorY: 0.5 }); escalerasCiel.x = gamePath[gamePath.length - 1].x - 50; escalerasCiel.y = gamePath[gamePath.length - 1].y + 50; escalerasCiel.visible = false; // Hide initially since menu is showing game.addChild(escalerasCiel); } // UI Elements var coinsText = new Text2('Coins: 100', { size: 60, fill: 0xFFD700 }); coinsText.anchor.set(0, 0); LK.gui.topRight.addChild(coinsText); var livesText = new Text2('Lives: 20', { size: 60, fill: 0xFF0000 }); livesText.anchor.set(0, 0); livesText.y = 70; LK.gui.topRight.addChild(livesText); var waveText = new Text2('Wave: 1', { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); waveText.visible = false; // Hide initially since menu is showing LK.gui.top.addChild(waveText); var towerCountText = new Text2('Towers placed: 0/7', { size: 60, fill: 0xFFFFFF }); towerCountText.anchor.set(0.5, 0); towerCountText.y = 70; towerCountText.visible = false; // Hide initially since menu is showing LK.gui.top.addChild(towerCountText); // Tower selection UI var towerSelectPanel = new Container(); towerSelectPanel.x = 50; towerSelectPanel.y = 2100; towerSelectPanel.visible = false; // Initially hidden // Shop button var shopButton = new Container(); var shopBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1, alpha: 0.8 }); shopButton.addChild(shopBg); var shopText = new Text2('SHOP', { size: 50, fill: 0xFFFFFF }); shopText.anchor.set(0.5, 0.5); shopButton.addChild(shopText); shopButton.x = 150; shopButton.y = 2600; game.addChild(shopButton); // Current money display near shop var currentMoneyText = new Text2('Money: $' + coins, { size: 55, fill: 0x00FF00 }); currentMoneyText.anchor.set(0.5, 0.5); currentMoneyText.x = 150; currentMoneyText.y = 2500; game.addChild(currentMoneyText); var buddhaButton = LK.getAsset('buddha_tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); buddhaButton.x = 120; buddhaButton.y = 0; towerSelectPanel.addChild(buddhaButton); var cristoButton = LK.getAsset('cristo_tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); cristoButton.x = 300; cristoButton.y = 0; towerSelectPanel.addChild(cristoButton); var angelButton = LK.getAsset('angel_tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); angelButton.x = 480; angelButton.y = 0; towerSelectPanel.addChild(angelButton); var seraphineButton = LK.getAsset('seraphine', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); seraphineButton.x = 660; seraphineButton.y = 0; towerSelectPanel.addChild(seraphineButton); // Define tower costs var towerCosts = { buddha: 50, cristo: 75, angel: 60, seraphine: 1111 }; // Add cost labels for towers var buddhaCostText = new Text2('Buddha\n$' + towerCosts.buddha, { size: 50, fill: 0x000000 }); buddhaCostText.anchor.set(0.5, 0); buddhaCostText.x = 120; buddhaCostText.y = 120; towerSelectPanel.addChild(buddhaCostText); var cristoCostText = new Text2('Cristo\n$' + towerCosts.cristo, { size: 50, fill: 0x000000 }); cristoCostText.anchor.set(0.5, 0); cristoCostText.x = 300; cristoCostText.y = 120; towerSelectPanel.addChild(cristoCostText); var angelCostText = new Text2('Angel\n$' + towerCosts.angel, { size: 50, fill: 0x000000 }); angelCostText.anchor.set(0.5, 0); angelCostText.x = 480; angelCostText.y = 120; towerSelectPanel.addChild(angelCostText); var seraphineCostText = new Text2('Seraphine\n$' + towerCosts.seraphine, { size: 50, fill: 0x000000 }); seraphineCostText.anchor.set(0.5, 0); seraphineCostText.x = 660; seraphineCostText.y = 120; towerSelectPanel.addChild(seraphineCostText); game.addChild(towerSelectPanel); // Tower info panel var towerInfoPanel = new Container(); towerInfoPanel.visible = false; var towerInfoBg = LK.getAsset('path_marker', { anchorX: 0, anchorY: 0, scaleX: 4, scaleY: 3, alpha: 0.8 }); towerInfoPanel.addChild(towerInfoBg); var towerInfoText = new Text2('Tower Info', { size: 45, fill: 0xFFFFFF }); towerInfoText.x = 20; towerInfoText.y = 20; towerInfoPanel.addChild(towerInfoText); towerInfoPanel.x = 1600; towerInfoPanel.y = 300; game.addChild(towerInfoPanel); function updateUI() { if (!showMenu) { towerCountText.setText('Towers placed: ' + towers.length + '/7'); } coinsText.setText('Coins: ' + coins); livesText.setText('Lives: ' + lives); waveText.setText('Wave: ' + wave); currentMoneyText.setText('Money: $' + coins); // Update tower affordability visual feedback if (coins >= towerCosts.buddha) { buddhaCostText.fill = 0x00FF00; // Green for affordable buddhaButton.alpha = 1.0; } else { buddhaCostText.fill = 0xFF0000; // Red for unaffordable buddhaButton.alpha = 0.5; } if (coins >= towerCosts.cristo) { cristoCostText.fill = 0x00FF00; // Green for affordable cristoButton.alpha = 1.0; } else { cristoCostText.fill = 0xFF0000; // Red for unaffordable cristoButton.alpha = 0.5; } if (coins >= towerCosts.angel) { angelCostText.fill = 0x00FF00; // Green for affordable angelButton.alpha = 1.0; } else { angelCostText.fill = 0xFF0000; // Red for unaffordable angelButton.alpha = 0.5; } if (coins >= towerCosts.seraphine) { seraphineCostText.fill = 0x00FF00; // Green for affordable seraphineButton.alpha = 1.0; } else { seraphineCostText.fill = 0xFF0000; // Red for unaffordable seraphineButton.alpha = 0.5; } } function showTowerInfo() { if (selectedTower) { towerInfoPanel.visible = true; towerInfoText.setText('Type: ' + selectedTower.type + '\nLevel: ' + selectedTower.level + '\nDamage: ' + selectedTower.damage); } } function spawnEnemy() { var enemyType = 'basic'; var spawnCount = 1; var spawnTypes = []; // Specific wave configurations if (wave === 2) { // Wave 2: 2 demon_fast + others (basics) if (enemiesSpawned < 2) { spawnTypes = ['fast', 'fast']; } else { spawnTypes = ['basic']; } } else if (wave === 3) { // Wave 3: trump appears if (enemiesSpawned === 0) { spawnTypes = ['trump']; } else { spawnTypes = ['basic']; } } else if (wave === 5) { // Wave 5: milei appears if (enemiesSpawned === 0) { spawnTypes = ['milei']; } else { spawnTypes = ['basic']; } } else if (wave === 6) { // Wave 6: devil with 7 demon_fast if (enemiesSpawned === 0) { spawnTypes = ['devil']; } else if (enemiesSpawned < 8) { spawnTypes = ['fast']; } else { spawnTypes = ['basic']; } } else if (wave === 7) { // Wave 7: milei, trump, devil with 3 demon_fast if (enemiesSpawned === 0) { spawnTypes = ['milei']; } else if (enemiesSpawned === 1) { spawnTypes = ['trump']; } else if (enemiesSpawned === 2) { spawnTypes = ['devil']; } else if (enemiesSpawned < 6) { spawnTypes = ['fast']; } else { spawnTypes = ['basic']; } } else if (wave === 13) { // Wave 13: 6 trump, 6 milei, 6 devil, 12 demon, 7 fast demon and 1 cervero if (enemiesSpawned < 6) { spawnTypes = ['trump']; } else if (enemiesSpawned < 12) { spawnTypes = ['milei']; } else if (enemiesSpawned < 18) { spawnTypes = ['devil']; } else if (enemiesSpawned < 30) { spawnTypes = ['basic']; } else if (enemiesSpawned < 37) { spawnTypes = ['fast']; } else if (enemiesSpawned < 38) { spawnTypes = ['cervero']; } else { spawnTypes = ['basic']; } } else if (wave === 15) { // Wave 15: Cervero boss final appears if (enemiesSpawned === 0) { spawnTypes = ['cervero']; } else { spawnTypes = ['basic']; } } else { // Original logic for other waves if (wave % 10 === 0) { if (wave === 10) enemyType = 'milei';else if (wave === 20) enemyType = 'trump';else if (wave === 30) enemyType = 'devil';else enemyType = Math.random() < 0.3 ? 'devil' : Math.random() < 0.5 ? 'milei' : 'trump'; } else if (wave > 5) { enemyType = Math.random() < 0.6 ? 'basic' : 'fast'; } spawnTypes = [enemyType]; } // Spawn the determined enemy types for (var i = 0; i < spawnTypes.length; i++) { var enemy = new Enemy(spawnTypes[i]); enemy.x = gamePath[0].x; enemy.y = gamePath[0].y; enemies.push(enemy); game.addChild(enemy); } enemiesSpawned++; } function canPlaceTower(x, y) { // Check if position is too close to path for (var i = 0; i < gamePath.length; i++) { var pathPoint = gamePath[i]; var distance = Math.sqrt(Math.pow(x - pathPoint.x, 2) + Math.pow(y - pathPoint.y, 2)); if (distance < 60) { return false; } } // Check if position is too close to existing towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var distance = Math.sqrt(Math.pow(x - tower.x, 2) + Math.pow(y - tower.y, 2)); if (distance < 180) { // Increased minimum distance to prevent overlap return false; } } // Prevent placing towers on shop button area var shopButtonDistance = Math.sqrt(Math.pow(x - 150, 2) + Math.pow(y - 2600, 2)); if (shopButtonDistance < 150) { return false; } // Prevent placing towers on current money text area var moneyTextDistance = Math.sqrt(Math.pow(x - 150, 2) + Math.pow(y - 2500, 2)); if (moneyTextDistance < 100) { return false; } // Prevent placing towers on tower selection panel when visible if (towerSelectPanel.visible) { // Check if position overlaps with tower selection buttons if (y >= 2100 - 100 && y <= 2100 + 200 && x >= 50 && x <= 580) { return false; } } return true; } function startNextWave() { wave++; enemiesSpawned = 0; if (wave === 13) { enemiesInWave = 38; // 6 trump + 6 milei + 6 devil + 12 demon + 7 fast demon + 1 cervero } else { enemiesInWave = Math.min(5 + Math.floor(wave / 2), 20); } spawnDelay = Math.max(30, 60 - wave * 2); waveComplete = false; updateUI(); } // Event handlers buddhaButton.down = function () { selectedTowerType = 'buddha'; selectedTower = null; towerInfoPanel.visible = false; }; cristoButton.down = function () { selectedTowerType = 'cristo'; selectedTower = null; towerInfoPanel.visible = false; }; angelButton.down = function () { selectedTowerType = 'angel'; selectedTower = null; towerInfoPanel.visible = false; }; seraphineButton.down = function () { selectedTowerType = 'seraphine'; selectedTower = null; towerInfoPanel.visible = false; }; shopButton.down = function () { towerSelectPanel.visible = !towerSelectPanel.visible; selectedTower = null; towerInfoPanel.visible = false; }; game.down = function (x, y, obj) { // Don't allow tower placement while menu is showing if (showMenu) return; // Only place towers in game area (not on UI) if (y > 2100) return; // Check if a tower type is selected if (selectedTowerType === null) return; // Check tower limit (maximum 7 towers) if (towers.length >= 7) return; var towerCost = towerCosts[selectedTowerType]; if (selectedTower === null && coins >= towerCost && canPlaceTower(x, y)) { var newTower = new Tower(selectedTowerType); newTower.x = x; newTower.y = y; towers.push(newTower); game.addChild(newTower); coins -= towerCost; // Show seraphine announcement if seraphine tower was placed if (selectedTowerType === 'seraphine') { var seraphineAnnouncement = new Text2('seraphine is here', { size: 120, fill: 0xFF0000 }); seraphineAnnouncement.anchor.set(0.5, 0.5); seraphineAnnouncement.x = 2048 / 2; seraphineAnnouncement.y = 2732 / 2; game.addChild(seraphineAnnouncement); // Create trembling effect var originalX = seraphineAnnouncement.x; var originalY = seraphineAnnouncement.y; var trembleTimer = LK.setInterval(function () { seraphineAnnouncement.x = originalX + (Math.random() - 0.5) * 20; seraphineAnnouncement.y = originalY + (Math.random() - 0.5) * 20; }, 50); // Remove text after 5 seconds LK.setTimeout(function () { LK.clearInterval(trembleTimer); seraphineAnnouncement.destroy(); }, 5000); // Play seraphine sound LK.getSound('seraphine_is_here').play(); } updateUI(); LK.getSound('tower_place').play(); } }; // Menu UI var menuContainer = new Container(); menuContainer.x = 2048 / 2; menuContainer.y = 2732 / 2; // Create full-screen background with heaven and hell var heavenBackground = LK.getAsset('path_marker', { anchorX: 0, anchorY: 0, scaleX: 20.48, scaleY: 13.66, color: 0x87CEEB }); heavenBackground.x = -1024; heavenBackground.y = -1366; menuContainer.addChild(heavenBackground); var hellBackground = LK.getAsset('path_marker', { anchorX: 0, anchorY: 0, scaleX: 20.48, scaleY: 13.66, color: 0x8B0000 }); hellBackground.x = -1024; hellBackground.y = 0; menuContainer.addChild(hellBackground); var menuIcon = LK.getAsset('icon_menu', { anchorX: 0.5, anchorY: 0.5 }); menuIcon.y = -600; menuContainer.addChild(menuIcon); var titleText = new Text2('Divine Defense: Battle for Souls', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.y = -200; menuContainer.addChild(titleText); var startButton = new Container(); var startButtonBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 2, alpha: 0.8 }); startButton.addChild(startButtonBg); var startButtonText = new Text2('START GAME', { size: 80, fill: 0x00FF00 }); startButtonText.anchor.set(0.5, 0.5); startButton.addChild(startButtonText); startButton.y = 100; menuContainer.addChild(startButton); var instructionsText = new Text2('Click START to begin defending!\nPlace towers to stop the demons!', { size: 60, fill: 0xFFFFFF }); instructionsText.anchor.set(0.5, 0.5); instructionsText.y = 250; menuContainer.addChild(instructionsText); var victoriesText = new Text2('Victories: ' + (storage.victories || 0), { size: 70, fill: 0xFFD700 }); victoriesText.anchor.set(0.5, 0.5); victoriesText.y = 350; menuContainer.addChild(victoriesText); var rebirthText = new Text2('Rebirth Level: ' + (storage.rebirthLevel || 0), { size: 60, fill: 0x000000 }); rebirthText.anchor.set(0.5, 0.5); rebirthText.y = 420; menuContainer.addChild(rebirthText); var rebirthButton = new Container(); var rebirthButtonBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 1.5, alpha: 0.8, color: 0x800080 }); rebirthButton.addChild(rebirthButtonBg); var rebirthButtonText = new Text2('REBIRTH', { size: 60, fill: 0xFFFFFF }); rebirthButtonText.anchor.set(0.5, 0.5); rebirthButton.addChild(rebirthButtonText); rebirthButton.y = 520; menuContainer.addChild(rebirthButton); var rebirthInfoText = new Text2('', { size: 50, fill: 0xFFFFFF }); rebirthInfoText.anchor.set(0.5, 0.5); rebirthInfoText.y = 620; menuContainer.addChild(rebirthInfoText); var endlessButton = new Container(); var endlessButtonBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 2, alpha: 0.8, color: 0xFF4500 }); endlessButton.addChild(endlessButtonBg); var endlessButtonText = new Text2('ENDLESS MODE', { size: 70, fill: 0xFFFFFF }); endlessButtonText.anchor.set(0.5, 0.5); endlessButton.addChild(endlessButtonText); endlessButton.y = 730; menuContainer.addChild(endlessButton); var leaderboardButton = new Container(); var leaderboardButtonBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 2, alpha: 0.8, color: 0x4169E1 }); leaderboardButton.addChild(leaderboardButtonBg); var leaderboardButtonText = new Text2('LEADERBOARD', { size: 70, fill: 0xFFFFFF }); leaderboardButtonText.anchor.set(0.5, 0.5); leaderboardButton.addChild(leaderboardButtonText); leaderboardButton.y = 870; menuContainer.addChild(leaderboardButton); // Back to menu button (for in-game use) var backToMenuButton = new Container(); var backToMenuBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5, alpha: 0.8, color: 0x666666 }); backToMenuButton.addChild(backToMenuBg); var backToMenuText = new Text2('MENU', { size: 50, fill: 0xFFFFFF }); backToMenuText.anchor.set(0.5, 0.5); backToMenuButton.addChild(backToMenuText); backToMenuButton.x = 1900; backToMenuButton.y = 2600; backToMenuButton.visible = false; // Hidden by default game.addChild(backToMenuButton); backToMenuButton.down = function () { // Update leaderboard if in endless mode before going back to menu if (endlessMode) { updateLeaderboard(wave); } // Reset game state and return to menu showMenu = true; menuContainer.visible = true; leaderboardContainer.visible = false; gameStarted = false; endlessMode = false; backToMenuButton.visible = false; waveText.visible = false; towerCountText.visible = false; puertaInf.visible = false; escalerasCiel.visible = false; // Clear all game objects for (var i = towers.length - 1; i >= 0; i--) { towers[i].destroy(); towers.splice(i, 1); } for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); enemies.splice(i, 1); } for (var i = projectiles.length - 1; i >= 0; i--) { projectiles[i].destroy(); projectiles.splice(i, 1); } // Reset all game variables to starting state towers = []; enemies = []; projectiles = []; selectedTower = null; selectedTowerType = null; coins = 100; lives = 20; wave = 1; enemiesSpawned = 0; spawnTimer = 0; waveComplete = false; enemiesInWave = 5; towerInfoPanel.visible = false; towerSelectPanel.visible = false; updateUI(); updateRebirthUI(); victoriesText.setText('Victories: ' + (storage.victories || 0)); }; function updateLeaderboard(waves) { // Only update if this is a new record for the player if (waves > (storage.endlessRecord || 0)) { storage.endlessRecord = waves; // Get current leaderboard or initialize empty array var leaderboard = storage.leaderboard || []; // Create player entry with name var playerEntry = { name: storage.currentPlayerName || 'Anonymous', waves: waves, timestamp: Date.now() }; // Add to leaderboard leaderboard.push(playerEntry); // Sort by waves (highest first), then by timestamp (earliest first for ties) leaderboard.sort(function (a, b) { if (b.waves !== a.waves) { return b.waves - a.waves; } return a.timestamp - b.timestamp; }); // Keep only top 100 if (leaderboard.length > 100) { leaderboard = leaderboard.slice(0, 100); } // Save back to storage storage.leaderboard = leaderboard; } } function displayLeaderboard() { // Clear previous content while (leaderboardContent.children.length > 0) { leaderboardContent.removeChild(leaderboardContent.children[0]); } var leaderboard = storage.leaderboard || []; var playerRecord = storage.endlessRecord || 0; // Show player's record at top var recordText = new Text2('Your Best: Wave ' + playerRecord, { size: 60, fill: 0x00FF00 }); recordText.anchor.set(0.5, 0.5); recordText.y = 0; leaderboardContent.addChild(recordText); // Header var headerText = new Text2('RANK NAME WAVES', { size: 50, fill: 0xFFFFFF }); headerText.anchor.set(0.5, 0.5); headerText.y = 80; leaderboardContent.addChild(headerText); // Display top entries var maxEntries = Math.min(leaderboard.length, 15); // Show max 15 entries to fit screen for (var i = 0; i < maxEntries; i++) { var entry = leaderboard[i]; var rank = i + 1; var playerName = entry.name || 'Anonymous'; var isPlayerRecord = entry.waves === playerRecord && playerName === (storage.currentPlayerName || 'Anonymous'); // Truncate name if too long if (playerName.length > 8) { playerName = playerName.substring(0, 8); } var entryText = new Text2('#' + rank + ' ' + playerName + ' Wave ' + entry.waves, { size: 45, fill: isPlayerRecord ? 0xFFD700 : 0xFFFFFF }); entryText.anchor.set(0.5, 0.5); entryText.y = 150 + i * 60; leaderboardContent.addChild(entryText); } if (leaderboard.length === 0) { var noDataText = new Text2('No records yet!\nPlay Endless Mode to set a record!', { size: 60, fill: 0x000000 }); noDataText.anchor.set(0.5, 0.5); noDataText.y = 300; leaderboardContent.addChild(noDataText); } } function updateRebirthUI() { var required = getRebirthRequirement(); var currentVictories = storage.victories || 0; var currentRebirth = storage.rebirthLevel || 0; rebirthText.setText('Rebirth Level: ' + currentRebirth); if (required === -1) { rebirthInfoText.setText('Max Rebirth Reached!\nTowers deal 6x damage'); rebirthButton.visible = false; } else if (canRebirth()) { rebirthInfoText.setText('Ready to Rebirth!\nNeed: ' + required + ' victories\nNext damage: ' + Math.floor(getDamageMultiplier() * (currentRebirth === 0 ? 105 : currentRebirth === 1 ? 115 : 600)) + '%'); rebirthButton.visible = true; rebirthButtonBg.alpha = 1.0; } else { var nextMultiplier = currentRebirth === 0 ? "5%" : currentRebirth === 1 ? "15%" : "6x"; rebirthInfoText.setText('Need ' + (required - currentVictories) + ' more victories\nNext rebirth: +' + nextMultiplier + ' damage'); rebirthButton.visible = true; rebirthButtonBg.alpha = 0.5; } } // Leaderboard container var leaderboardContainer = new Container(); leaderboardContainer.x = 2048 / 2; leaderboardContainer.y = 2732 / 2; leaderboardContainer.visible = false; var leaderboardBackground = LK.getAsset('path_marker', { anchorX: 0, anchorY: 0, scaleX: 18, scaleY: 25, alpha: 0.9, color: 0x2F4F4F }); leaderboardBackground.x = -900; leaderboardBackground.y = -1250; leaderboardContainer.addChild(leaderboardBackground); var leaderboardTitle = new Text2('TOP 100 ENDLESS LEADERS', { size: 80, fill: 0xFFD700 }); leaderboardTitle.anchor.set(0.5, 0.5); leaderboardTitle.y = -1100; leaderboardContainer.addChild(leaderboardTitle); var leaderboardContent = new Container(); leaderboardContent.y = -950; leaderboardContainer.addChild(leaderboardContent); var backFromLeaderboardButton = new Container(); var backFromLeaderboardBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5, alpha: 0.8, color: 0x666666 }); backFromLeaderboardButton.addChild(backFromLeaderboardBg); var backFromLeaderboardText = new Text2('BACK', { size: 60, fill: 0xFFFFFF }); backFromLeaderboardText.anchor.set(0.5, 0.5); backFromLeaderboardButton.addChild(backFromLeaderboardText); backFromLeaderboardButton.y = 1050; leaderboardContainer.addChild(backFromLeaderboardButton); game.addChild(leaderboardContainer); game.addChild(menuContainer); rebirthButton.down = function () { if (canRebirth()) { storage.rebirthLevel = (storage.rebirthLevel || 0) + 1; storage.victories = 0; updateRebirthUI(); victoriesText.setText('Victories: 0'); } }; leaderboardButton.down = function () { displayLeaderboard(); menuContainer.visible = false; leaderboardContainer.visible = true; }; backFromLeaderboardButton.down = function () { leaderboardContainer.visible = false; menuContainer.visible = true; }; endlessButton.down = function () { // Show name input prompt showNameInput(); }; // Name input functionality var nameInputContainer = new Container(); nameInputContainer.x = 2048 / 2; nameInputContainer.y = 2732 / 2; nameInputContainer.visible = false; var nameInputBackground = LK.getAsset('path_marker', { anchorX: 0, anchorY: 0, scaleX: 15, scaleY: 8, alpha: 0.9, color: 0x2F2F2F }); nameInputBackground.x = -750; nameInputBackground.y = -400; nameInputContainer.addChild(nameInputBackground); var nameInputTitle = new Text2('Enter your name for the leaderboard:', { size: 70, fill: 0xFFFFFF }); nameInputTitle.anchor.set(0.5, 0.5); nameInputTitle.y = -250; nameInputContainer.addChild(nameInputTitle); var currentPlayerName = ''; var nameDisplayText = new Text2('_', { size: 80, fill: 0x00FF00, width: 1200 }); nameDisplayText.anchor.set(0.5, 0.5); nameDisplayText.y = -100; nameInputContainer.addChild(nameDisplayText); // Virtual keyboard var keyboardContainer = new Container(); keyboardContainer.y = 50; nameInputContainer.addChild(keyboardContainer); var keyboard = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']]; var keyButtons = []; for (var row = 0; row < keyboard.length; row++) { for (var col = 0; col < keyboard[row].length; col++) { var letter = keyboard[row][col]; var keyButton = new Container(); var keyBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, alpha: 0.8, color: 0x666666 }); keyButton.addChild(keyBg); var keyText = new Text2(letter, { size: 40, fill: 0xFFFFFF }); keyText.anchor.set(0.5, 0.5); keyButton.addChild(keyText); keyButton.x = (col - keyboard[row].length / 2) * 80 + 40; keyButton.y = row * 80; keyButton.letter = letter; keyButton.down = function () { if (currentPlayerName.length < 12) { currentPlayerName += this.letter; updateNameDisplay(); } }; keyButtons.push(keyButton); keyboardContainer.addChild(keyButton); } } // Backspace button var backspaceButton = new Container(); var backspaceBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.8, alpha: 0.8, color: 0x8B0000 }); backspaceButton.addChild(backspaceBg); var backspaceText = new Text2('DELETE', { size: 35, fill: 0xFFFFFF }); backspaceText.anchor.set(0.5, 0.5); backspaceButton.addChild(backspaceText); backspaceButton.x = 600; backspaceButton.y = 160; backspaceButton.down = function () { if (currentPlayerName.length > 0) { currentPlayerName = currentPlayerName.slice(0, -1); updateNameDisplay(); } }; keyboardContainer.addChild(backspaceButton); // Space button var spaceButton = new Container(); var spaceBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 0.8, alpha: 0.8, color: 0x444444 }); spaceButton.addChild(spaceBg); var spaceText = new Text2('SPACE', { size: 35, fill: 0xFFFFFF }); spaceText.anchor.set(0.5, 0.5); spaceButton.addChild(spaceText); spaceButton.x = 0; spaceButton.y = 240; spaceButton.down = function () { if (currentPlayerName.length < 12 && currentPlayerName.length > 0) { currentPlayerName += ' '; updateNameDisplay(); } }; keyboardContainer.addChild(spaceButton); // Start button var startEndlessButton = new Container(); var startEndlessBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 1.5, alpha: 0.8, color: 0x228B22 }); startEndlessButton.addChild(startEndlessBg); var startEndlessText = new Text2('START ENDLESS', { size: 50, fill: 0xFFFFFF }); startEndlessText.anchor.set(0.5, 0.5); startEndlessButton.addChild(startEndlessText); startEndlessButton.y = 450; startEndlessButton.down = function () { if (currentPlayerName.length > 0) { storage.currentPlayerName = currentPlayerName; nameInputContainer.visible = false; startEndlessMode(); } }; nameInputContainer.addChild(startEndlessButton); // Cancel button var cancelNameButton = new Container(); var cancelNameBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 1.2, alpha: 0.8, color: 0x666666 }); cancelNameButton.addChild(cancelNameBg); var cancelNameText = new Text2('CANCEL', { size: 40, fill: 0xFFFFFF }); cancelNameText.anchor.set(0.5, 0.5); cancelNameButton.addChild(cancelNameText); cancelNameButton.x = -300; cancelNameButton.y = 450; cancelNameButton.down = function () { nameInputContainer.visible = false; menuContainer.visible = true; currentPlayerName = ''; updateNameDisplay(); }; nameInputContainer.addChild(cancelNameButton); game.addChild(nameInputContainer); function updateNameDisplay() { nameDisplayText.setText(currentPlayerName.length > 0 ? currentPlayerName : '_'); } function showNameInput() { currentPlayerName = ''; updateNameDisplay(); nameInputContainer.visible = true; menuContainer.visible = false; } function startEndlessMode() { endlessMode = true; showMenu = false; gameStarted = true; spawnTimer = 0; backToMenuButton.visible = true; waveText.visible = true; towerCountText.visible = true; puertaInf.visible = true; escalerasCiel.visible = true; // Update victories display victoriesText.setText('Victories: ' + (storage.victories || 0)); updateRebirthUI(); } startButton.down = function () { endlessMode = false; showMenu = false; menuContainer.visible = false; gameStarted = true; spawnTimer = 0; backToMenuButton.visible = true; waveText.visible = true; towerCountText.visible = true; puertaInf.visible = true; escalerasCiel.visible = true; // Update victories display victoriesText.setText('Victories: ' + (storage.victories || 0)); updateRebirthUI(); }; // Initialize game createPath(); updateUI(); updateRebirthUI(); // Start music LK.playMusic('divine_battle'); game.update = function () { if (showMenu) { return; // Don't update game logic while menu is showing } if (!gameStarted) { gameStarted = true; spawnTimer = 0; } // Spawn enemies if (enemiesSpawned < enemiesInWave) { spawnTimer++; if (spawnTimer >= spawnDelay) { spawnEnemy(); spawnTimer = 0; } } // Check if wave is complete if (enemiesSpawned >= enemiesInWave && enemies.length === 0 && !waveComplete) { waveComplete = true; // Bonus coins for completing wave coins += 25 + wave * 5; updateUI(); // Start next wave after delay LK.setTimeout(function () { startNextWave(); }, 2000); } // Check win condition for wave 15 (only in normal mode) if (!endlessMode && wave > 15 && enemies.length === 0 && enemiesSpawned >= enemiesInWave) { // Increment victories storage.victories = (storage.victories || 0) + 1; // Show you win and return to menu LK.showYouWin(); // Reset game state and show menu showMenu = true; menuContainer.visible = true; gameStarted = false; endlessMode = false; backToMenuButton.visible = false; waveText.visible = false; towerCountText.visible = false; puertaInf.visible = false; escalerasCiel.visible = false; // Reset all game variables to starting state towers = []; enemies = []; projectiles = []; selectedTower = null; selectedTowerType = null; coins = 100; lives = 20; wave = 1; enemiesSpawned = 0; spawnTimer = 0; waveComplete = false; enemiesInWave = 5; towerInfoPanel.visible = false; towerSelectPanel.visible = false; updateUI(); updateRebirthUI(); victoriesText.setText('Victories: ' + (storage.victories || 0)); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
victories: 0,
rebirthLevel: 0,
endlessRecord: 0,
leaderboard: [],
currentPlayerName: ""
});
/****
* Classes
****/
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'basic';
self.health = 50;
self.maxHealth = 50;
self.speed = 1;
self.reward = 10;
self.pathIndex = 0;
self.pathProgress = 0;
if (self.type === 'basic') {
self.health = 50;
self.maxHealth = 50;
self.speed = 1;
self.reward = 25;
self.graphics = self.attachAsset('demon_basic', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'fast') {
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.reward = 25;
self.graphics = self.attachAsset('demon_fast', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'devil') {
self.health = 200;
self.maxHealth = 200;
self.speed = 0.5;
self.reward = 50;
self.graphics = self.attachAsset('boss_devil', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'milei') {
self.health = 150;
self.maxHealth = 150;
self.speed = 1.2;
self.reward = 50;
self.graphics = self.attachAsset('boss_milei', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'trump') {
self.health = 180;
self.maxHealth = 180;
self.speed = 0.8;
self.reward = 50;
self.graphics = self.attachAsset('boss_trump', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'cervero') {
self.health = 666;
self.maxHealth = 666;
self.speed = 0.3;
self.reward = 1000;
self.graphics = self.attachAsset('cervero_boss_final', {
anchorX: 0.5,
anchorY: 0.5
});
}
self.takeDamage = function (damage) {
self.health -= damage;
// Flash red when hit
var originalTint = self.graphics.tint;
self.graphics.tint = 0xFF0000;
LK.setTimeout(function () {
if (self.graphics) {
self.graphics.tint = originalTint;
}
}, 100);
LK.getSound('enemy_hit').play();
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Award coins
coins += self.reward;
updateUI();
// Remove from enemies array
var index = enemies.indexOf(self);
if (index > -1) {
enemies.splice(index, 1);
}
// Destroy the enemy
self.destroy();
};
self.update = function () {
// Move along path
if (self.pathIndex < gamePath.length) {
var targetPoint = gamePath[self.pathIndex];
var dx = targetPoint.x - self.x;
var dy = targetPoint.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length) {
// Reached end, update leaderboard if in endless mode
if (endlessMode) {
updateLeaderboard(wave);
}
// Reset entire game
// Remove all towers
for (var i = towers.length - 1; i >= 0; i--) {
towers[i].destroy();
towers.splice(i, 1);
}
// Remove all enemies
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
// Remove all projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
projectiles[i].destroy();
projectiles.splice(i, 1);
}
// Reset all game variables to starting state
coins = 100;
wave = 1;
enemiesSpawned = 0;
spawnTimer = 0;
waveComplete = false;
enemiesInWave = 5;
selectedTower = null;
towerInfoPanel.visible = false;
towerSelectPanel.visible = false;
updateUI();
// Remove this enemy
self.destroy();
return;
}
} else {
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
}
}
};
return self;
});
var Projectile = Container.expand(function () {
var self = Container.call(this);
self.speed = 8;
self.damage = 20;
self.target = null;
self.graphics = self.attachAsset('projectile', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (!self.target || enemies.indexOf(self.target) === -1) {
// Target is gone, remove projectile
var index = projectiles.indexOf(self);
if (index > -1) {
projectiles.splice(index, 1);
}
self.destroy();
return;
}
// Move towards target
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) {
// Hit target
self.target.takeDamage(self.damage);
var index = projectiles.indexOf(self);
if (index > -1) {
projectiles.splice(index, 1);
}
self.destroy();
} else {
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
}
};
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'buddha';
self.level = 1;
self.damage = 20;
self.range = 150;
self.fireRate = 60; // frames between shots
self.lastShot = 0;
self.cost = 50;
self.lastClickTime = 0;
self.clickCount = 0;
self.seraphine = null;
// Set tower specific properties
if (self.type === 'buddha') {
self.damage = 15;
self.range = 120;
self.fireRate = 90;
self.cost = 50;
self.graphics = self.attachAsset('buddha_tower', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'cristo') {
self.damage = 30;
self.range = 200;
self.fireRate = 120;
self.cost = 75;
self.graphics = self.attachAsset('cristo_tower', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'angel') {
self.damage = 25;
self.range = 180;
self.fireRate = 45;
self.cost = 60;
self.graphics = self.attachAsset('angel_tower', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.type === 'seraphine') {
self.damage = 100;
self.range = 250;
self.fireRate = 30;
self.cost = 1111;
self.graphics = self.attachAsset('seraphine', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Apply rebirth damage multiplier
self.damage = Math.floor(self.damage * getDamageMultiplier());
self.canShoot = function () {
return LK.ticks - self.lastShot >= self.fireRate;
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
var projectile = new Projectile();
projectile.x = self.x;
projectile.y = self.y;
projectile.target = target;
projectile.damage = self.damage;
projectiles.push(projectile);
game.addChild(projectile);
self.lastShot = LK.ticks;
LK.getSound('tower_shoot').play();
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
self.down = function (x, y, obj) {
var currentTime = Date.now();
var timeDiff = currentTime - self.lastClickTime;
// Check for double click (within 500ms)
if (timeDiff < 500 && self.clickCount === 1) {
// Double click detected - delete tower and refund half cost
var refund = Math.floor(self.cost / 2);
coins += refund;
updateUI();
// Remove from towers array
var index = towers.indexOf(self);
if (index > -1) {
towers.splice(index, 1);
}
// Clear selection if this tower was selected
if (selectedTower === self) {
selectedTower = null;
towerInfoPanel.visible = false;
}
// Destroy seraphine if it exists (only for seraphine tower type)
if (self.seraphine && self.type === 'seraphine') {
self.seraphine.destroy();
}
// Destroy the tower
self.destroy();
return;
}
// Single click logic
if (selectedTower === self) {
selectedTower = null;
towerInfoPanel.visible = false;
} else {
selectedTower = self;
showTowerInfo();
}
// Update click tracking
self.lastClickTime = currentTime;
self.clickCount = timeDiff < 500 ? self.clickCount + 1 : 1;
// Reset click count after delay
LK.setTimeout(function () {
if (self.clickCount > 0) {
self.clickCount = 0;
}
}, 500);
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Game variables
var towers = [];
var enemies = [];
var projectiles = [];
var selectedTower = null;
var selectedTowerType = null; // Start with no tower selected
var coins = 100;
var lives = 20;
var wave = 1;
var enemiesSpawned = 0;
var enemiesInWave = 5;
var spawnTimer = 0;
var spawnDelay = 60;
var waveComplete = false;
var gameStarted = false;
var showMenu = true;
var endlessMode = false;
var puertaInf = null;
var escalerasCiel = null;
// Rebirth system
function getDamageMultiplier() {
var rebirthLevel = storage.rebirthLevel || 0;
if (rebirthLevel === 0) return 1;
if (rebirthLevel === 1) return 1.05; // 5% more damage
if (rebirthLevel === 2) return 1.15; // 15% more damage
if (rebirthLevel === 3) return 6; // 6x damage
return 6; // Cap at 6x
}
function getRebirthRequirement() {
var rebirthLevel = storage.rebirthLevel || 0;
if (rebirthLevel === 0) return 15;
if (rebirthLevel === 1) return 30;
if (rebirthLevel === 2) return 100;
return -1; // No more rebirths
}
function canRebirth() {
var required = getRebirthRequirement();
return required !== -1 && (storage.victories || 0) >= required;
}
// Game path
var gamePath = [{
x: 0,
y: 400
}, {
x: 300,
y: 400
}, {
x: 300,
y: 200
}, {
x: 600,
y: 200
}, {
x: 600,
y: 500
}, {
x: 900,
y: 500
}, {
x: 900,
y: 300
}, {
x: 1200,
y: 300
}, {
x: 1200,
y: 600
}, {
x: 1500,
y: 600
}, {
x: 1500,
y: 100
}, {
x: 2048,
y: 100
}];
// Draw path markers
function createPath() {
for (var i = 0; i < gamePath.length - 1; i++) {
var marker = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
marker.x = gamePath[i].x;
marker.y = gamePath[i].y;
game.addChild(marker);
}
// Add puerta_inf at enemy spawn point (start of path)
puertaInf = LK.getAsset('puerta_inf', {
anchorX: 0.5,
anchorY: 0.5
});
puertaInf.x = gamePath[0].x + 100;
puertaInf.y = gamePath[0].y;
puertaInf.visible = false; // Hide initially since menu is showing
game.addChild(puertaInf);
// Add escaleras_ciel at enemy destination (end of path)
escalerasCiel = LK.getAsset('escaleras_ciel', {
anchorX: 0.5,
anchorY: 0.5
});
escalerasCiel.x = gamePath[gamePath.length - 1].x - 50;
escalerasCiel.y = gamePath[gamePath.length - 1].y + 50;
escalerasCiel.visible = false; // Hide initially since menu is showing
game.addChild(escalerasCiel);
}
// UI Elements
var coinsText = new Text2('Coins: 100', {
size: 60,
fill: 0xFFD700
});
coinsText.anchor.set(0, 0);
LK.gui.topRight.addChild(coinsText);
var livesText = new Text2('Lives: 20', {
size: 60,
fill: 0xFF0000
});
livesText.anchor.set(0, 0);
livesText.y = 70;
LK.gui.topRight.addChild(livesText);
var waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.visible = false; // Hide initially since menu is showing
LK.gui.top.addChild(waveText);
var towerCountText = new Text2('Towers placed: 0/7', {
size: 60,
fill: 0xFFFFFF
});
towerCountText.anchor.set(0.5, 0);
towerCountText.y = 70;
towerCountText.visible = false; // Hide initially since menu is showing
LK.gui.top.addChild(towerCountText);
// Tower selection UI
var towerSelectPanel = new Container();
towerSelectPanel.x = 50;
towerSelectPanel.y = 2100;
towerSelectPanel.visible = false; // Initially hidden
// Shop button
var shopButton = new Container();
var shopBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1,
alpha: 0.8
});
shopButton.addChild(shopBg);
var shopText = new Text2('SHOP', {
size: 50,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopButton.addChild(shopText);
shopButton.x = 150;
shopButton.y = 2600;
game.addChild(shopButton);
// Current money display near shop
var currentMoneyText = new Text2('Money: $' + coins, {
size: 55,
fill: 0x00FF00
});
currentMoneyText.anchor.set(0.5, 0.5);
currentMoneyText.x = 150;
currentMoneyText.y = 2500;
game.addChild(currentMoneyText);
var buddhaButton = LK.getAsset('buddha_tower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
buddhaButton.x = 120;
buddhaButton.y = 0;
towerSelectPanel.addChild(buddhaButton);
var cristoButton = LK.getAsset('cristo_tower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
cristoButton.x = 300;
cristoButton.y = 0;
towerSelectPanel.addChild(cristoButton);
var angelButton = LK.getAsset('angel_tower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
angelButton.x = 480;
angelButton.y = 0;
towerSelectPanel.addChild(angelButton);
var seraphineButton = LK.getAsset('seraphine', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
seraphineButton.x = 660;
seraphineButton.y = 0;
towerSelectPanel.addChild(seraphineButton);
// Define tower costs
var towerCosts = {
buddha: 50,
cristo: 75,
angel: 60,
seraphine: 1111
};
// Add cost labels for towers
var buddhaCostText = new Text2('Buddha\n$' + towerCosts.buddha, {
size: 50,
fill: 0x000000
});
buddhaCostText.anchor.set(0.5, 0);
buddhaCostText.x = 120;
buddhaCostText.y = 120;
towerSelectPanel.addChild(buddhaCostText);
var cristoCostText = new Text2('Cristo\n$' + towerCosts.cristo, {
size: 50,
fill: 0x000000
});
cristoCostText.anchor.set(0.5, 0);
cristoCostText.x = 300;
cristoCostText.y = 120;
towerSelectPanel.addChild(cristoCostText);
var angelCostText = new Text2('Angel\n$' + towerCosts.angel, {
size: 50,
fill: 0x000000
});
angelCostText.anchor.set(0.5, 0);
angelCostText.x = 480;
angelCostText.y = 120;
towerSelectPanel.addChild(angelCostText);
var seraphineCostText = new Text2('Seraphine\n$' + towerCosts.seraphine, {
size: 50,
fill: 0x000000
});
seraphineCostText.anchor.set(0.5, 0);
seraphineCostText.x = 660;
seraphineCostText.y = 120;
towerSelectPanel.addChild(seraphineCostText);
game.addChild(towerSelectPanel);
// Tower info panel
var towerInfoPanel = new Container();
towerInfoPanel.visible = false;
var towerInfoBg = LK.getAsset('path_marker', {
anchorX: 0,
anchorY: 0,
scaleX: 4,
scaleY: 3,
alpha: 0.8
});
towerInfoPanel.addChild(towerInfoBg);
var towerInfoText = new Text2('Tower Info', {
size: 45,
fill: 0xFFFFFF
});
towerInfoText.x = 20;
towerInfoText.y = 20;
towerInfoPanel.addChild(towerInfoText);
towerInfoPanel.x = 1600;
towerInfoPanel.y = 300;
game.addChild(towerInfoPanel);
function updateUI() {
if (!showMenu) {
towerCountText.setText('Towers placed: ' + towers.length + '/7');
}
coinsText.setText('Coins: ' + coins);
livesText.setText('Lives: ' + lives);
waveText.setText('Wave: ' + wave);
currentMoneyText.setText('Money: $' + coins);
// Update tower affordability visual feedback
if (coins >= towerCosts.buddha) {
buddhaCostText.fill = 0x00FF00; // Green for affordable
buddhaButton.alpha = 1.0;
} else {
buddhaCostText.fill = 0xFF0000; // Red for unaffordable
buddhaButton.alpha = 0.5;
}
if (coins >= towerCosts.cristo) {
cristoCostText.fill = 0x00FF00; // Green for affordable
cristoButton.alpha = 1.0;
} else {
cristoCostText.fill = 0xFF0000; // Red for unaffordable
cristoButton.alpha = 0.5;
}
if (coins >= towerCosts.angel) {
angelCostText.fill = 0x00FF00; // Green for affordable
angelButton.alpha = 1.0;
} else {
angelCostText.fill = 0xFF0000; // Red for unaffordable
angelButton.alpha = 0.5;
}
if (coins >= towerCosts.seraphine) {
seraphineCostText.fill = 0x00FF00; // Green for affordable
seraphineButton.alpha = 1.0;
} else {
seraphineCostText.fill = 0xFF0000; // Red for unaffordable
seraphineButton.alpha = 0.5;
}
}
function showTowerInfo() {
if (selectedTower) {
towerInfoPanel.visible = true;
towerInfoText.setText('Type: ' + selectedTower.type + '\nLevel: ' + selectedTower.level + '\nDamage: ' + selectedTower.damage);
}
}
function spawnEnemy() {
var enemyType = 'basic';
var spawnCount = 1;
var spawnTypes = [];
// Specific wave configurations
if (wave === 2) {
// Wave 2: 2 demon_fast + others (basics)
if (enemiesSpawned < 2) {
spawnTypes = ['fast', 'fast'];
} else {
spawnTypes = ['basic'];
}
} else if (wave === 3) {
// Wave 3: trump appears
if (enemiesSpawned === 0) {
spawnTypes = ['trump'];
} else {
spawnTypes = ['basic'];
}
} else if (wave === 5) {
// Wave 5: milei appears
if (enemiesSpawned === 0) {
spawnTypes = ['milei'];
} else {
spawnTypes = ['basic'];
}
} else if (wave === 6) {
// Wave 6: devil with 7 demon_fast
if (enemiesSpawned === 0) {
spawnTypes = ['devil'];
} else if (enemiesSpawned < 8) {
spawnTypes = ['fast'];
} else {
spawnTypes = ['basic'];
}
} else if (wave === 7) {
// Wave 7: milei, trump, devil with 3 demon_fast
if (enemiesSpawned === 0) {
spawnTypes = ['milei'];
} else if (enemiesSpawned === 1) {
spawnTypes = ['trump'];
} else if (enemiesSpawned === 2) {
spawnTypes = ['devil'];
} else if (enemiesSpawned < 6) {
spawnTypes = ['fast'];
} else {
spawnTypes = ['basic'];
}
} else if (wave === 13) {
// Wave 13: 6 trump, 6 milei, 6 devil, 12 demon, 7 fast demon and 1 cervero
if (enemiesSpawned < 6) {
spawnTypes = ['trump'];
} else if (enemiesSpawned < 12) {
spawnTypes = ['milei'];
} else if (enemiesSpawned < 18) {
spawnTypes = ['devil'];
} else if (enemiesSpawned < 30) {
spawnTypes = ['basic'];
} else if (enemiesSpawned < 37) {
spawnTypes = ['fast'];
} else if (enemiesSpawned < 38) {
spawnTypes = ['cervero'];
} else {
spawnTypes = ['basic'];
}
} else if (wave === 15) {
// Wave 15: Cervero boss final appears
if (enemiesSpawned === 0) {
spawnTypes = ['cervero'];
} else {
spawnTypes = ['basic'];
}
} else {
// Original logic for other waves
if (wave % 10 === 0) {
if (wave === 10) enemyType = 'milei';else if (wave === 20) enemyType = 'trump';else if (wave === 30) enemyType = 'devil';else enemyType = Math.random() < 0.3 ? 'devil' : Math.random() < 0.5 ? 'milei' : 'trump';
} else if (wave > 5) {
enemyType = Math.random() < 0.6 ? 'basic' : 'fast';
}
spawnTypes = [enemyType];
}
// Spawn the determined enemy types
for (var i = 0; i < spawnTypes.length; i++) {
var enemy = new Enemy(spawnTypes[i]);
enemy.x = gamePath[0].x;
enemy.y = gamePath[0].y;
enemies.push(enemy);
game.addChild(enemy);
}
enemiesSpawned++;
}
function canPlaceTower(x, y) {
// Check if position is too close to path
for (var i = 0; i < gamePath.length; i++) {
var pathPoint = gamePath[i];
var distance = Math.sqrt(Math.pow(x - pathPoint.x, 2) + Math.pow(y - pathPoint.y, 2));
if (distance < 60) {
return false;
}
}
// Check if position is too close to existing towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var distance = Math.sqrt(Math.pow(x - tower.x, 2) + Math.pow(y - tower.y, 2));
if (distance < 180) {
// Increased minimum distance to prevent overlap
return false;
}
}
// Prevent placing towers on shop button area
var shopButtonDistance = Math.sqrt(Math.pow(x - 150, 2) + Math.pow(y - 2600, 2));
if (shopButtonDistance < 150) {
return false;
}
// Prevent placing towers on current money text area
var moneyTextDistance = Math.sqrt(Math.pow(x - 150, 2) + Math.pow(y - 2500, 2));
if (moneyTextDistance < 100) {
return false;
}
// Prevent placing towers on tower selection panel when visible
if (towerSelectPanel.visible) {
// Check if position overlaps with tower selection buttons
if (y >= 2100 - 100 && y <= 2100 + 200 && x >= 50 && x <= 580) {
return false;
}
}
return true;
}
function startNextWave() {
wave++;
enemiesSpawned = 0;
if (wave === 13) {
enemiesInWave = 38; // 6 trump + 6 milei + 6 devil + 12 demon + 7 fast demon + 1 cervero
} else {
enemiesInWave = Math.min(5 + Math.floor(wave / 2), 20);
}
spawnDelay = Math.max(30, 60 - wave * 2);
waveComplete = false;
updateUI();
}
// Event handlers
buddhaButton.down = function () {
selectedTowerType = 'buddha';
selectedTower = null;
towerInfoPanel.visible = false;
};
cristoButton.down = function () {
selectedTowerType = 'cristo';
selectedTower = null;
towerInfoPanel.visible = false;
};
angelButton.down = function () {
selectedTowerType = 'angel';
selectedTower = null;
towerInfoPanel.visible = false;
};
seraphineButton.down = function () {
selectedTowerType = 'seraphine';
selectedTower = null;
towerInfoPanel.visible = false;
};
shopButton.down = function () {
towerSelectPanel.visible = !towerSelectPanel.visible;
selectedTower = null;
towerInfoPanel.visible = false;
};
game.down = function (x, y, obj) {
// Don't allow tower placement while menu is showing
if (showMenu) return;
// Only place towers in game area (not on UI)
if (y > 2100) return;
// Check if a tower type is selected
if (selectedTowerType === null) return;
// Check tower limit (maximum 7 towers)
if (towers.length >= 7) return;
var towerCost = towerCosts[selectedTowerType];
if (selectedTower === null && coins >= towerCost && canPlaceTower(x, y)) {
var newTower = new Tower(selectedTowerType);
newTower.x = x;
newTower.y = y;
towers.push(newTower);
game.addChild(newTower);
coins -= towerCost;
// Show seraphine announcement if seraphine tower was placed
if (selectedTowerType === 'seraphine') {
var seraphineAnnouncement = new Text2('seraphine is here', {
size: 120,
fill: 0xFF0000
});
seraphineAnnouncement.anchor.set(0.5, 0.5);
seraphineAnnouncement.x = 2048 / 2;
seraphineAnnouncement.y = 2732 / 2;
game.addChild(seraphineAnnouncement);
// Create trembling effect
var originalX = seraphineAnnouncement.x;
var originalY = seraphineAnnouncement.y;
var trembleTimer = LK.setInterval(function () {
seraphineAnnouncement.x = originalX + (Math.random() - 0.5) * 20;
seraphineAnnouncement.y = originalY + (Math.random() - 0.5) * 20;
}, 50);
// Remove text after 5 seconds
LK.setTimeout(function () {
LK.clearInterval(trembleTimer);
seraphineAnnouncement.destroy();
}, 5000);
// Play seraphine sound
LK.getSound('seraphine_is_here').play();
}
updateUI();
LK.getSound('tower_place').play();
}
};
// Menu UI
var menuContainer = new Container();
menuContainer.x = 2048 / 2;
menuContainer.y = 2732 / 2;
// Create full-screen background with heaven and hell
var heavenBackground = LK.getAsset('path_marker', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 13.66,
color: 0x87CEEB
});
heavenBackground.x = -1024;
heavenBackground.y = -1366;
menuContainer.addChild(heavenBackground);
var hellBackground = LK.getAsset('path_marker', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 13.66,
color: 0x8B0000
});
hellBackground.x = -1024;
hellBackground.y = 0;
menuContainer.addChild(hellBackground);
var menuIcon = LK.getAsset('icon_menu', {
anchorX: 0.5,
anchorY: 0.5
});
menuIcon.y = -600;
menuContainer.addChild(menuIcon);
var titleText = new Text2('Divine Defense: Battle for Souls', {
size: 100,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -200;
menuContainer.addChild(titleText);
var startButton = new Container();
var startButtonBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2,
alpha: 0.8
});
startButton.addChild(startButtonBg);
var startButtonText = new Text2('START GAME', {
size: 80,
fill: 0x00FF00
});
startButtonText.anchor.set(0.5, 0.5);
startButton.addChild(startButtonText);
startButton.y = 100;
menuContainer.addChild(startButton);
var instructionsText = new Text2('Click START to begin defending!\nPlace towers to stop the demons!', {
size: 60,
fill: 0xFFFFFF
});
instructionsText.anchor.set(0.5, 0.5);
instructionsText.y = 250;
menuContainer.addChild(instructionsText);
var victoriesText = new Text2('Victories: ' + (storage.victories || 0), {
size: 70,
fill: 0xFFD700
});
victoriesText.anchor.set(0.5, 0.5);
victoriesText.y = 350;
menuContainer.addChild(victoriesText);
var rebirthText = new Text2('Rebirth Level: ' + (storage.rebirthLevel || 0), {
size: 60,
fill: 0x000000
});
rebirthText.anchor.set(0.5, 0.5);
rebirthText.y = 420;
menuContainer.addChild(rebirthText);
var rebirthButton = new Container();
var rebirthButtonBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 1.5,
alpha: 0.8,
color: 0x800080
});
rebirthButton.addChild(rebirthButtonBg);
var rebirthButtonText = new Text2('REBIRTH', {
size: 60,
fill: 0xFFFFFF
});
rebirthButtonText.anchor.set(0.5, 0.5);
rebirthButton.addChild(rebirthButtonText);
rebirthButton.y = 520;
menuContainer.addChild(rebirthButton);
var rebirthInfoText = new Text2('', {
size: 50,
fill: 0xFFFFFF
});
rebirthInfoText.anchor.set(0.5, 0.5);
rebirthInfoText.y = 620;
menuContainer.addChild(rebirthInfoText);
var endlessButton = new Container();
var endlessButtonBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2,
alpha: 0.8,
color: 0xFF4500
});
endlessButton.addChild(endlessButtonBg);
var endlessButtonText = new Text2('ENDLESS MODE', {
size: 70,
fill: 0xFFFFFF
});
endlessButtonText.anchor.set(0.5, 0.5);
endlessButton.addChild(endlessButtonText);
endlessButton.y = 730;
menuContainer.addChild(endlessButton);
var leaderboardButton = new Container();
var leaderboardButtonBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2,
alpha: 0.8,
color: 0x4169E1
});
leaderboardButton.addChild(leaderboardButtonBg);
var leaderboardButtonText = new Text2('LEADERBOARD', {
size: 70,
fill: 0xFFFFFF
});
leaderboardButtonText.anchor.set(0.5, 0.5);
leaderboardButton.addChild(leaderboardButtonText);
leaderboardButton.y = 870;
menuContainer.addChild(leaderboardButton);
// Back to menu button (for in-game use)
var backToMenuButton = new Container();
var backToMenuBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5,
alpha: 0.8,
color: 0x666666
});
backToMenuButton.addChild(backToMenuBg);
var backToMenuText = new Text2('MENU', {
size: 50,
fill: 0xFFFFFF
});
backToMenuText.anchor.set(0.5, 0.5);
backToMenuButton.addChild(backToMenuText);
backToMenuButton.x = 1900;
backToMenuButton.y = 2600;
backToMenuButton.visible = false; // Hidden by default
game.addChild(backToMenuButton);
backToMenuButton.down = function () {
// Update leaderboard if in endless mode before going back to menu
if (endlessMode) {
updateLeaderboard(wave);
}
// Reset game state and return to menu
showMenu = true;
menuContainer.visible = true;
leaderboardContainer.visible = false;
gameStarted = false;
endlessMode = false;
backToMenuButton.visible = false;
waveText.visible = false;
towerCountText.visible = false;
puertaInf.visible = false;
escalerasCiel.visible = false;
// Clear all game objects
for (var i = towers.length - 1; i >= 0; i--) {
towers[i].destroy();
towers.splice(i, 1);
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
for (var i = projectiles.length - 1; i >= 0; i--) {
projectiles[i].destroy();
projectiles.splice(i, 1);
}
// Reset all game variables to starting state
towers = [];
enemies = [];
projectiles = [];
selectedTower = null;
selectedTowerType = null;
coins = 100;
lives = 20;
wave = 1;
enemiesSpawned = 0;
spawnTimer = 0;
waveComplete = false;
enemiesInWave = 5;
towerInfoPanel.visible = false;
towerSelectPanel.visible = false;
updateUI();
updateRebirthUI();
victoriesText.setText('Victories: ' + (storage.victories || 0));
};
function updateLeaderboard(waves) {
// Only update if this is a new record for the player
if (waves > (storage.endlessRecord || 0)) {
storage.endlessRecord = waves;
// Get current leaderboard or initialize empty array
var leaderboard = storage.leaderboard || [];
// Create player entry with name
var playerEntry = {
name: storage.currentPlayerName || 'Anonymous',
waves: waves,
timestamp: Date.now()
};
// Add to leaderboard
leaderboard.push(playerEntry);
// Sort by waves (highest first), then by timestamp (earliest first for ties)
leaderboard.sort(function (a, b) {
if (b.waves !== a.waves) {
return b.waves - a.waves;
}
return a.timestamp - b.timestamp;
});
// Keep only top 100
if (leaderboard.length > 100) {
leaderboard = leaderboard.slice(0, 100);
}
// Save back to storage
storage.leaderboard = leaderboard;
}
}
function displayLeaderboard() {
// Clear previous content
while (leaderboardContent.children.length > 0) {
leaderboardContent.removeChild(leaderboardContent.children[0]);
}
var leaderboard = storage.leaderboard || [];
var playerRecord = storage.endlessRecord || 0;
// Show player's record at top
var recordText = new Text2('Your Best: Wave ' + playerRecord, {
size: 60,
fill: 0x00FF00
});
recordText.anchor.set(0.5, 0.5);
recordText.y = 0;
leaderboardContent.addChild(recordText);
// Header
var headerText = new Text2('RANK NAME WAVES', {
size: 50,
fill: 0xFFFFFF
});
headerText.anchor.set(0.5, 0.5);
headerText.y = 80;
leaderboardContent.addChild(headerText);
// Display top entries
var maxEntries = Math.min(leaderboard.length, 15); // Show max 15 entries to fit screen
for (var i = 0; i < maxEntries; i++) {
var entry = leaderboard[i];
var rank = i + 1;
var playerName = entry.name || 'Anonymous';
var isPlayerRecord = entry.waves === playerRecord && playerName === (storage.currentPlayerName || 'Anonymous');
// Truncate name if too long
if (playerName.length > 8) {
playerName = playerName.substring(0, 8);
}
var entryText = new Text2('#' + rank + ' ' + playerName + ' Wave ' + entry.waves, {
size: 45,
fill: isPlayerRecord ? 0xFFD700 : 0xFFFFFF
});
entryText.anchor.set(0.5, 0.5);
entryText.y = 150 + i * 60;
leaderboardContent.addChild(entryText);
}
if (leaderboard.length === 0) {
var noDataText = new Text2('No records yet!\nPlay Endless Mode to set a record!', {
size: 60,
fill: 0x000000
});
noDataText.anchor.set(0.5, 0.5);
noDataText.y = 300;
leaderboardContent.addChild(noDataText);
}
}
function updateRebirthUI() {
var required = getRebirthRequirement();
var currentVictories = storage.victories || 0;
var currentRebirth = storage.rebirthLevel || 0;
rebirthText.setText('Rebirth Level: ' + currentRebirth);
if (required === -1) {
rebirthInfoText.setText('Max Rebirth Reached!\nTowers deal 6x damage');
rebirthButton.visible = false;
} else if (canRebirth()) {
rebirthInfoText.setText('Ready to Rebirth!\nNeed: ' + required + ' victories\nNext damage: ' + Math.floor(getDamageMultiplier() * (currentRebirth === 0 ? 105 : currentRebirth === 1 ? 115 : 600)) + '%');
rebirthButton.visible = true;
rebirthButtonBg.alpha = 1.0;
} else {
var nextMultiplier = currentRebirth === 0 ? "5%" : currentRebirth === 1 ? "15%" : "6x";
rebirthInfoText.setText('Need ' + (required - currentVictories) + ' more victories\nNext rebirth: +' + nextMultiplier + ' damage');
rebirthButton.visible = true;
rebirthButtonBg.alpha = 0.5;
}
}
// Leaderboard container
var leaderboardContainer = new Container();
leaderboardContainer.x = 2048 / 2;
leaderboardContainer.y = 2732 / 2;
leaderboardContainer.visible = false;
var leaderboardBackground = LK.getAsset('path_marker', {
anchorX: 0,
anchorY: 0,
scaleX: 18,
scaleY: 25,
alpha: 0.9,
color: 0x2F4F4F
});
leaderboardBackground.x = -900;
leaderboardBackground.y = -1250;
leaderboardContainer.addChild(leaderboardBackground);
var leaderboardTitle = new Text2('TOP 100 ENDLESS LEADERS', {
size: 80,
fill: 0xFFD700
});
leaderboardTitle.anchor.set(0.5, 0.5);
leaderboardTitle.y = -1100;
leaderboardContainer.addChild(leaderboardTitle);
var leaderboardContent = new Container();
leaderboardContent.y = -950;
leaderboardContainer.addChild(leaderboardContent);
var backFromLeaderboardButton = new Container();
var backFromLeaderboardBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5,
alpha: 0.8,
color: 0x666666
});
backFromLeaderboardButton.addChild(backFromLeaderboardBg);
var backFromLeaderboardText = new Text2('BACK', {
size: 60,
fill: 0xFFFFFF
});
backFromLeaderboardText.anchor.set(0.5, 0.5);
backFromLeaderboardButton.addChild(backFromLeaderboardText);
backFromLeaderboardButton.y = 1050;
leaderboardContainer.addChild(backFromLeaderboardButton);
game.addChild(leaderboardContainer);
game.addChild(menuContainer);
rebirthButton.down = function () {
if (canRebirth()) {
storage.rebirthLevel = (storage.rebirthLevel || 0) + 1;
storage.victories = 0;
updateRebirthUI();
victoriesText.setText('Victories: 0');
}
};
leaderboardButton.down = function () {
displayLeaderboard();
menuContainer.visible = false;
leaderboardContainer.visible = true;
};
backFromLeaderboardButton.down = function () {
leaderboardContainer.visible = false;
menuContainer.visible = true;
};
endlessButton.down = function () {
// Show name input prompt
showNameInput();
};
// Name input functionality
var nameInputContainer = new Container();
nameInputContainer.x = 2048 / 2;
nameInputContainer.y = 2732 / 2;
nameInputContainer.visible = false;
var nameInputBackground = LK.getAsset('path_marker', {
anchorX: 0,
anchorY: 0,
scaleX: 15,
scaleY: 8,
alpha: 0.9,
color: 0x2F2F2F
});
nameInputBackground.x = -750;
nameInputBackground.y = -400;
nameInputContainer.addChild(nameInputBackground);
var nameInputTitle = new Text2('Enter your name for the leaderboard:', {
size: 70,
fill: 0xFFFFFF
});
nameInputTitle.anchor.set(0.5, 0.5);
nameInputTitle.y = -250;
nameInputContainer.addChild(nameInputTitle);
var currentPlayerName = '';
var nameDisplayText = new Text2('_', {
size: 80,
fill: 0x00FF00,
width: 1200
});
nameDisplayText.anchor.set(0.5, 0.5);
nameDisplayText.y = -100;
nameInputContainer.addChild(nameDisplayText);
// Virtual keyboard
var keyboardContainer = new Container();
keyboardContainer.y = 50;
nameInputContainer.addChild(keyboardContainer);
var keyboard = [['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'], ['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'], ['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'], ['Z', 'X', 'C', 'V', 'B', 'N', 'M']];
var keyButtons = [];
for (var row = 0; row < keyboard.length; row++) {
for (var col = 0; col < keyboard[row].length; col++) {
var letter = keyboard[row][col];
var keyButton = new Container();
var keyBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.8,
color: 0x666666
});
keyButton.addChild(keyBg);
var keyText = new Text2(letter, {
size: 40,
fill: 0xFFFFFF
});
keyText.anchor.set(0.5, 0.5);
keyButton.addChild(keyText);
keyButton.x = (col - keyboard[row].length / 2) * 80 + 40;
keyButton.y = row * 80;
keyButton.letter = letter;
keyButton.down = function () {
if (currentPlayerName.length < 12) {
currentPlayerName += this.letter;
updateNameDisplay();
}
};
keyButtons.push(keyButton);
keyboardContainer.addChild(keyButton);
}
}
// Backspace button
var backspaceButton = new Container();
var backspaceBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.8,
alpha: 0.8,
color: 0x8B0000
});
backspaceButton.addChild(backspaceBg);
var backspaceText = new Text2('DELETE', {
size: 35,
fill: 0xFFFFFF
});
backspaceText.anchor.set(0.5, 0.5);
backspaceButton.addChild(backspaceText);
backspaceButton.x = 600;
backspaceButton.y = 160;
backspaceButton.down = function () {
if (currentPlayerName.length > 0) {
currentPlayerName = currentPlayerName.slice(0, -1);
updateNameDisplay();
}
};
keyboardContainer.addChild(backspaceButton);
// Space button
var spaceButton = new Container();
var spaceBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.8,
alpha: 0.8,
color: 0x444444
});
spaceButton.addChild(spaceBg);
var spaceText = new Text2('SPACE', {
size: 35,
fill: 0xFFFFFF
});
spaceText.anchor.set(0.5, 0.5);
spaceButton.addChild(spaceText);
spaceButton.x = 0;
spaceButton.y = 240;
spaceButton.down = function () {
if (currentPlayerName.length < 12 && currentPlayerName.length > 0) {
currentPlayerName += ' ';
updateNameDisplay();
}
};
keyboardContainer.addChild(spaceButton);
// Start button
var startEndlessButton = new Container();
var startEndlessBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 1.5,
alpha: 0.8,
color: 0x228B22
});
startEndlessButton.addChild(startEndlessBg);
var startEndlessText = new Text2('START ENDLESS', {
size: 50,
fill: 0xFFFFFF
});
startEndlessText.anchor.set(0.5, 0.5);
startEndlessButton.addChild(startEndlessText);
startEndlessButton.y = 450;
startEndlessButton.down = function () {
if (currentPlayerName.length > 0) {
storage.currentPlayerName = currentPlayerName;
nameInputContainer.visible = false;
startEndlessMode();
}
};
nameInputContainer.addChild(startEndlessButton);
// Cancel button
var cancelNameButton = new Container();
var cancelNameBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 1.2,
alpha: 0.8,
color: 0x666666
});
cancelNameButton.addChild(cancelNameBg);
var cancelNameText = new Text2('CANCEL', {
size: 40,
fill: 0xFFFFFF
});
cancelNameText.anchor.set(0.5, 0.5);
cancelNameButton.addChild(cancelNameText);
cancelNameButton.x = -300;
cancelNameButton.y = 450;
cancelNameButton.down = function () {
nameInputContainer.visible = false;
menuContainer.visible = true;
currentPlayerName = '';
updateNameDisplay();
};
nameInputContainer.addChild(cancelNameButton);
game.addChild(nameInputContainer);
function updateNameDisplay() {
nameDisplayText.setText(currentPlayerName.length > 0 ? currentPlayerName : '_');
}
function showNameInput() {
currentPlayerName = '';
updateNameDisplay();
nameInputContainer.visible = true;
menuContainer.visible = false;
}
function startEndlessMode() {
endlessMode = true;
showMenu = false;
gameStarted = true;
spawnTimer = 0;
backToMenuButton.visible = true;
waveText.visible = true;
towerCountText.visible = true;
puertaInf.visible = true;
escalerasCiel.visible = true;
// Update victories display
victoriesText.setText('Victories: ' + (storage.victories || 0));
updateRebirthUI();
}
startButton.down = function () {
endlessMode = false;
showMenu = false;
menuContainer.visible = false;
gameStarted = true;
spawnTimer = 0;
backToMenuButton.visible = true;
waveText.visible = true;
towerCountText.visible = true;
puertaInf.visible = true;
escalerasCiel.visible = true;
// Update victories display
victoriesText.setText('Victories: ' + (storage.victories || 0));
updateRebirthUI();
};
// Initialize game
createPath();
updateUI();
updateRebirthUI();
// Start music
LK.playMusic('divine_battle');
game.update = function () {
if (showMenu) {
return; // Don't update game logic while menu is showing
}
if (!gameStarted) {
gameStarted = true;
spawnTimer = 0;
}
// Spawn enemies
if (enemiesSpawned < enemiesInWave) {
spawnTimer++;
if (spawnTimer >= spawnDelay) {
spawnEnemy();
spawnTimer = 0;
}
}
// Check if wave is complete
if (enemiesSpawned >= enemiesInWave && enemies.length === 0 && !waveComplete) {
waveComplete = true;
// Bonus coins for completing wave
coins += 25 + wave * 5;
updateUI();
// Start next wave after delay
LK.setTimeout(function () {
startNextWave();
}, 2000);
}
// Check win condition for wave 15 (only in normal mode)
if (!endlessMode && wave > 15 && enemies.length === 0 && enemiesSpawned >= enemiesInWave) {
// Increment victories
storage.victories = (storage.victories || 0) + 1;
// Show you win and return to menu
LK.showYouWin();
// Reset game state and show menu
showMenu = true;
menuContainer.visible = true;
gameStarted = false;
endlessMode = false;
backToMenuButton.visible = false;
waveText.visible = false;
towerCountText.visible = false;
puertaInf.visible = false;
escalerasCiel.visible = false;
// Reset all game variables to starting state
towers = [];
enemies = [];
projectiles = [];
selectedTower = null;
selectedTowerType = null;
coins = 100;
lives = 20;
wave = 1;
enemiesSpawned = 0;
spawnTimer = 0;
waveComplete = false;
enemiesInWave = 5;
towerInfoPanel.visible = false;
towerSelectPanel.visible = false;
updateUI();
updateRebirthUI();
victoriesText.setText('Victories: ' + (storage.victories || 0));
}
};
demon. In-Game asset. 2d. High contrast. No shadows
javier milei con motosierra. In-Game asset. 2d. High contrast. No shadows
donald trump con bate de beisbol. In-Game asset. 2d. High contrast. No shadows
buddha. In-Game asset. 2d. High contrast. No shadows
cristo. In-Game asset. 2d. High contrast. No shadows
angel. In-Game asset. 2d. High contrast. No shadows
yellow demon. In-Game asset. 2d. High contrast. No shadows
devil. In-Game asset. 2d. High contrast. No shadows
rayos. In-Game asset. 2d. High contrast. No shadows
cristo, buddha y angeles vs milei con motosierra, trump con bate de basebol, devil, demons y yellow demons. In-Game asset. 2d. High contrast. No shadows
puerta del infierno. In-Game asset. 2d. High contrast. No shadows
escaleras al cielo. In-Game asset. 2d. High contrast. No shadows
Alas: Tiene un par de grandes alas emplumadas de color gris claro o blanco, similares a las de un ángel o un grifo, que se extienden prominentemente hacia los lados. Aro dentado/rueda: En el centro del ser hay un gran aro o rueda de color oscuro, posiblemente marrón rojizo o burdeos, con una textura rugosa o dentada, como si estuviera hecho de metal corroído o material orgánico. Este aro tiene aberturas o huecos irregulares a lo largo de su perímetro. Cubo central: Dentro del aro, hay un objeto cúbico, de apariencia más pequeña que el aro, de color oscuro (posiblemente negro o morado muy oscuro). En cada una de las caras visibles de este cubo hay lo que parecen ser ojos rojos brillantes con pupilas oscuras, lo que le da un aspecto vigilante o inquietante.. In-Game asset. 2d. High contrast. No shadows
cervero. In-Game asset. 2d. High contrast. No shadows