Code edit (1 edits merged)
Please save this source code
User prompt
Elimina la mejora de la derecha, cambia el BG del de izquierda por upgradeBG
User prompt
Haz que enemy pueda ser destruido durante la animación de llegar al final ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (2 edits merged)
Please save this source code
User prompt
Haz que empiece mucho antes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Agrega una animacion dónde enemi se va haciendo más pequeño y reduce su opacidad a 0 cuando está apenas llegando al final ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Quita la transparencia de end
User prompt
Haz que el asset se acerque más al penúltimo camino
User prompt
Haz que gire siempre apuntando a la dirección del penúltimo camino ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Agrega un objeto llamado end que siempre se coloca al final del camino
Code edit (1 edits merged)
Please save this source code
User prompt
Suaviza el giro, hazlo parecer natural empezando un poco antes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Hazlo parecer natural empezando un poco antes del centro del siguiente camino ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Suaviza giro del enemigo ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Suaviza el giro de soldado ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Arregla el error que hace que hitbox no funcione cuando está en invisible
User prompt
Crea una variable llamada shotHitbox, cuando es true se ven la hitbox de los enemigos, cuando es false no. Establecerlo en false
User prompt
Agrega hasta 3 sonidos aleatorios
User prompt
Agrega un sonido cuando los enemigos mueren
User prompt
Agrega una variable llamada Cross, si este s uno la bala se destruye al tocar un enemigos si es dos al tocar dos, etc
User prompt
Haz que las balas que no golpean a los enemigos a la primera si le peguen a los demás si es que los toca
User prompt
Agrega 10 oleadas, haz que la aparición entre tropas sea 20% más rápido,aumenta su velocidad un 30%, disminuye el tiempo entre oleadas
User prompt
Haz que enemy gire para que apunte al siguiente camino
User prompt
Haz que se quite la capa cuando bala toque hitbox no enemy
User prompt
Agrega un cuadrado llamado hitbox a los enemigos con una transparencia de 0.2
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('Bala', { anchorX: 0.5, anchorY: 0.5 }); self.target = null; self.speed = 800; // pixels per second self.damage = 0; self.directionX = 0; // Direction components for straight line movement self.directionY = 0; self.isAlive = true; // Set lifespan of 1.3 seconds using tween tween(self, {}, { duration: 1300, onFinish: function onFinish() { if (self.parent) { self.destroy(); } } }); self.update = function () { if (!self.isAlive) { return; } // Move in straight line using direction self.x += self.directionX * self.speed * frameTime; self.y += self.directionY * self.speed * frameTime; // Check collision with target if it still exists if (self.target && self.target.parent) { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distanceSquared = dx * dx + dy * dy; // If close enough to target, hit it (using squared distance to avoid sqrt) if (distanceSquared < 400) { // 20 * 20 = 400 // Apply damage to enemy using its takeDamage method if (self.target.takeDamage) { self.target.takeDamage(self.damage); } // Add damage to tower's total damage counter if (self.towerRef && self.towerRef.parent) { self.towerRef.totalDamage += self.damage; } // Remove bullet self.isAlive = false; self.destroy(); return; } } }; return self; }); // Reusable tower creation function var Gameplay = Container.expand(function () { var self = Container.call(this); var gameplayGraphics = self.attachAsset('gameplayBackground', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var UI = Container.expand(function () { var self = Container.call(this); var uiGraphics = self.attachAsset('uiBackground', { anchorX: 0.5, anchorY: 0.5 }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Reusable tower creation function 7; function createTowerBase(params) { return Container.expand(function () { var self = Container.call(this); // Tower parameters self.damage = params.damage; self.cadence = params.cadence; self.range = params.range; self.rangeSquared = self.range * self.range; // Cache squared range for optimization self.totalDamage = 0; // Track total damage dealt by this tower var areaGraphics = self.attachAsset('Area', { anchorX: 0.5, anchorY: 0.5 }); // Scale the area to match the tower's actual range var areaScale = self.range * 2 / 100; // Area asset is 100x100, so scale to range diameter areaGraphics.scaleX = areaScale; areaGraphics.scaleY = areaScale; areaGraphics.alpha = 0.3; areaGraphics.visible = false; // Hide range area by default var towerGraphics = self.attachAsset(params.asset, { anchorX: 0.5, anchorY: 0.5 }); // Method to show range area self.showRange = function () { areaGraphics.visible = true; }; // Method to hide range area self.hideRange = function () { areaGraphics.visible = false; }; // Tower selection state self.isSelected = false; // Tower placement state self.isPlaced = false; // Shooting properties self.lastShotTime = 0; self.bullets = []; // Method to find enemies in range self.findEnemiesInRange = function () { var enemiesInRange = []; // Check all enemies in the enemies array for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy && enemy.parent) { var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distanceSquared = dx * dx + dy * dy; if (distanceSquared <= self.rangeSquared) { enemiesInRange.push(enemy); } } } return enemiesInRange; }; // Method to shoot at target self.shoot = function (target) { var currentTime = Date.now(); if (currentTime - self.lastShotTime >= self.cadence) { var bullet = new Bullet(); // Calculate current direction to target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Predict where the enemy will be when bullet arrives var bulletSpeed = 800; // Match bullet speed from Bullet class var timeToTarget = distance / bulletSpeed; // Get enemy velocity by checking if it's moving and has a current tween var enemyVelocityX = 0; var enemyVelocityY = 0; if (target.isMoving && target.currentWaypointIndex !== undefined) { // Calculate enemy's current direction based on next waypoint var nextWaypoint = caminoPositions[(target.currentWaypointIndex + 1) % caminoPositions.length]; var enemyDx = nextWaypoint.x - target.x; var enemyDy = nextWaypoint.y - target.y; var enemyDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy); var enemySpeed = 200; // Match enemy speed from moveToNextWaypoint if (enemyDistance > 0) { enemyVelocityX = enemyDx / enemyDistance * enemySpeed; enemyVelocityY = enemyDy / enemyDistance * enemySpeed; } } // Predict enemy position var predictedX = target.x + enemyVelocityX * timeToTarget; var predictedY = target.y + enemyVelocityY * timeToTarget; // Calculate direction to predicted position var predictedDx = predictedX - self.x; var predictedDy = predictedY - self.y; var predictedDistance = Math.sqrt(predictedDx * predictedDx + predictedDy * predictedDy); var offsetDistance = 50; // Distance to spawn bullet ahead of tower // Spawn bullet slightly ahead in the direction of the predicted target bullet.x = self.x + predictedDx / predictedDistance * offsetDistance; bullet.y = self.y + predictedDy / predictedDistance * offsetDistance; // Set bullet direction for straight line movement toward predicted position bullet.directionX = predictedDx / predictedDistance; bullet.directionY = predictedDy / predictedDistance; bullet.target = target; bullet.damage = self.damage; bullet.towerRef = self; // Store reference to the tower that fired this bullet // Rotate bullet to face predicted direction bullet.rotation = Math.atan2(predictedDy, predictedDx); self.bullets.push(bullet); game.addChild(bullet); self.lastShotTime = currentTime; // Play bullet shooting sound LK.getSound('Balababa').play(); // Change to attack sprite var attackSprite = self.attachAsset('TorreinicialAttack', { anchorX: 0.5, anchorY: 0.5 }); attackSprite.rotation = towerGraphics.rotation; attackSprite.scaleY = towerGraphics.scaleY; self.removeChild(towerGraphics); towerGraphics = attackSprite; // Add squash animation to tower tween.stop(towerGraphics, { scaleX: true }); // Stop any existing scale tweens tween(towerGraphics, { scaleX: 0.7 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(towerGraphics, { scaleX: 1.0 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Change back to normal sprite after attack animation var normalSprite = self.attachAsset(params.asset, { anchorX: 0.5, anchorY: 0.5 }); normalSprite.rotation = towerGraphics.rotation; normalSprite.scaleY = towerGraphics.scaleY; self.removeChild(towerGraphics); towerGraphics = normalSprite; } }); } }); } }; // Update method for tower self.update = function () { // Clean up destroyed bullets for (var i = self.bullets.length - 1; i >= 0; i--) { if (!self.bullets[i].parent) { self.bullets.splice(i, 1); } } // Only shoot if tower is placed (check cached status) if (self.isPlaced) { // Find and shoot at enemies var enemies = self.findEnemiesInRange(); if (enemies.length > 0) { var target = enemies[0]; // Calculate angle to target var dx = target.x - self.x; var dy = target.y - self.y; var targetAngle = Math.atan2(dy, dx); // Rotate tower to face target towerGraphics.rotation = targetAngle; // Flip tower horizontally if target is on the left side if (dx < 0) { towerGraphics.scaleY = -1; // Flip on Y axis when target is to the left } else { towerGraphics.scaleY = 1; // Normal orientation when target is to the right } // Shoot at the first enemy found self.shoot(target); } } }; // Handle tower selection self.down = function (x, y, obj) { // Deselect all other towers first for (var i = 0; i < placedTowers.length; i++) { if (placedTowers[i] !== self) { placedTowers[i].isSelected = false; placedTowers[i].hideRange(); } } // Toggle selection for this tower self.isSelected = !self.isSelected; if (self.isSelected) { self.showRange(); updateTowerUpgrade(self); // Update UI with this tower's upgrade options } else { self.hideRange(); updateTowerUpgrade(null); // Clear UI when deselected } }; return self; }); } var Tower = createTowerBase({ damage: 1, // Damage dealt per shot cadence: 1000, // Time between shots in milliseconds (1 second) range: 400, // Range in pixels asset: 'torreInicial' }); var draggedTower = null; var isDragging = false; var placedTowers = []; // Track all placed towers // Cache frequently used values var gameplayBounds = null; var uiBounds = null; var frameTime = 1 / 60; // Cache frame time calculation // Reusable tower creation function that accepts tower constructor function createTowerButton(color, x, y, TowerClass) { var buttonBG = game.addChild(LK.getAsset('BGbuttonTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 })); buttonBG.x = x; buttonBG.y = y; var button = game.addChild(new TowerClass()); button.x = x; button.y = y; button.tint = color; // Store reference to button background for easy access button.buttonBG = buttonBG; button.down = function (x, y, obj) { isDragging = true; draggedTower = game.addChild(new TowerClass()); draggedTower.x = button.x; draggedTower.y = button.y; draggedTower.alpha = 0.7; draggedTower.tint = color; draggedTower.showRange(); // Show range area when dragging }; return button; } // Game down handler to deselect towers when clicking on gameplay area game.down = function (x, y, obj) { // Check if click is in gameplay area (not on UI or towers) var gameplayBounds = getGameplayBounds(); var clickInGameplay = x >= gameplayBounds.left && x <= gameplayBounds.right && y >= gameplayBounds.top && y <= gameplayBounds.bottom; if (clickInGameplay) { // Deselect all towers for (var i = 0; i < placedTowers.length; i++) { placedTowers[i].isSelected = false; placedTowers[i].hideRange(); } updateTowerUpgrade(null); // Clear tower upgrade when no tower is selected } }; // Helper functions for bounds calculations function getGameplayBounds() { return { left: gameplay.x - gameplay.width / 2, right: gameplay.x + gameplay.width / 2, top: gameplay.y - gameplay.height / 2, bottom: gameplay.y + gameplay.height / 2, centerX: gameplay.x, centerY: gameplay.y }; } function getUIBounds() { return { left: ui.x - ui.width / 2, right: ui.x + ui.width / 2, top: ui.y - ui.height / 2, bottom: ui.y + ui.height / 2 }; } function isPointInUI(x, y) { var bounds = getUIBounds(); return x >= bounds.left && x <= bounds.right && y >= bounds.top && y <= bounds.bottom; } function calculateDragOffset(x, y) { var bounds = getGameplayBounds(); var distanceFromCenterX = Math.abs(x - bounds.centerX); var distanceFromCenterY = Math.abs(y - bounds.centerY); var maxDistanceX = gameplay.width / 2; var maxDistanceY = gameplay.height / 2; var normalizedDistanceX = distanceFromCenterX / maxDistanceX; var normalizedDistanceY = distanceFromCenterY / maxDistanceY; var maxOffset = 200; var offsetMagnitudeX = maxOffset * normalizedDistanceX; var offsetMagnitudeY = maxOffset * normalizedDistanceY; var offsetX = 0; var offsetY = 0; if (x >= bounds.centerX && y <= bounds.centerY) { offsetX = offsetMagnitudeX; offsetY = -offsetMagnitudeY; } else if (x <= bounds.centerX && y <= bounds.centerY) { offsetX = -offsetMagnitudeX; offsetY = -offsetMagnitudeY; } else if (x <= bounds.centerX && y >= bounds.centerY) { offsetX = -offsetMagnitudeX; offsetY = offsetMagnitudeY; } else if (x >= bounds.centerX && y >= bounds.centerY) { offsetX = offsetMagnitudeX; offsetY = offsetMagnitudeY; } return { offsetX: offsetX, offsetY: offsetY }; } // Layer management - objects are added in z-index order (bottom to top) // Background layer var gameplay = game.addChild(new Gameplay()); gameplay.x = 1024; gameplay.y = 1093; // Game objects layer (towers will be added here) // UI layer var ui = game.addChild(new UI()); ui.x = 1024; ui.y = 2459; // Create single tower creation button var towerButtons = []; var startX = 400; // Position button on the left side towerButtons.push(createTowerButton(0xffffff, startX, 2459, Tower)); // White tower (normal) // Create path objects with transparency var caminoObjects = []; // Add several camino objects to create a path var caminoPositions = [{ x: 990, y: -100, rotation: 0 }, { x: 890, y: 40, rotation: 12 }, { x: 650, y: 150, rotation: 70 }, { x: 500, y: 340, rotation: 5 }, { x: 500, y: 470, rotation: Math.PI }, { x: 550, y: 540, rotation: -Math.PI / 4 }, { x: 700, y: 620, rotation: 0 }, { x: 900, y: 620, rotation: 0 }, { x: 1100, y: 620, rotation: 0 }, { x: 1250, y: 650, rotation: -20 }, { x: 1420, y: 740, rotation: Math.PI / 4 }, { x: 1530, y: 880, rotation: Math.PI }, { x: 1510, y: 1080, rotation: -12 }, { x: 1380, y: 1210, rotation: 20 }, { x: 1180, y: 1300, rotation: 9 }, { x: 950, y: 1370, rotation: 20 }, { x: 750, y: 1480, rotation: 70 }, { x: 655, y: 1610, rotation: 0 }, { x: 610, y: 1750, rotation: 0 }, { x: 610, y: 1900, rotation: Math.PI / 2 }, { x: 610, y: 2100, rotation: 0 }]; for (var i = 0; i < caminoPositions.length; i++) { var camino = game.addChild(LK.getAsset('camino', { anchorX: 0.5, anchorY: 0.5 })); camino.x = caminoPositions[i].x; camino.y = caminoPositions[i].y; camino.rotation = caminoPositions[i].rotation; // Apply rotation from positions array camino.alpha = 0.2; // Set transparency to 0.2 caminoObjects.push(camino); } // Wave system variables var enemies = []; var waveData = [{ // Wave 1: 10 enemies with 1 layer enemyCount: 10, healthLayers: 1, spawnDelay: 1000 // 1 second between spawns }, { // Wave 2: 15 enemies with 1 layer + 5 enemies with 2 layers enemyCount: 20, healthLayers: 1, // We'll handle mixed health in spawn function spawnDelay: 800 // 0.8 seconds between spawns }]; var currentWaveIndex = 0; var enemiesSpawned = 0; var waveInProgress = false; var nextSpawnTime = 0; // Function to create enemy with specified health layers function createEnemy(healthLayers) { var enemy = game.addChild(LK.getAsset('Enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 })); // Add hitbox to enemy var hitboxGraphics = enemy.attachAsset('hitbox', { anchorX: 0.5, anchorY: 0.5 }); hitboxGraphics.alpha = 0.2; // Set starting position to first camino waypoint enemy.x = caminoPositions[0].x; enemy.y = caminoPositions[0].y; // Path following properties enemy.currentWaypointIndex = 0; enemy.isMoving = false; // Health system for enemy enemy.maxHealth = healthLayers; enemy.currentHealth = enemy.maxHealth; enemy.healthLayers = []; // Create health layers based on healthLayers parameter for (var i = 0; i < healthLayers; i++) { var colors = [0xff0000, 0x0000ff, 0x00ff00]; // Red, Blue, Green enemy.healthLayers.push({ min: i + 1, max: i + 1, color: colors[i % colors.length] }); } // Method to update enemy color based on health enemy.updateHealthColor = function () { for (var i = 0; i < this.healthLayers.length; i++) { var layer = this.healthLayers[i]; if (this.currentHealth >= layer.min && this.currentHealth <= layer.max) { // Animate color transition using tween tween(this, { tint: layer.color }, { duration: 200, easing: tween.easeOut }); break; } } }; // Method to take damage enemy.takeDamage = function (damage) { var oldHealth = this.currentHealth; this.currentHealth = Math.max(0, this.currentHealth - damage); // Check if we crossed a layer boundary var oldLayer = -1; var newLayer = -1; for (var i = 0; i < this.healthLayers.length; i++) { var layer = this.healthLayers[i]; if (oldHealth >= layer.min && oldHealth <= layer.max) { oldLayer = i; } if (this.currentHealth >= layer.min && this.currentHealth <= layer.max) { newLayer = i; } } // Update color if we changed layers or if this is the first damage if (oldLayer !== newLayer || oldHealth === this.maxHealth) { this.updateHealthColor(); } // Check if enemy is dead if (this.currentHealth <= 0) { // Remove from enemies array for (var j = 0; j < enemies.length; j++) { if (enemies[j] === this) { enemies.splice(j, 1); break; } } // Enemy is destroyed, remove it from the game this.destroy(); } }; // Method to move to next waypoint enemy.moveToNextWaypoint = function () { if (this.isMoving) { return; } // Don't start new movement if already moving // Check if we've reached the end of the path if (this.currentWaypointIndex === caminoPositions.length - 1) { // Enemy reached the end - reduce player life based on remaining health layers playerLife -= this.currentHealth; // Update life display lifeText.setText('Life: ' + playerLife); // Check if player is defeated if (playerLife <= 0) { LK.showGameOver(); return; } // Remove from enemies array for (var j = 0; j < enemies.length; j++) { if (enemies[j] === this) { enemies.splice(j, 1); break; } } // Destroy the enemy this.destroy(); return; } this.currentWaypointIndex = (this.currentWaypointIndex + 1) % caminoPositions.length; var nextWaypoint = caminoPositions[this.currentWaypointIndex]; // Calculate distance for movement duration var dx = nextWaypoint.x - this.x; var dy = nextWaypoint.y - this.y; var distance = Math.sqrt(dx * dx + dy * dy); var speed = 200; // pixels per second var duration = distance / speed * 1000; // Convert to milliseconds this.isMoving = true; // Use tween for smooth movement tween(this, { x: nextWaypoint.x, y: nextWaypoint.y }, { duration: duration, easing: tween.linear, onFinish: function onFinish() { enemy.isMoving = false; // Move to next waypoint immediately without delay enemy.moveToNextWaypoint(); } }); }; // Initialize enemy color enemy.updateHealthColor(); // Start movement after a short delay LK.setTimeout(function () { enemy.moveToNextWaypoint(); }, 500); enemies.push(enemy); return enemy; } // Function to start a wave function startWave(waveIndex) { if (waveIndex >= waveData.length) { // All waves completed LK.showYouWin(); return; } currentWaveIndex = waveIndex; enemiesSpawned = 0; waveInProgress = true; nextSpawnTime = Date.now() + 2000; // Start spawning after 2 seconds currentWave = waveIndex + 1; } // Start first wave startWave(0); // Enemy is now stationary - no movement code needed // UI mode management var UI_MODE_TOWER_CREATION = 'creation'; var UI_MODE_TOWER_UPGRADE = 'upgrade'; var currentUIMode = UI_MODE_TOWER_CREATION; // UI Upgrade panel for selected tower var towerUpgradeContainer = game.addChild(new Container()); towerUpgradeContainer.x = 1024; towerUpgradeContainer.y = 2250; var upgradeTitle = towerUpgradeContainer.addChild(new Text2('Upgrade Tower', { size: 100, fill: 0xFFFFFF })); upgradeTitle.anchor.set(0.5, 0); upgradeTitle.x = 0; upgradeTitle.y = 0; // Left upgrade frame - Damage +1 var leftUpgradeFrame = towerUpgradeContainer.addChild(LK.getAsset('uiBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.8 })); leftUpgradeFrame.x = -400; leftUpgradeFrame.y = 180; leftUpgradeFrame.tint = 0x444444; var leftUpgradeTitle = towerUpgradeContainer.addChild(new Text2('Damage +1', { size: 60, fill: 0xFFFFFF })); leftUpgradeTitle.anchor.set(0.5, 0.5); leftUpgradeTitle.x = -400; leftUpgradeTitle.y = 120; var leftUpgradeBuyButton = towerUpgradeContainer.addChild(LK.getAsset('BGbuttonTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 })); leftUpgradeBuyButton.x = -400; leftUpgradeBuyButton.y = 220; leftUpgradeBuyButton.tint = 0x00FF00; var leftUpgradeBuyText = towerUpgradeContainer.addChild(new Text2('BUY', { size: 50, fill: 0x000000 })); leftUpgradeBuyText.anchor.set(0.5, 0.5); leftUpgradeBuyText.x = -400; leftUpgradeBuyText.y = 220; // Right upgrade frame - Fire Rate +30% var rightUpgradeFrame = towerUpgradeContainer.addChild(LK.getAsset('uiBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.8 })); rightUpgradeFrame.x = 400; rightUpgradeFrame.y = 180; rightUpgradeFrame.tint = 0x444444; var rightUpgradeTitle = towerUpgradeContainer.addChild(new Text2('Fire Rate +30%', { size: 50, fill: 0xFFFFFF })); rightUpgradeTitle.anchor.set(0.5, 0.5); rightUpgradeTitle.x = 400; rightUpgradeTitle.y = 120; var rightUpgradeBuyButton = towerUpgradeContainer.addChild(LK.getAsset('BGbuttonTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 })); rightUpgradeBuyButton.x = 400; rightUpgradeBuyButton.y = 220; rightUpgradeBuyButton.tint = 0x00FF00; var rightUpgradeBuyText = towerUpgradeContainer.addChild(new Text2('BUY', { size: 50, fill: 0x000000 })); rightUpgradeBuyText.anchor.set(0.5, 0.5); rightUpgradeBuyText.x = 400; rightUpgradeBuyText.y = 220; // Store reference to currently selected tower var selectedTowerForUpgrade = null; // Function to switch UI modes function setUIMode(mode) { currentUIMode = mode; if (mode === UI_MODE_TOWER_CREATION) { // Show tower creation buttons and their backgrounds for (var i = 0; i < towerButtons.length; i++) { towerButtons[i].visible = true; if (towerButtons[i].buttonBG) { towerButtons[i].buttonBG.visible = true; } } // Hide tower upgrade panel towerUpgradeContainer.visible = false; } else if (mode === UI_MODE_TOWER_UPGRADE) { // Hide tower creation buttons and their backgrounds for (var i = 0; i < towerButtons.length; i++) { towerButtons[i].visible = false; if (towerButtons[i].buttonBG) { towerButtons[i].buttonBG.visible = false; } } // Show tower upgrade panel towerUpgradeContainer.visible = true; } } // Function to update tower upgrade display function updateTowerUpgrade(tower) { if (tower) { // Store reference to selected tower for upgrades selectedTowerForUpgrade = tower; // Switch to tower upgrade mode when a tower is selected setUIMode(UI_MODE_TOWER_UPGRADE); } else { // Clear reference to selected tower selectedTowerForUpgrade = null; // Switch back to tower creation mode when no tower is selected setUIMode(UI_MODE_TOWER_CREATION); } } // Left upgrade buy button click handler - Damage +1 leftUpgradeBuyButton.down = function (x, y, obj) { if (selectedTowerForUpgrade) { selectedTowerForUpgrade.damage += 1; // Flash button to show upgrade LK.effects.flashObject(leftUpgradeBuyButton, 0xFFFFFF, 300); } }; // Right upgrade buy button click handler - Fire Rate +30% rightUpgradeBuyButton.down = function (x, y, obj) { if (selectedTowerForUpgrade) { // Reduce cadence by 30% (faster shooting) selectedTowerForUpgrade.cadence = Math.max(100, Math.floor(selectedTowerForUpgrade.cadence * 0.7)); // Flash button to show upgrade LK.effects.flashObject(rightUpgradeBuyButton, 0xFFFFFF, 300); } }; // Initialize with no tower selected and tower creation mode updateTowerUpgrade(null); setUIMode(UI_MODE_TOWER_CREATION); // Add money, life and wave UI texts on the right side, centered vertically var moneyText = new Text2('Money: 100', { size: 80, fill: 0xFFFF00 }); moneyText.anchor.set(1, 0.5); moneyText.x = 1900; moneyText.y = 2350; game.addChild(moneyText); var lifeText = new Text2('Life: 20', { size: 80, fill: 0xFF0000 }); lifeText.anchor.set(1, 0.5); lifeText.x = 1900; lifeText.y = 2450; game.addChild(lifeText); var waveText = new Text2('Wave: 1', { size: 80, fill: 0x00FF00 }); waveText.anchor.set(1, 0.5); waveText.x = 1900; waveText.y = 2550; game.addChild(waveText); // Initialize game variables var playerMoney = 100; var playerLife = 20; var currentWave = 1; var PathShow = false; // Boolean to control path visibility var vida = 20; // Player's life/health var dinero = 100; // Player's money/currency // Top overlay layer (pointer on top of everything) var puntero = game.addChild(LK.getAsset('Puntero', { anchorX: 0.5, anchorY: 0.5 })); puntero.x = 1024; puntero.y = 1366; puntero.alpha = 0; // Game move handler for dragging game.move = function (x, y, obj) { puntero.x = x; puntero.y = y; if (isDragging && draggedTower) { var gameplayBounds = getGameplayBounds(); var uiBounds = getUIBounds(); var offset = calculateDragOffset(x, y); var targetX = x + offset.offsetX; var targetY = y + offset.offsetY; // Apply boundary constraints if (targetY > uiBounds.top) { targetY = uiBounds.top - 70; } if (targetY < gameplayBounds.top) { targetY = gameplayBounds.top + 70; } if (targetX < uiBounds.left) { targetX = uiBounds.left + 70; } if (targetX > uiBounds.right) { targetX = uiBounds.right - 70; } // Check if tower is too close to existing towers var minDistance = 120; // Minimum distance between towers var minDistanceSquared = minDistance * minDistance; var tooCloseToOtherTowers = false; for (var i = 0; i < placedTowers.length; i++) { var existingTower = placedTowers[i]; var dx = targetX - existingTower.x; var dy = targetY - existingTower.y; var distanceSquared = dx * dx + dy * dy; if (distanceSquared < minDistanceSquared) { tooCloseToOtherTowers = true; break; } } // Check if tower would be placed on camino path var onCamino = false; for (var i = 0; i < caminoObjects.length; i++) { var camino = caminoObjects[i]; var dx = targetX - camino.x; var dy = targetY - camino.y; var distanceSquared = dx * dx + dy * dy; // Check if tower center would overlap with camino (considering both sizes) var overlapDistance = 150; // Reasonable overlap distance if (distanceSquared < overlapDistance * overlapDistance) { onCamino = true; break; } } // Update area tint based on pointer position and tower proximity var pointerInUI = isPointInUI(puntero.x, puntero.y); var cannotPlace = pointerInUI || tooCloseToOtherTowers || onCamino; if (cannotPlace) { // Apply red tint to area when cannot place tower if (draggedTower.children[0]) { draggedTower.children[0].tint = 0xff0000; } } else { // Remove tint from area when tower can be placed if (draggedTower.children[0]) { draggedTower.children[0].tint = 0xFFFFFF; } } // Smooth movement var smoothness = 0.15; draggedTower.x += (targetX - draggedTower.x) * smoothness; draggedTower.y += (targetY - draggedTower.y) * smoothness; } }; // Game update handler game.update = function () { // Update UI texts with current values moneyText.setText('Money: ' + playerMoney); lifeText.setText('Life: ' + playerLife); waveText.setText('Wave: ' + currentWave); // Update path visibility based on PathShow variable for (var i = 0; i < caminoObjects.length; i++) { if (PathShow) { caminoObjects[i].alpha = 0.1; } else { caminoObjects[i].alpha = 0; } } // Wave spawning logic if (waveInProgress && currentWaveIndex < waveData.length) { var currentWaveData = waveData[currentWaveIndex]; var currentTime = Date.now(); if (currentTime >= nextSpawnTime && enemiesSpawned < currentWaveData.enemyCount) { // Determine health layers for this enemy var healthLayers = 1; // Default to 1 layer if (currentWaveIndex === 1) { // Wave 2: First 15 enemies have 1 layer, last 5 have 2 layers if (enemiesSpawned >= 15) { healthLayers = 2; } } // Spawn enemy createEnemy(healthLayers); enemiesSpawned++; nextSpawnTime = currentTime + currentWaveData.spawnDelay; } // Check if wave is complete if (enemiesSpawned >= currentWaveData.enemyCount && enemies.length === 0) { waveInProgress = false; // Start next wave after a delay LK.setTimeout(function () { startWave(currentWaveIndex + 1); }, 3000); // 3 second delay between waves } } }; // Game release handler game.up = function (x, y, obj) { if (isDragging && draggedTower) { var gameplayBounds = getGameplayBounds(); var uiBounds = getUIBounds(); var pointerInUI = isPointInUI(puntero.x, puntero.y); var towerInUI = draggedTower.x >= uiBounds.left && draggedTower.x <= uiBounds.right && draggedTower.y >= uiBounds.top && draggedTower.y <= uiBounds.bottom; if (pointerInUI || towerInUI) { draggedTower.destroy(); } else if (draggedTower.x >= gameplayBounds.left && draggedTower.x <= gameplayBounds.right && draggedTower.y >= gameplayBounds.top && draggedTower.y <= gameplayBounds.bottom) { // Check if tower is too close to existing towers var minDistance = 170; // Minimum distance between towers var minDistanceSquared = minDistance * minDistance; var canPlace = true; for (var i = 0; i < placedTowers.length; i++) { var existingTower = placedTowers[i]; var dx = draggedTower.x - existingTower.x; var dy = draggedTower.y - existingTower.y; var distanceSquared = dx * dx + dy * dy; if (distanceSquared < minDistanceSquared) { canPlace = false; break; } } // Check if tower would be placed on camino path if (canPlace) { for (var i = 0; i < caminoObjects.length; i++) { var camino = caminoObjects[i]; var dx = draggedTower.x - camino.x; var dy = draggedTower.y - camino.y; var distanceSquared = dx * dx + dy * dy; var overlapDistance = 170; if (distanceSquared < overlapDistance * overlapDistance) { canPlace = false; break; } } } if (canPlace) { draggedTower.alpha = 1.0; draggedTower.tint = 0xffffff; draggedTower.hideRange(); // Hide range area when placed draggedTower.isPlaced = true; // Set placement status placedTowers.push(draggedTower); // Add to placed towers array LK.getSound('TorreColocada').play(); // Play tower placement sound } else { // Tower is too close to existing towers, destroy it draggedTower.destroy(); } } else { draggedTower.destroy(); } isDragging = false; draggedTower = null; } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('Bala', {
anchorX: 0.5,
anchorY: 0.5
});
self.target = null;
self.speed = 800; // pixels per second
self.damage = 0;
self.directionX = 0; // Direction components for straight line movement
self.directionY = 0;
self.isAlive = true;
// Set lifespan of 1.3 seconds using tween
tween(self, {}, {
duration: 1300,
onFinish: function onFinish() {
if (self.parent) {
self.destroy();
}
}
});
self.update = function () {
if (!self.isAlive) {
return;
}
// Move in straight line using direction
self.x += self.directionX * self.speed * frameTime;
self.y += self.directionY * self.speed * frameTime;
// Check collision with target if it still exists
if (self.target && self.target.parent) {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distanceSquared = dx * dx + dy * dy;
// If close enough to target, hit it (using squared distance to avoid sqrt)
if (distanceSquared < 400) {
// 20 * 20 = 400
// Apply damage to enemy using its takeDamage method
if (self.target.takeDamage) {
self.target.takeDamage(self.damage);
}
// Add damage to tower's total damage counter
if (self.towerRef && self.towerRef.parent) {
self.towerRef.totalDamage += self.damage;
}
// Remove bullet
self.isAlive = false;
self.destroy();
return;
}
}
};
return self;
});
// Reusable tower creation function
var Gameplay = Container.expand(function () {
var self = Container.call(this);
var gameplayGraphics = self.attachAsset('gameplayBackground', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var UI = Container.expand(function () {
var self = Container.call(this);
var uiGraphics = self.attachAsset('uiBackground', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Reusable tower creation function
7;
function createTowerBase(params) {
return Container.expand(function () {
var self = Container.call(this);
// Tower parameters
self.damage = params.damage;
self.cadence = params.cadence;
self.range = params.range;
self.rangeSquared = self.range * self.range; // Cache squared range for optimization
self.totalDamage = 0; // Track total damage dealt by this tower
var areaGraphics = self.attachAsset('Area', {
anchorX: 0.5,
anchorY: 0.5
});
// Scale the area to match the tower's actual range
var areaScale = self.range * 2 / 100; // Area asset is 100x100, so scale to range diameter
areaGraphics.scaleX = areaScale;
areaGraphics.scaleY = areaScale;
areaGraphics.alpha = 0.3;
areaGraphics.visible = false; // Hide range area by default
var towerGraphics = self.attachAsset(params.asset, {
anchorX: 0.5,
anchorY: 0.5
});
// Method to show range area
self.showRange = function () {
areaGraphics.visible = true;
};
// Method to hide range area
self.hideRange = function () {
areaGraphics.visible = false;
};
// Tower selection state
self.isSelected = false;
// Tower placement state
self.isPlaced = false;
// Shooting properties
self.lastShotTime = 0;
self.bullets = [];
// Method to find enemies in range
self.findEnemiesInRange = function () {
var enemiesInRange = [];
// Check all enemies in the enemies array
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy && enemy.parent) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared <= self.rangeSquared) {
enemiesInRange.push(enemy);
}
}
}
return enemiesInRange;
};
// Method to shoot at target
self.shoot = function (target) {
var currentTime = Date.now();
if (currentTime - self.lastShotTime >= self.cadence) {
var bullet = new Bullet();
// Calculate current direction to target
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Predict where the enemy will be when bullet arrives
var bulletSpeed = 800; // Match bullet speed from Bullet class
var timeToTarget = distance / bulletSpeed;
// Get enemy velocity by checking if it's moving and has a current tween
var enemyVelocityX = 0;
var enemyVelocityY = 0;
if (target.isMoving && target.currentWaypointIndex !== undefined) {
// Calculate enemy's current direction based on next waypoint
var nextWaypoint = caminoPositions[(target.currentWaypointIndex + 1) % caminoPositions.length];
var enemyDx = nextWaypoint.x - target.x;
var enemyDy = nextWaypoint.y - target.y;
var enemyDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy);
var enemySpeed = 200; // Match enemy speed from moveToNextWaypoint
if (enemyDistance > 0) {
enemyVelocityX = enemyDx / enemyDistance * enemySpeed;
enemyVelocityY = enemyDy / enemyDistance * enemySpeed;
}
}
// Predict enemy position
var predictedX = target.x + enemyVelocityX * timeToTarget;
var predictedY = target.y + enemyVelocityY * timeToTarget;
// Calculate direction to predicted position
var predictedDx = predictedX - self.x;
var predictedDy = predictedY - self.y;
var predictedDistance = Math.sqrt(predictedDx * predictedDx + predictedDy * predictedDy);
var offsetDistance = 50; // Distance to spawn bullet ahead of tower
// Spawn bullet slightly ahead in the direction of the predicted target
bullet.x = self.x + predictedDx / predictedDistance * offsetDistance;
bullet.y = self.y + predictedDy / predictedDistance * offsetDistance;
// Set bullet direction for straight line movement toward predicted position
bullet.directionX = predictedDx / predictedDistance;
bullet.directionY = predictedDy / predictedDistance;
bullet.target = target;
bullet.damage = self.damage;
bullet.towerRef = self; // Store reference to the tower that fired this bullet
// Rotate bullet to face predicted direction
bullet.rotation = Math.atan2(predictedDy, predictedDx);
self.bullets.push(bullet);
game.addChild(bullet);
self.lastShotTime = currentTime;
// Play bullet shooting sound
LK.getSound('Balababa').play();
// Change to attack sprite
var attackSprite = self.attachAsset('TorreinicialAttack', {
anchorX: 0.5,
anchorY: 0.5
});
attackSprite.rotation = towerGraphics.rotation;
attackSprite.scaleY = towerGraphics.scaleY;
self.removeChild(towerGraphics);
towerGraphics = attackSprite;
// Add squash animation to tower
tween.stop(towerGraphics, {
scaleX: true
}); // Stop any existing scale tweens
tween(towerGraphics, {
scaleX: 0.7
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1.0
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Change back to normal sprite after attack animation
var normalSprite = self.attachAsset(params.asset, {
anchorX: 0.5,
anchorY: 0.5
});
normalSprite.rotation = towerGraphics.rotation;
normalSprite.scaleY = towerGraphics.scaleY;
self.removeChild(towerGraphics);
towerGraphics = normalSprite;
}
});
}
});
}
};
// Update method for tower
self.update = function () {
// Clean up destroyed bullets
for (var i = self.bullets.length - 1; i >= 0; i--) {
if (!self.bullets[i].parent) {
self.bullets.splice(i, 1);
}
}
// Only shoot if tower is placed (check cached status)
if (self.isPlaced) {
// Find and shoot at enemies
var enemies = self.findEnemiesInRange();
if (enemies.length > 0) {
var target = enemies[0];
// Calculate angle to target
var dx = target.x - self.x;
var dy = target.y - self.y;
var targetAngle = Math.atan2(dy, dx);
// Rotate tower to face target
towerGraphics.rotation = targetAngle;
// Flip tower horizontally if target is on the left side
if (dx < 0) {
towerGraphics.scaleY = -1; // Flip on Y axis when target is to the left
} else {
towerGraphics.scaleY = 1; // Normal orientation when target is to the right
}
// Shoot at the first enemy found
self.shoot(target);
}
}
};
// Handle tower selection
self.down = function (x, y, obj) {
// Deselect all other towers first
for (var i = 0; i < placedTowers.length; i++) {
if (placedTowers[i] !== self) {
placedTowers[i].isSelected = false;
placedTowers[i].hideRange();
}
}
// Toggle selection for this tower
self.isSelected = !self.isSelected;
if (self.isSelected) {
self.showRange();
updateTowerUpgrade(self); // Update UI with this tower's upgrade options
} else {
self.hideRange();
updateTowerUpgrade(null); // Clear UI when deselected
}
};
return self;
});
}
var Tower = createTowerBase({
damage: 1,
// Damage dealt per shot
cadence: 1000,
// Time between shots in milliseconds (1 second)
range: 400,
// Range in pixels
asset: 'torreInicial'
});
var draggedTower = null;
var isDragging = false;
var placedTowers = []; // Track all placed towers
// Cache frequently used values
var gameplayBounds = null;
var uiBounds = null;
var frameTime = 1 / 60; // Cache frame time calculation
// Reusable tower creation function that accepts tower constructor
function createTowerButton(color, x, y, TowerClass) {
var buttonBG = game.addChild(LK.getAsset('BGbuttonTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
}));
buttonBG.x = x;
buttonBG.y = y;
var button = game.addChild(new TowerClass());
button.x = x;
button.y = y;
button.tint = color;
// Store reference to button background for easy access
button.buttonBG = buttonBG;
button.down = function (x, y, obj) {
isDragging = true;
draggedTower = game.addChild(new TowerClass());
draggedTower.x = button.x;
draggedTower.y = button.y;
draggedTower.alpha = 0.7;
draggedTower.tint = color;
draggedTower.showRange(); // Show range area when dragging
};
return button;
}
// Game down handler to deselect towers when clicking on gameplay area
game.down = function (x, y, obj) {
// Check if click is in gameplay area (not on UI or towers)
var gameplayBounds = getGameplayBounds();
var clickInGameplay = x >= gameplayBounds.left && x <= gameplayBounds.right && y >= gameplayBounds.top && y <= gameplayBounds.bottom;
if (clickInGameplay) {
// Deselect all towers
for (var i = 0; i < placedTowers.length; i++) {
placedTowers[i].isSelected = false;
placedTowers[i].hideRange();
}
updateTowerUpgrade(null); // Clear tower upgrade when no tower is selected
}
};
// Helper functions for bounds calculations
function getGameplayBounds() {
return {
left: gameplay.x - gameplay.width / 2,
right: gameplay.x + gameplay.width / 2,
top: gameplay.y - gameplay.height / 2,
bottom: gameplay.y + gameplay.height / 2,
centerX: gameplay.x,
centerY: gameplay.y
};
}
function getUIBounds() {
return {
left: ui.x - ui.width / 2,
right: ui.x + ui.width / 2,
top: ui.y - ui.height / 2,
bottom: ui.y + ui.height / 2
};
}
function isPointInUI(x, y) {
var bounds = getUIBounds();
return x >= bounds.left && x <= bounds.right && y >= bounds.top && y <= bounds.bottom;
}
function calculateDragOffset(x, y) {
var bounds = getGameplayBounds();
var distanceFromCenterX = Math.abs(x - bounds.centerX);
var distanceFromCenterY = Math.abs(y - bounds.centerY);
var maxDistanceX = gameplay.width / 2;
var maxDistanceY = gameplay.height / 2;
var normalizedDistanceX = distanceFromCenterX / maxDistanceX;
var normalizedDistanceY = distanceFromCenterY / maxDistanceY;
var maxOffset = 200;
var offsetMagnitudeX = maxOffset * normalizedDistanceX;
var offsetMagnitudeY = maxOffset * normalizedDistanceY;
var offsetX = 0;
var offsetY = 0;
if (x >= bounds.centerX && y <= bounds.centerY) {
offsetX = offsetMagnitudeX;
offsetY = -offsetMagnitudeY;
} else if (x <= bounds.centerX && y <= bounds.centerY) {
offsetX = -offsetMagnitudeX;
offsetY = -offsetMagnitudeY;
} else if (x <= bounds.centerX && y >= bounds.centerY) {
offsetX = -offsetMagnitudeX;
offsetY = offsetMagnitudeY;
} else if (x >= bounds.centerX && y >= bounds.centerY) {
offsetX = offsetMagnitudeX;
offsetY = offsetMagnitudeY;
}
return {
offsetX: offsetX,
offsetY: offsetY
};
}
// Layer management - objects are added in z-index order (bottom to top)
// Background layer
var gameplay = game.addChild(new Gameplay());
gameplay.x = 1024;
gameplay.y = 1093;
// Game objects layer (towers will be added here)
// UI layer
var ui = game.addChild(new UI());
ui.x = 1024;
ui.y = 2459;
// Create single tower creation button
var towerButtons = [];
var startX = 400; // Position button on the left side
towerButtons.push(createTowerButton(0xffffff, startX, 2459, Tower)); // White tower (normal)
// Create path objects with transparency
var caminoObjects = [];
// Add several camino objects to create a path
var caminoPositions = [{
x: 990,
y: -100,
rotation: 0
}, {
x: 890,
y: 40,
rotation: 12
}, {
x: 650,
y: 150,
rotation: 70
}, {
x: 500,
y: 340,
rotation: 5
}, {
x: 500,
y: 470,
rotation: Math.PI
}, {
x: 550,
y: 540,
rotation: -Math.PI / 4
}, {
x: 700,
y: 620,
rotation: 0
}, {
x: 900,
y: 620,
rotation: 0
}, {
x: 1100,
y: 620,
rotation: 0
}, {
x: 1250,
y: 650,
rotation: -20
}, {
x: 1420,
y: 740,
rotation: Math.PI / 4
}, {
x: 1530,
y: 880,
rotation: Math.PI
}, {
x: 1510,
y: 1080,
rotation: -12
}, {
x: 1380,
y: 1210,
rotation: 20
}, {
x: 1180,
y: 1300,
rotation: 9
}, {
x: 950,
y: 1370,
rotation: 20
}, {
x: 750,
y: 1480,
rotation: 70
}, {
x: 655,
y: 1610,
rotation: 0
}, {
x: 610,
y: 1750,
rotation: 0
}, {
x: 610,
y: 1900,
rotation: Math.PI / 2
}, {
x: 610,
y: 2100,
rotation: 0
}];
for (var i = 0; i < caminoPositions.length; i++) {
var camino = game.addChild(LK.getAsset('camino', {
anchorX: 0.5,
anchorY: 0.5
}));
camino.x = caminoPositions[i].x;
camino.y = caminoPositions[i].y;
camino.rotation = caminoPositions[i].rotation; // Apply rotation from positions array
camino.alpha = 0.2; // Set transparency to 0.2
caminoObjects.push(camino);
}
// Wave system variables
var enemies = [];
var waveData = [{
// Wave 1: 10 enemies with 1 layer
enemyCount: 10,
healthLayers: 1,
spawnDelay: 1000 // 1 second between spawns
}, {
// Wave 2: 15 enemies with 1 layer + 5 enemies with 2 layers
enemyCount: 20,
healthLayers: 1,
// We'll handle mixed health in spawn function
spawnDelay: 800 // 0.8 seconds between spawns
}];
var currentWaveIndex = 0;
var enemiesSpawned = 0;
var waveInProgress = false;
var nextSpawnTime = 0;
// Function to create enemy with specified health layers
function createEnemy(healthLayers) {
var enemy = game.addChild(LK.getAsset('Enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
}));
// Add hitbox to enemy
var hitboxGraphics = enemy.attachAsset('hitbox', {
anchorX: 0.5,
anchorY: 0.5
});
hitboxGraphics.alpha = 0.2;
// Set starting position to first camino waypoint
enemy.x = caminoPositions[0].x;
enemy.y = caminoPositions[0].y;
// Path following properties
enemy.currentWaypointIndex = 0;
enemy.isMoving = false;
// Health system for enemy
enemy.maxHealth = healthLayers;
enemy.currentHealth = enemy.maxHealth;
enemy.healthLayers = [];
// Create health layers based on healthLayers parameter
for (var i = 0; i < healthLayers; i++) {
var colors = [0xff0000, 0x0000ff, 0x00ff00]; // Red, Blue, Green
enemy.healthLayers.push({
min: i + 1,
max: i + 1,
color: colors[i % colors.length]
});
}
// Method to update enemy color based on health
enemy.updateHealthColor = function () {
for (var i = 0; i < this.healthLayers.length; i++) {
var layer = this.healthLayers[i];
if (this.currentHealth >= layer.min && this.currentHealth <= layer.max) {
// Animate color transition using tween
tween(this, {
tint: layer.color
}, {
duration: 200,
easing: tween.easeOut
});
break;
}
}
};
// Method to take damage
enemy.takeDamage = function (damage) {
var oldHealth = this.currentHealth;
this.currentHealth = Math.max(0, this.currentHealth - damage);
// Check if we crossed a layer boundary
var oldLayer = -1;
var newLayer = -1;
for (var i = 0; i < this.healthLayers.length; i++) {
var layer = this.healthLayers[i];
if (oldHealth >= layer.min && oldHealth <= layer.max) {
oldLayer = i;
}
if (this.currentHealth >= layer.min && this.currentHealth <= layer.max) {
newLayer = i;
}
}
// Update color if we changed layers or if this is the first damage
if (oldLayer !== newLayer || oldHealth === this.maxHealth) {
this.updateHealthColor();
}
// Check if enemy is dead
if (this.currentHealth <= 0) {
// Remove from enemies array
for (var j = 0; j < enemies.length; j++) {
if (enemies[j] === this) {
enemies.splice(j, 1);
break;
}
}
// Enemy is destroyed, remove it from the game
this.destroy();
}
};
// Method to move to next waypoint
enemy.moveToNextWaypoint = function () {
if (this.isMoving) {
return;
} // Don't start new movement if already moving
// Check if we've reached the end of the path
if (this.currentWaypointIndex === caminoPositions.length - 1) {
// Enemy reached the end - reduce player life based on remaining health layers
playerLife -= this.currentHealth;
// Update life display
lifeText.setText('Life: ' + playerLife);
// Check if player is defeated
if (playerLife <= 0) {
LK.showGameOver();
return;
}
// Remove from enemies array
for (var j = 0; j < enemies.length; j++) {
if (enemies[j] === this) {
enemies.splice(j, 1);
break;
}
}
// Destroy the enemy
this.destroy();
return;
}
this.currentWaypointIndex = (this.currentWaypointIndex + 1) % caminoPositions.length;
var nextWaypoint = caminoPositions[this.currentWaypointIndex];
// Calculate distance for movement duration
var dx = nextWaypoint.x - this.x;
var dy = nextWaypoint.y - this.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var speed = 200; // pixels per second
var duration = distance / speed * 1000; // Convert to milliseconds
this.isMoving = true;
// Use tween for smooth movement
tween(this, {
x: nextWaypoint.x,
y: nextWaypoint.y
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
enemy.isMoving = false;
// Move to next waypoint immediately without delay
enemy.moveToNextWaypoint();
}
});
};
// Initialize enemy color
enemy.updateHealthColor();
// Start movement after a short delay
LK.setTimeout(function () {
enemy.moveToNextWaypoint();
}, 500);
enemies.push(enemy);
return enemy;
}
// Function to start a wave
function startWave(waveIndex) {
if (waveIndex >= waveData.length) {
// All waves completed
LK.showYouWin();
return;
}
currentWaveIndex = waveIndex;
enemiesSpawned = 0;
waveInProgress = true;
nextSpawnTime = Date.now() + 2000; // Start spawning after 2 seconds
currentWave = waveIndex + 1;
}
// Start first wave
startWave(0);
// Enemy is now stationary - no movement code needed
// UI mode management
var UI_MODE_TOWER_CREATION = 'creation';
var UI_MODE_TOWER_UPGRADE = 'upgrade';
var currentUIMode = UI_MODE_TOWER_CREATION;
// UI Upgrade panel for selected tower
var towerUpgradeContainer = game.addChild(new Container());
towerUpgradeContainer.x = 1024;
towerUpgradeContainer.y = 2250;
var upgradeTitle = towerUpgradeContainer.addChild(new Text2('Upgrade Tower', {
size: 100,
fill: 0xFFFFFF
}));
upgradeTitle.anchor.set(0.5, 0);
upgradeTitle.x = 0;
upgradeTitle.y = 0;
// Left upgrade frame - Damage +1
var leftUpgradeFrame = towerUpgradeContainer.addChild(LK.getAsset('uiBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.8
}));
leftUpgradeFrame.x = -400;
leftUpgradeFrame.y = 180;
leftUpgradeFrame.tint = 0x444444;
var leftUpgradeTitle = towerUpgradeContainer.addChild(new Text2('Damage +1', {
size: 60,
fill: 0xFFFFFF
}));
leftUpgradeTitle.anchor.set(0.5, 0.5);
leftUpgradeTitle.x = -400;
leftUpgradeTitle.y = 120;
var leftUpgradeBuyButton = towerUpgradeContainer.addChild(LK.getAsset('BGbuttonTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
}));
leftUpgradeBuyButton.x = -400;
leftUpgradeBuyButton.y = 220;
leftUpgradeBuyButton.tint = 0x00FF00;
var leftUpgradeBuyText = towerUpgradeContainer.addChild(new Text2('BUY', {
size: 50,
fill: 0x000000
}));
leftUpgradeBuyText.anchor.set(0.5, 0.5);
leftUpgradeBuyText.x = -400;
leftUpgradeBuyText.y = 220;
// Right upgrade frame - Fire Rate +30%
var rightUpgradeFrame = towerUpgradeContainer.addChild(LK.getAsset('uiBackground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.8
}));
rightUpgradeFrame.x = 400;
rightUpgradeFrame.y = 180;
rightUpgradeFrame.tint = 0x444444;
var rightUpgradeTitle = towerUpgradeContainer.addChild(new Text2('Fire Rate +30%', {
size: 50,
fill: 0xFFFFFF
}));
rightUpgradeTitle.anchor.set(0.5, 0.5);
rightUpgradeTitle.x = 400;
rightUpgradeTitle.y = 120;
var rightUpgradeBuyButton = towerUpgradeContainer.addChild(LK.getAsset('BGbuttonTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
}));
rightUpgradeBuyButton.x = 400;
rightUpgradeBuyButton.y = 220;
rightUpgradeBuyButton.tint = 0x00FF00;
var rightUpgradeBuyText = towerUpgradeContainer.addChild(new Text2('BUY', {
size: 50,
fill: 0x000000
}));
rightUpgradeBuyText.anchor.set(0.5, 0.5);
rightUpgradeBuyText.x = 400;
rightUpgradeBuyText.y = 220;
// Store reference to currently selected tower
var selectedTowerForUpgrade = null;
// Function to switch UI modes
function setUIMode(mode) {
currentUIMode = mode;
if (mode === UI_MODE_TOWER_CREATION) {
// Show tower creation buttons and their backgrounds
for (var i = 0; i < towerButtons.length; i++) {
towerButtons[i].visible = true;
if (towerButtons[i].buttonBG) {
towerButtons[i].buttonBG.visible = true;
}
}
// Hide tower upgrade panel
towerUpgradeContainer.visible = false;
} else if (mode === UI_MODE_TOWER_UPGRADE) {
// Hide tower creation buttons and their backgrounds
for (var i = 0; i < towerButtons.length; i++) {
towerButtons[i].visible = false;
if (towerButtons[i].buttonBG) {
towerButtons[i].buttonBG.visible = false;
}
}
// Show tower upgrade panel
towerUpgradeContainer.visible = true;
}
}
// Function to update tower upgrade display
function updateTowerUpgrade(tower) {
if (tower) {
// Store reference to selected tower for upgrades
selectedTowerForUpgrade = tower;
// Switch to tower upgrade mode when a tower is selected
setUIMode(UI_MODE_TOWER_UPGRADE);
} else {
// Clear reference to selected tower
selectedTowerForUpgrade = null;
// Switch back to tower creation mode when no tower is selected
setUIMode(UI_MODE_TOWER_CREATION);
}
}
// Left upgrade buy button click handler - Damage +1
leftUpgradeBuyButton.down = function (x, y, obj) {
if (selectedTowerForUpgrade) {
selectedTowerForUpgrade.damage += 1;
// Flash button to show upgrade
LK.effects.flashObject(leftUpgradeBuyButton, 0xFFFFFF, 300);
}
};
// Right upgrade buy button click handler - Fire Rate +30%
rightUpgradeBuyButton.down = function (x, y, obj) {
if (selectedTowerForUpgrade) {
// Reduce cadence by 30% (faster shooting)
selectedTowerForUpgrade.cadence = Math.max(100, Math.floor(selectedTowerForUpgrade.cadence * 0.7));
// Flash button to show upgrade
LK.effects.flashObject(rightUpgradeBuyButton, 0xFFFFFF, 300);
}
};
// Initialize with no tower selected and tower creation mode
updateTowerUpgrade(null);
setUIMode(UI_MODE_TOWER_CREATION);
// Add money, life and wave UI texts on the right side, centered vertically
var moneyText = new Text2('Money: 100', {
size: 80,
fill: 0xFFFF00
});
moneyText.anchor.set(1, 0.5);
moneyText.x = 1900;
moneyText.y = 2350;
game.addChild(moneyText);
var lifeText = new Text2('Life: 20', {
size: 80,
fill: 0xFF0000
});
lifeText.anchor.set(1, 0.5);
lifeText.x = 1900;
lifeText.y = 2450;
game.addChild(lifeText);
var waveText = new Text2('Wave: 1', {
size: 80,
fill: 0x00FF00
});
waveText.anchor.set(1, 0.5);
waveText.x = 1900;
waveText.y = 2550;
game.addChild(waveText);
// Initialize game variables
var playerMoney = 100;
var playerLife = 20;
var currentWave = 1;
var PathShow = false; // Boolean to control path visibility
var vida = 20; // Player's life/health
var dinero = 100; // Player's money/currency
// Top overlay layer (pointer on top of everything)
var puntero = game.addChild(LK.getAsset('Puntero', {
anchorX: 0.5,
anchorY: 0.5
}));
puntero.x = 1024;
puntero.y = 1366;
puntero.alpha = 0;
// Game move handler for dragging
game.move = function (x, y, obj) {
puntero.x = x;
puntero.y = y;
if (isDragging && draggedTower) {
var gameplayBounds = getGameplayBounds();
var uiBounds = getUIBounds();
var offset = calculateDragOffset(x, y);
var targetX = x + offset.offsetX;
var targetY = y + offset.offsetY;
// Apply boundary constraints
if (targetY > uiBounds.top) {
targetY = uiBounds.top - 70;
}
if (targetY < gameplayBounds.top) {
targetY = gameplayBounds.top + 70;
}
if (targetX < uiBounds.left) {
targetX = uiBounds.left + 70;
}
if (targetX > uiBounds.right) {
targetX = uiBounds.right - 70;
}
// Check if tower is too close to existing towers
var minDistance = 120; // Minimum distance between towers
var minDistanceSquared = minDistance * minDistance;
var tooCloseToOtherTowers = false;
for (var i = 0; i < placedTowers.length; i++) {
var existingTower = placedTowers[i];
var dx = targetX - existingTower.x;
var dy = targetY - existingTower.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared < minDistanceSquared) {
tooCloseToOtherTowers = true;
break;
}
}
// Check if tower would be placed on camino path
var onCamino = false;
for (var i = 0; i < caminoObjects.length; i++) {
var camino = caminoObjects[i];
var dx = targetX - camino.x;
var dy = targetY - camino.y;
var distanceSquared = dx * dx + dy * dy;
// Check if tower center would overlap with camino (considering both sizes)
var overlapDistance = 150; // Reasonable overlap distance
if (distanceSquared < overlapDistance * overlapDistance) {
onCamino = true;
break;
}
}
// Update area tint based on pointer position and tower proximity
var pointerInUI = isPointInUI(puntero.x, puntero.y);
var cannotPlace = pointerInUI || tooCloseToOtherTowers || onCamino;
if (cannotPlace) {
// Apply red tint to area when cannot place tower
if (draggedTower.children[0]) {
draggedTower.children[0].tint = 0xff0000;
}
} else {
// Remove tint from area when tower can be placed
if (draggedTower.children[0]) {
draggedTower.children[0].tint = 0xFFFFFF;
}
}
// Smooth movement
var smoothness = 0.15;
draggedTower.x += (targetX - draggedTower.x) * smoothness;
draggedTower.y += (targetY - draggedTower.y) * smoothness;
}
};
// Game update handler
game.update = function () {
// Update UI texts with current values
moneyText.setText('Money: ' + playerMoney);
lifeText.setText('Life: ' + playerLife);
waveText.setText('Wave: ' + currentWave);
// Update path visibility based on PathShow variable
for (var i = 0; i < caminoObjects.length; i++) {
if (PathShow) {
caminoObjects[i].alpha = 0.1;
} else {
caminoObjects[i].alpha = 0;
}
}
// Wave spawning logic
if (waveInProgress && currentWaveIndex < waveData.length) {
var currentWaveData = waveData[currentWaveIndex];
var currentTime = Date.now();
if (currentTime >= nextSpawnTime && enemiesSpawned < currentWaveData.enemyCount) {
// Determine health layers for this enemy
var healthLayers = 1; // Default to 1 layer
if (currentWaveIndex === 1) {
// Wave 2: First 15 enemies have 1 layer, last 5 have 2 layers
if (enemiesSpawned >= 15) {
healthLayers = 2;
}
}
// Spawn enemy
createEnemy(healthLayers);
enemiesSpawned++;
nextSpawnTime = currentTime + currentWaveData.spawnDelay;
}
// Check if wave is complete
if (enemiesSpawned >= currentWaveData.enemyCount && enemies.length === 0) {
waveInProgress = false;
// Start next wave after a delay
LK.setTimeout(function () {
startWave(currentWaveIndex + 1);
}, 3000); // 3 second delay between waves
}
}
};
// Game release handler
game.up = function (x, y, obj) {
if (isDragging && draggedTower) {
var gameplayBounds = getGameplayBounds();
var uiBounds = getUIBounds();
var pointerInUI = isPointInUI(puntero.x, puntero.y);
var towerInUI = draggedTower.x >= uiBounds.left && draggedTower.x <= uiBounds.right && draggedTower.y >= uiBounds.top && draggedTower.y <= uiBounds.bottom;
if (pointerInUI || towerInUI) {
draggedTower.destroy();
} else if (draggedTower.x >= gameplayBounds.left && draggedTower.x <= gameplayBounds.right && draggedTower.y >= gameplayBounds.top && draggedTower.y <= gameplayBounds.bottom) {
// Check if tower is too close to existing towers
var minDistance = 170; // Minimum distance between towers
var minDistanceSquared = minDistance * minDistance;
var canPlace = true;
for (var i = 0; i < placedTowers.length; i++) {
var existingTower = placedTowers[i];
var dx = draggedTower.x - existingTower.x;
var dy = draggedTower.y - existingTower.y;
var distanceSquared = dx * dx + dy * dy;
if (distanceSquared < minDistanceSquared) {
canPlace = false;
break;
}
}
// Check if tower would be placed on camino path
if (canPlace) {
for (var i = 0; i < caminoObjects.length; i++) {
var camino = caminoObjects[i];
var dx = draggedTower.x - camino.x;
var dy = draggedTower.y - camino.y;
var distanceSquared = dx * dx + dy * dy;
var overlapDistance = 170;
if (distanceSquared < overlapDistance * overlapDistance) {
canPlace = false;
break;
}
}
}
if (canPlace) {
draggedTower.alpha = 1.0;
draggedTower.tint = 0xffffff;
draggedTower.hideRange(); // Hide range area when placed
draggedTower.isPlaced = true; // Set placement status
placedTowers.push(draggedTower); // Add to placed towers array
LK.getSound('TorreColocada').play(); // Play tower placement sound
} else {
// Tower is too close to existing towers, destroy it
draggedTower.destroy();
}
} else {
draggedTower.destroy();
}
isDragging = false;
draggedTower = null;
}
};