User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'visible')' in or related to this line: 'puntero.visible = visible;' Line Number: 2249
User prompt
Please fix the bug: 'game.addChild(...) is not a function' in or related to this line: 'return self;' Line Number: 1088
User prompt
Please fix the bug: 'game.addChild(...) is not a function' in or related to this line: 'return self;' Line Number: 835
User prompt
Please fix the bug: 'game.addChild(...) is not a function' in or related to this line: 'return self;' Line Number: 837
User prompt
Agrega una variable para cambiar el fondo de gameplay para permitir múltiples mapas
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'visible')' in or related to this line: 'puntero.visible = visible;' Line Number: 2249
User prompt
Agrega un menú inicial
User prompt
Agrega un menú inicial
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < enemies.length; i++) {' Line Number: 1062
User prompt
Agrega un menú inicial y un botón play con selección de mapas con un display de como se ve y su nombre debajo (agrega a cada mapa nuevo 3 caminos para tener una base modificable)
User prompt
Agrega un menú inicial y un botón play con selección de mapas con un display de como se ve y su nombre debajo (agrega cada mapa con 3 caminos para tener una base modificable)
User prompt
Asigna los caminos actuales como parte de mapa 1 para poder soportar nuevos mapas en el futuro
User prompt
Crea una nueva página por separado de menú y uno de niveles
User prompt
Haz que las mejoras de la izquierda sean 1: +10% de rango.2: +2 segundos de congelación. 3: quita 1 capa al congelar. 4: El 30% de los enemigos en el rango tienen una relentizacion del 100% (se mantienen en su posición) por 1 segundo) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que las mejoras de la izquierda de la torre de hielo sean 1: +10% de rango.2: +2 segundos de congelación. 3: quita 1 capa al congelar. 4: El 30% de los enemigos en el rango tienen una relentizacion del 100% (se mantienen en su posición) por 1 segundo) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz el sistema de mejoras flexibles para permitir distintas habilidades individuales para cada a torre
User prompt
Haz que las mejoras de la izquierda sean 1: +10% de rango.2: +2 segundos de congelación. 3: quita 1 capa al congelar. 4: El 30% de los enemigos en el rango tienen una relentizacion del 100% (se mantienen en su posición) por 1 segundo) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que el efecto de congelación dure 1 segundos después de ataque ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que la torre de hielo en vez de quitar capas aplique un efecto de relentizar del 20% a los enemigos
User prompt
Elimina las habilidades actuales de la torre de hielo
User prompt
Haz que torre de hielo no dispare y ataque a todos los enemigos de su rango con una onda espansiva (haz una animación donde se va expandiendo y cobre todo el rango) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Elimina las mejoras de la torre de hielo
User prompt
Elimina el efecto de tinte a la torre de hielo y el tinte en los botones de crear torre
User prompt
Agrega una animación diferente a la torre de hielo con sus propios asset y que no que los asset del inicil ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Agrega dos torres nuevas sin habilidades y ataque básico: icetower y FireTower
/**** * 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; self.hitCount = 0; // Track number of enemies hit by this bullet self.hitEnemies = []; // Track which specific enemies have been hit to prevent double-hitting // Calculate lifespan based on range and bullet speed with 20% buffer var bulletSpeed = 800; // Base bullet speed in pixels per second // Apply fastGame speed boost to bullet speed for lifespan calculation if (fastGame) { bulletSpeed = bulletSpeed * 1.5; // 50% faster when fastGame is true } var towerRange = self.towerRef ? self.towerRef.range : 400; // Use tower's range or default to 400 var rangeBasedTravelTime = towerRange / bulletSpeed * 1000; // Convert to milliseconds var baseDuration = rangeBasedTravelTime * 1.2; // 20% more than range travel time var duration = baseDuration; // Apply duration multiplier if tower reference exists and has the multiplier if (self.towerRef && self.towerRef.bulletDurationMultiplier) { duration = baseDuration * self.towerRef.bulletDurationMultiplier; } tween(self, {}, { duration: duration, onFinish: function onFinish() { if (self.parent) { self.destroy(); } } }); self.update = function () { if (!self.isAlive) { return; } // Move in straight line using direction var effectiveSpeed = self.speed; // Apply fastGame speed boost to bullet movement (50% faster) if (fastGame) { effectiveSpeed = self.speed * 1.5; } self.x += self.directionX * effectiveSpeed * frameTime; self.y += self.directionY * effectiveSpeed * frameTime; // Check collision with all enemies, not just the original target for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; if (enemy && enemy.parent) { // Skip enemies that have already been hit by this bullet if (self.hitEnemies && self.hitEnemies.indexOf(enemy) !== -1) { continue; } // Check collision with enemy's hitbox instead of enemy center var hitbox = null; // Find the hitbox child in the enemy for (var i = 0; i < enemy.children.length; i++) { var child = enemy.children[i]; // Check by texture URL first if (child.texture && child.texture.baseTexture && child.texture.baseTexture.resource && child.texture.baseTexture.resource.url && child.texture.baseTexture.resource.url.includes('hitbox')) { hitbox = child; break; } // Check by alpha value (hitbox has alpha 0.2 when visible, 0 when invisible) if (child.alpha === 0.2 || child.alpha === 0) { // Additional check: hitbox should have width and height of 120 if (child.width === 120 && child.height === 120) { hitbox = child; break; } } } if (hitbox) { // Calculate distance to hitbox position (relative to enemy) var hitboxWorldX = enemy.x + hitbox.x; var hitboxWorldY = enemy.y + hitbox.y; var dx = hitboxWorldX - self.x; var dy = hitboxWorldY - self.y; var distanceSquared = dx * dx + dy * dy; // Use appropriate hitbox size for collision detection (hitbox is 120x120) var hitboxRadius = 60; // Standard collision radius matching visual hitbox if (distanceSquared < hitboxRadius * hitboxRadius) { // Initialize hit enemies array if it doesn't exist if (!self.hitEnemies) { self.hitEnemies = []; } // Add this enemy to the hit list self.hitEnemies.push(enemy); // Apply damage to enemy using its takeDamage method if (enemy.takeDamage) { enemy.takeDamage(self.damage); } // Add damage to tower's total damage counter if (self.towerRef && self.towerRef.parent) { self.towerRef.totalDamage += self.damage; } // Increment hit count and check if bullet should be destroyed self.hitCount++; var towerCross = self.towerRef && self.towerRef.cross ? self.towerRef.cross : 1; if (self.hitCount >= towerCross) { // Bullet has hit the required number of enemies, destroy it self.isAlive = false; if (self.parent) { self.destroy(); } return; } // Continue to check other enemies break; } } else { // Fallback to enemy center if hitbox not found var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distanceSquared = dx * dx + dy * dy; // If close enough to target, hit it (using squared distance to avoid sqrt) if (distanceSquared < 1600) { // 40 * 40 = 1600 - reasonable collision radius for visual accuracy // Initialize hit enemies array if it doesn't exist if (!self.hitEnemies) { self.hitEnemies = []; } // Add this enemy to the hit list self.hitEnemies.push(enemy); // Apply damage to enemy using its takeDamage method if (enemy.takeDamage) { enemy.takeDamage(self.damage); } // Add damage to tower's total damage counter if (self.towerRef && self.towerRef.parent) { self.towerRef.totalDamage += self.damage; } // Increment hit count and check if bullet should be destroyed self.hitCount++; var towerCross = self.towerRef && self.towerRef.cross ? self.towerRef.cross : 1; if (self.hitCount >= towerCross) { // Bullet has hit the required number of enemies, destroy it self.isAlive = false; if (self.parent) { self.destroy(); } return; } // Continue to check other enemies break; } } } } }; return self; }); // Enemy Layer 6 (White/Black, 10% faster - same as green) var EnemyCapa6 = Container.expand(function () { var self = createEnemyBase(6, 1.1, 0xFFFFFF).call(this); return self; }); // Centralized enemy speed calculation function // 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 LevelSelectionPage = Container.expand(function () { var self = Container.call(this); // Level selection background var levelBG = self.attachAsset('MenuBackground', { anchorX: 0.5, anchorY: 0.5 }); // Title var titleText = self.addChild(new Text2('SELECT LEVEL', { size: 100, fill: 0xFFFFFF })); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -900; // Back button var backButton = self.attachAsset('BackButton', { anchorX: 0.5, anchorY: 0.5 }); backButton.x = -800; backButton.y = -900; var backText = self.addChild(new Text2('BACK', { size: 60, fill: 0xFFFFFF })); backText.anchor.set(0.5, 0.5); backText.x = -800; backText.y = -900; // Create level buttons in a grid var levelButtons = []; var levelsPerRow = 4; var totalLevels = 15; var buttonSpacing = 200; var startX = -450; var startY = -500; for (var i = 0; i < totalLevels; i++) { var row = Math.floor(i / levelsPerRow); var col = i % levelsPerRow; var levelButton = self.attachAsset('LevelButton', { anchorX: 0.5, anchorY: 0.5 }); levelButton.x = startX + col * buttonSpacing; levelButton.y = startY + row * buttonSpacing; var levelText = self.addChild(new Text2((i + 1).toString(), { size: 80, fill: 0xFFFFFF })); levelText.anchor.set(0.5, 0.5); levelText.x = levelButton.x; levelText.y = levelButton.y; // Store level number for button handler levelButton.levelNumber = i + 1; levelButton.down = function (x, y, obj) { // Start game with selected level selectedLevel = this.levelNumber; switchToGameplay(); }; levelButtons.push(levelButton); } // Back button handler backButton.down = function (x, y, obj) { switchToMenu(); }; return self; }); var MenuPage = Container.expand(function () { var self = Container.call(this); // Menu background var menuBG = self.attachAsset('MenuBackground', { anchorX: 0.5, anchorY: 0.5 }); // Game title var titleText = self.addChild(new Text2('TOWER DEFENSE', { size: 120, fill: 0xFFFFFF })); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -800; // Play button var playButton = self.attachAsset('MenuButton', { anchorX: 0.5, anchorY: 0.5 }); playButton.x = 0; playButton.y = -200; var playText = self.addChild(new Text2('PLAY', { size: 80, fill: 0xFFFFFF })); playText.anchor.set(0.5, 0.5); playText.x = 0; playText.y = -200; // Level selection button var levelsButton = self.attachAsset('MenuButton', { anchorX: 0.5, anchorY: 0.5 }); levelsButton.x = 0; levelsButton.y = 100; var levelsText = self.addChild(new Text2('LEVELS', { size: 80, fill: 0xFFFFFF })); levelsText.anchor.set(0.5, 0.5); levelsText.x = 0; levelsText.y = 100; // Settings button var settingsButton = self.attachAsset('MenuButton', { anchorX: 0.5, anchorY: 0.5 }); settingsButton.x = 0; settingsButton.y = 400; var settingsText = self.addChild(new Text2('SETTINGS', { size: 80, fill: 0xFFFFFF })); settingsText.anchor.set(0.5, 0.5); settingsText.x = 0; settingsText.y = 400; // Button handlers playButton.down = function (x, y, obj) { // Start game directly switchToGameplay(); }; levelsButton.down = function (x, y, obj) { // Switch to level selection switchToLevelSelection(); }; settingsButton.down = function (x, y, obj) { // Future settings implementation console.log("Settings clicked"); }; 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 ****/ // Game state management var GAME_STATE_MENU = 'menu'; var GAME_STATE_LEVELS = 'levels'; var GAME_STATE_GAMEPLAY = 'gameplay'; var currentGameState = GAME_STATE_MENU; var selectedLevel = 1; // Page containers var menuPage = null; var levelSelectionPage = null; var gameplayContainer = null; // Page switching functions function switchToMenu() { currentGameState = GAME_STATE_MENU; hideAllPages(); if (!menuPage) { menuPage = game.addChild(new MenuPage()); menuPage.x = 1024; menuPage.y = 1366; } menuPage.visible = true; } function switchToLevelSelection() { currentGameState = GAME_STATE_LEVELS; hideAllPages(); if (!levelSelectionPage) { levelSelectionPage = game.addChild(new LevelSelectionPage()); levelSelectionPage.x = 1024; levelSelectionPage.y = 1366; } levelSelectionPage.visible = true; } function switchToGameplay() { currentGameState = GAME_STATE_GAMEPLAY; hideAllPages(); if (!gameplayContainer) { initializeGameplay(); } gameplayContainer.visible = true; } function hideAllPages() { if (menuPage) menuPage.visible = false; if (levelSelectionPage) levelSelectionPage.visible = false; if (gameplayContainer) gameplayContainer.visible = false; } function initializeGameplay() { // Create gameplay container to hold all game elements gameplayContainer = game.addChild(new Container()); // Move all existing gameplay elements to the container moveGameplayElementsToContainer(); } function moveGameplayElementsToContainer() { // Show all gameplay elements when switching to gameplay if (gameplay) gameplay.visible = true; if (ui) ui.visible = true; // Show gameplay UI elements if (moneyText) moneyText.visible = true; if (lifeText) lifeText.visible = true; if (waveText) waveText.visible = true; if (puntero) puntero.visible = true; // Show camino objects for (var i = 0; i < caminoObjects.length; i++) { if (caminoObjects[i]) caminoObjects[i].visible = true; } // Show end object if (end) end.visible = true; // Show tower buttons for (var i = 0; i < towerButtons.length; i++) { if (towerButtons[i]) { towerButtons[i].visible = true; if (towerButtons[i].buttonBG) towerButtons[i].buttonBG.visible = true; } } } // 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 self.cross = 1; // Initialize cross value for this tower self.targetingPriority = 'first'; // Default targeting priority: 'first', 'last', 'strongest' 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 = []; // Current target tracking self.currentTarget = null; // 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) { // Analyze total incoming damage from all towers targeting this enemy var totalIncomingDamage = 0; var isBeingTargeted = false; // Check all towers and their bullets targeting this enemy for (var j = 0; j < placedTowers.length; j++) { var otherTower = placedTowers[j]; if (otherTower !== self) { // Check if this tower is targeting the enemy if (otherTower.currentTarget === enemy) { isBeingTargeted = true; } // Count all bullets from this tower that are targeting this enemy for (var k = 0; k < otherTower.bullets.length; k++) { var bullet = otherTower.bullets[k]; if (bullet && bullet.target === enemy && bullet.isAlive) { // Check if bullet hasn't already hit this enemy if (!bullet.hitEnemies || bullet.hitEnemies.indexOf(enemy) === -1) { totalIncomingDamage += bullet.damage; } } } } } // Add enemy to list if: // 1. Not being targeted at all, OR // 2. Being targeted but total incoming damage won't kill it var canTarget = !isBeingTargeted || totalIncomingDamage < enemy.currentHealth; if (canTarget) { enemiesInRange.push(enemy); } } } } // Sort enemies based on targeting priority enemiesInRange.sort(function (a, b) { if (self.targetingPriority === 'first') { // Calculate progress for enemy a var progressA = a.currentWaypointIndex; if (a.currentWaypointIndex < caminoPositions.length - 1) { var currentWaypointA = caminoPositions[a.currentWaypointIndex]; var nextWaypointA = caminoPositions[a.currentWaypointIndex + 1]; var totalDistanceA = Math.sqrt((nextWaypointA.x - currentWaypointA.x) * (nextWaypointA.x - currentWaypointA.x) + (nextWaypointA.y - currentWaypointA.y) * (nextWaypointA.y - currentWaypointA.y)); var coveredDistanceA = Math.sqrt((a.x - currentWaypointA.x) * (a.x - currentWaypointA.x) + (a.y - currentWaypointA.y) * (a.y - currentWaypointA.y)); progressA += totalDistanceA > 0 ? coveredDistanceA / totalDistanceA : 0; } // Calculate progress for enemy b var progressB = b.currentWaypointIndex; if (b.currentWaypointIndex < caminoPositions.length - 1) { var currentWaypointB = caminoPositions[b.currentWaypointIndex]; var nextWaypointB = caminoPositions[b.currentWaypointIndex + 1]; var totalDistanceB = Math.sqrt((nextWaypointB.x - currentWaypointB.x) * (nextWaypointB.x - currentWaypointB.x) + (nextWaypointB.y - currentWaypointB.y) * (nextWaypointB.y - currentWaypointB.y)); var coveredDistanceB = Math.sqrt((b.x - currentWaypointB.x) * (b.x - currentWaypointB.x) + (b.y - currentWaypointB.y) * (b.y - currentWaypointB.y)); progressB += totalDistanceB > 0 ? coveredDistanceB / totalDistanceB : 0; } // Sort in descending order (highest progress first = closest to end) return progressB - progressA; } else if (self.targetingPriority === 'last') { // Calculate progress for enemy a var progressA = a.currentWaypointIndex; if (a.currentWaypointIndex < caminoPositions.length - 1) { var currentWaypointA = caminoPositions[a.currentWaypointIndex]; var nextWaypointA = caminoPositions[a.currentWaypointIndex + 1]; var totalDistanceA = Math.sqrt((nextWaypointA.x - currentWaypointA.x) * (nextWaypointA.x - currentWaypointA.x) + (nextWaypointA.y - currentWaypointA.y) * (nextWaypointA.y - currentWaypointA.y)); var coveredDistanceA = Math.sqrt((a.x - currentWaypointA.x) * (a.x - currentWaypointA.x) + (a.y - currentWaypointA.y) * (a.y - currentWaypointA.y)); progressA += totalDistanceA > 0 ? coveredDistanceA / totalDistanceA : 0; } // Calculate progress for enemy b var progressB = b.currentWaypointIndex; if (b.currentWaypointIndex < caminoPositions.length - 1) { var currentWaypointB = caminoPositions[b.currentWaypointIndex]; var nextWaypointB = caminoPositions[b.currentWaypointIndex + 1]; var totalDistanceB = Math.sqrt((nextWaypointB.x - currentWaypointB.x) * (nextWaypointB.x - currentWaypointB.x) + (nextWaypointB.y - currentWaypointB.y) * (nextWaypointB.y - currentWaypointB.y)); var coveredDistanceB = Math.sqrt((b.x - currentWaypointB.x) * (b.x - currentWaypointB.x) + (b.y - currentWaypointB.y) * (b.y - currentWaypointB.y)); progressB += totalDistanceB > 0 ? coveredDistanceB / totalDistanceB : 0; } // Sort in ascending order (lowest progress first = farthest from end) return progressA - progressB; } else if (self.targetingPriority === 'strongest') { // Sort by health (highest health first = strongest) return b.currentHealth - a.currentHealth; } return 0; }); return enemiesInRange; }; // Method to shoot at target self.shoot = function (target) { var currentTime = Date.now(); var effectiveCadence = self.cadence; // Apply fastGame speed boost to firing rate (50% faster = reduce cadence by 33%) if (fastGame) { effectiveCadence = self.cadence / 1.5; } if (currentTime - self.lastShotTime >= effectiveCadence) { // Function to create and fire a bullet var createBullet = function createBullet(angleOffset) { 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 enemy position based on distance var bulletSpeed = 800 * (self.bulletSpeedMultiplier || 1); // Apply speed multiplier if available // Apply fastGame speed boost to bullet speed (50% faster) if (fastGame) { bulletSpeed = bulletSpeed * 1.5; } var timeToReach = distance / bulletSpeed; // time in seconds for bullet to reach current target position // Calculate enemy movement prediction - farther enemies get more prediction (reduced) var baseEnemySpeed = 260; // Base enemy speed in pixels per second var enemySpeed = getEnemySpeed(target); // Adjust prediction factor based on fastGame mode for better accuracy var predictionFactor = fastGame ? 0.8 : 0.6; // More aggressive prediction in fast mode var predictionDistance = enemySpeed * timeToReach * predictionFactor; // Get enemy's current movement direction var enemyDx = 0; var enemyDy = 0; if (target.currentWaypointIndex < caminoPositions.length - 1) { var nextWaypoint = caminoPositions[target.currentWaypointIndex + 1]; enemyDx = nextWaypoint.x - target.x; enemyDy = nextWaypoint.y - target.y; var enemyMoveDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy); if (enemyMoveDistance > 0) { // Normalize enemy direction enemyDx = enemyDx / enemyMoveDistance; enemyDy = enemyDy / enemyMoveDistance; } } // Calculate predicted target position var predictedX = target.x + enemyDx * predictionDistance; var predictedY = target.y + enemyDy * predictionDistance; // Calculate direction to predicted position with angle offset var predictedDx = predictedX - self.x; var predictedDy = predictedY - self.y; var baseAngle = Math.atan2(predictedDy, predictedDx); var adjustedAngle = baseAngle + angleOffset; var offsetDistance = 50; // Distance to spawn bullet ahead of tower // Spawn bullet slightly ahead in the adjusted direction bullet.x = self.x + Math.cos(adjustedAngle) * offsetDistance; bullet.y = self.y + Math.sin(adjustedAngle) * offsetDistance; // Set bullet direction for straight line movement in adjusted direction bullet.directionX = Math.cos(adjustedAngle); bullet.directionY = Math.sin(adjustedAngle); bullet.target = target; bullet.damage = self.damage; bullet.speed = bulletSpeed; // Use calculated speed with multiplier bullet.towerRef = self; // Store reference to the tower that fired this bullet // Rotate bullet to face adjusted direction bullet.rotation = adjustedAngle; self.bullets.push(bullet); game.addChild(bullet); }; createBullet(0); // Fire side bullets if multishot upgrade is active if (self.hasMultishot) { createBullet(-Math.PI / 12); // 15 degrees to the left createBullet(Math.PI / 12); // 15 degrees to the right } self.lastShotTime = currentTime; // Play bullet shooting sound LK.getSound('Balababa').play(); // Initialize attack sprites if not already created // Use ice-specific attack sprites if this is an ice tower var attackAsset1 = params.asset === 'Icetower' ? 'IceTowerAttack1' : 'TorreinicialAttack'; var attackAsset2 = params.asset === 'Icetower' ? 'IceTowerAttack2' : 'TorreinicialAttack2'; var attackAsset3 = params.asset === 'Icetower' ? 'IceTowerAttack3' : 'TorreinicialAttack3'; if (!self.attackSprite1) { self.attackSprite1 = self.attachAsset(attackAsset1, { anchorX: 0.5, anchorY: 0.5 }); self.attackSprite1.visible = false; } if (!self.attackSprite2) { self.attackSprite2 = self.attachAsset(attackAsset2, { anchorX: 0.5, anchorY: 0.5 }); self.attackSprite2.visible = false; } if (!self.attackSprite3) { self.attackSprite3 = self.attachAsset(attackAsset3, { anchorX: 0.5, anchorY: 0.5 }); self.attackSprite3.visible = false; } if (!self.normalSprite) { self.normalSprite = towerGraphics; } // Initialize animation frame counter if not exists if (!self.animationFrame) { self.animationFrame = 0; } // Cycle through attack sprites self.animationFrame = (self.animationFrame + 1) % 3; var currentAttackSprite; if (self.animationFrame === 0) { currentAttackSprite = self.attackSprite1; } else if (self.animationFrame === 1) { currentAttackSprite = self.attackSprite2; } else { currentAttackSprite = self.attackSprite3; } // Sync attack sprite properties with current sprite currentAttackSprite.rotation = towerGraphics.rotation; currentAttackSprite.scaleY = towerGraphics.scaleY; currentAttackSprite.x = towerGraphics.x; currentAttackSprite.y = towerGraphics.y; // Hide current sprite and show attack sprite towerGraphics.visible = false; currentAttackSprite.visible = true; towerGraphics = currentAttackSprite; // Animate attack sprite with enhanced effects for ice tower if (params.asset === 'Icetower') { // Ice tower specific animation: pulse without tint tween(currentAttackSprite, { scaleX: 1.3, scaleY: currentAttackSprite.scaleY > 0 ? 1.3 : -1.3 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(currentAttackSprite, { scaleX: 1.0, scaleY: currentAttackSprite.scaleY > 0 ? 1.0 : -1.0 }, { duration: 120, easing: tween.elasticOut }); } }); } else { // Standard animation for other towers tween(currentAttackSprite, { scaleX: 1.2, scaleY: currentAttackSprite.scaleY > 0 ? 1.2 : -1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(currentAttackSprite, { scaleX: 1.0, scaleY: currentAttackSprite.scaleY > 0 ? 1.0 : -1.0 }, { duration: 150, easing: tween.easeIn }); } }); } // Change back to normal sprite after a short delay using LK.setTimeout LK.setTimeout(function () { if (self.parent && currentAttackSprite && self.normalSprite) { // Sync normal sprite properties with current attack sprite self.normalSprite.rotation = currentAttackSprite.rotation; self.normalSprite.scaleY = currentAttackSprite.scaleY; self.normalSprite.x = currentAttackSprite.x; self.normalSprite.y = currentAttackSprite.y; // Hide attack sprite and show normal sprite currentAttackSprite.visible = false; self.normalSprite.visible = true; towerGraphics = self.normalSprite; } }, 250); } }; // 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) { // Clear current target if it's no longer valid if (self.currentTarget && (!self.currentTarget.parent || self.currentTarget.currentHealth <= 0)) { self.currentTarget = null; } // Find and shoot at enemies var enemies = self.findEnemiesInRange(); if (enemies.length > 0) { var target = enemies[0]; // Set current target self.currentTarget = target; // Calculate predicted target position for tower rotation var distance = Math.sqrt((target.x - self.x) * (target.x - self.x) + (target.y - self.y) * (target.y - self.y)); var bulletSpeed = 800; // pixels per second // Apply fastGame speed boost to bullet speed for prediction calculations if (fastGame) { bulletSpeed = bulletSpeed * 1.5; } var timeToReach = distance / bulletSpeed; var enemySpeed = getEnemySpeed(target); // Adjust prediction factor based on fastGame mode for better accuracy var predictionFactor = fastGame ? 0.8 : 0.6; // More aggressive prediction in fast mode var predictionDistance = enemySpeed * timeToReach * predictionFactor; // Get enemy's current movement direction var enemyDx = 0; var enemyDy = 0; if (target.currentWaypointIndex < caminoPositions.length - 1) { var nextWaypoint = caminoPositions[target.currentWaypointIndex + 1]; enemyDx = nextWaypoint.x - target.x; enemyDy = nextWaypoint.y - target.y; var enemyMoveDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy); if (enemyMoveDistance > 0) { // Normalize enemy direction enemyDx = enemyDx / enemyMoveDistance; enemyDy = enemyDy / enemyMoveDistance; } } // Calculate predicted target position var predictedX = target.x + enemyDx * predictionDistance; var predictedY = target.y + enemyDy * predictionDistance; // Calculate angle to predicted target position var dx = predictedX - self.x; var dy = predictedY - self.y; var targetAngle = Math.atan2(dy, dx) + Math.PI / 2; // Add 90° rotation offset // Smooth rotation using tween animation var angleDiff = targetAngle - towerGraphics.rotation; // Normalize angle difference to shortest path while (angleDiff > Math.PI) { angleDiff -= 2 * Math.PI; } while (angleDiff < -Math.PI) { angleDiff += 2 * Math.PI; } // Only rotate if angle difference is significant (> 0.1 radians) if (Math.abs(angleDiff) > 0.1) { // Stop any existing rotation tween tween.stop(towerGraphics, { rotation: true }); // Animate rotation smoothly tween(towerGraphics, { rotation: targetAngle }, { duration: 200, easing: tween.easeOut }); } // Keep tower orientation consistent - no flipping based on target position towerGraphics.scaleY = -1; // Always maintain normal orientation // Shoot at the first enemy found self.shoot(target); } else { // No enemies in range, clear current target self.currentTarget = null; } } }; // 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' }); // Initialize tower-specific properties Tower.prototype.cross = 1; // Each tower starts with cross value of 1 var IceTower = createTowerBase({ damage: 1, // Damage dealt per shot cadence: 1000, // Time between shots in milliseconds (1 second) range: 400, // Range in pixels asset: 'Icetower' }); // Initialize ice tower-specific properties IceTower.prototype.cross = 1; // Each tower starts with cross value of 1 var FireTower = createTowerBase({ damage: 1, // Damage dealt per shot cadence: 1000, // Time between shots in milliseconds (1 second) range: 400, // Range in pixels asset: 'FireTower' }); // Initialize fire tower-specific properties FireTower.prototype.cross = 1; // Each tower starts with cross value of 1 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.visible = false; // Hide initially // Store reference to button background for easy access button.buttonBG = buttonBG; buttonBG.visible = false; // Hide initially 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.showRange(); // Show range area when dragging // Initialize last valid position to starting position draggedTower.lastValidX = button.x; draggedTower.lastValidY = button.y; }; 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 function to check if point is inside rotated rectangle function isPointInRotatedRect(pointX, pointY, rectX, rectY, rectWidth, rectHeight, rotation) { // Translate point to rectangle's coordinate system var dx = pointX - rectX; var dy = pointY - rectY; // Rotate point by negative rotation to align with rectangle var cos = Math.cos(-rotation); var sin = Math.sin(-rotation); var rotatedX = dx * cos - dy * sin; var rotatedY = dx * sin + dy * cos; // Check if rotated point is within rectangle bounds with much larger bounds for better collision var halfWidth = rectWidth / 2 + 90; // Add 80 pixels buffer for much better collision detection var halfHeight = rectHeight / 2 + 60; // Add 80 pixels buffer for much better collision detection return Math.abs(rotatedX) <= halfWidth && Math.abs(rotatedY) <= halfHeight; } // 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; gameplay.visible = false; // Hide initially // Game objects layer (towers will be added here) // UI layer var ui = game.addChild(new UI()); ui.x = 1024; ui.y = 2459; ui.visible = false; // Hide initially // Create tower creation buttons var towerButtons = []; var startX = 400; // Position button on the left side towerButtons.push(createTowerButton(0xffffff, startX, 2459, Tower)); // White tower (normal) towerButtons.push(createTowerButton(0x87CEEB, startX + 200, 2459, IceTower)); // Light blue ice tower towerButtons.push(createTowerButton(0xFF4500, startX + 400, 2459, FireTower)); // Orange fire tower // Create path objects with transparency var caminoObjects = []; // Add several camino objects to create a path var caminoPositions = [{ x: 959, y: -99, rotation: 0, width: 200, height: 200 }, { x: 831, y: 50, rotation: 0.9948, width: 200, height: 510.2 }, { x: 574, y: 224, rotation: 0.8029, width: 190.3, height: 201.6 }, { x: 508, y: 313, rotation: 0.4538, width: 203.6, height: 200 }, { x: 488, y: 412, rotation: 0, width: 220.8, height: 191.1 }, { x: 516, y: 506, rotation: -0.6981, width: 214.2, height: 220 }, { x: 632, y: 587, rotation: 0.3491, width: 197, height: 220.9 }, { x: 883, y: 609, rotation: -0.0313, width: 390.1, height: 210.3 }, { x: 1144, y: 619, rotation: 0.1745, width: 200, height: 225.4 }, { x: 1318, y: 675, rotation: 0.4363, width: 224.6, height: 240 }, { x: 1457, y: 772, rotation: -0.733, width: 267.3, height: 199.6 }, { x: 1508, y: 922, rotation: 0, width: 228.8, height: 351.5 }, { x: 1459, y: 1134, rotation: -0.9250, width: 232.2, height: 231.9 }, { x: 1121, y: 1309, rotation: -0.3665, width: 652.6, height: 224.8 }, { x: 754, y: 1478, rotation: -0.7156, width: 227.5, height: 225.5 }, { x: 639, y: 1643, rotation: 0.4013, width: 224.5, height: 257.7 }, { x: 601, y: 1863, rotation: 0.0698, width: 239.2, height: 274.2 }, { x: 612, y: 2086, rotation: -0.1396, width: 236.3, height: 235 }]; 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 // Scale camino to match desired dimensions using width and height from caminoPositions var caminoWidth = caminoPositions[i].width || 200; // Use width from array or default to 200 var caminoHeight = caminoPositions[i].height || 200; // Use height from array or default to 200 camino.scaleX = caminoWidth / 200; // Asset is 200x200 by default camino.scaleY = caminoHeight / 200; camino.alpha = 0.2; // Set transparency to 0.2 camino.visible = false; // Hide initially caminoObjects.push(camino); } // Create end object and place it at the final waypoint var end = game.addChild(LK.getAsset('End', { anchorX: 0.5, anchorY: 0.5 })); // Position end closer to the penultimate waypoint var finalPosition = caminoPositions[caminoPositions.length - 1]; var penultimatePosition = caminoPositions[caminoPositions.length - 2]; // Calculate direction from penultimate to final waypoint var dx = finalPosition.x - penultimatePosition.x; var dy = finalPosition.y - penultimatePosition.y; var distance = Math.sqrt(dx * dx + dy * dy); // Move end object 60% of the way from final to penultimate position var moveBackRatio = 0.6; end.x = finalPosition.x - dx * moveBackRatio; end.y = finalPosition.y - dy * moveBackRatio; var directionAngle = Math.atan2(dy, dx); end.rotation = directionAngle; end.alpha = 1.0; // Remove transparency end.visible = false; // Hide initially // Wave system variables var enemies = []; // Dynamic wave generation function function generateWaveData() { var waves = []; var baseEnemyCount = 10; var baseSpawnDelay = 800; for (var i = 0; i < 15; i++) { var distribution = getWaveDistribution(i); waves.push({ enemyCount: baseEnemyCount + i * 5, spawnDelay: Math.max(220, baseSpawnDelay - i * 40), // Dynamic enemy distribution based on wave number distribution: distribution, // Track spawned layer 6 variants layer6SpawnedA: 0, layer6SpawnedB: 0 }); } return waves; } // Simplified enemy distribution logic with layer 6 variant support function getWaveDistribution(waveIndex) { var distributions = [{ layers: [8, 0, 0, 0, 0, 2], layer6Variants: { A: 1, B: 1 } }, // Wave 1: 8x1-layer, 2x6-layer (1x6A, 1x6B) { layers: [15, 5, 0, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 2: 15x1-layer, 5x2-layer { layers: [15, 5, 5, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 3: 15x1, 5x2, 5x3-layer { layers: [15, 10, 5, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 4: 15x1, 10x2, 5x3-layer { layers: [15, 10, 10, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 5: 15x1, 10x2, 10x3-layer { layers: [15, 15, 10, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 6: 15x1, 15x2, 10x3-layer { layers: [15, 15, 15, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 7: 15x1, 15x2, 15x3-layer { layers: [15, 20, 15, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 8: 15x1, 20x2, 15x3-layer { layers: [15, 20, 20, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 9: 15x1, 20x2, 20x3-layer { layers: [15, 25, 20, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 10: 15x1, 25x2, 20x3-layer { layers: [15, 25, 20, 5, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 11: 15x1, 25x2, 20x3, 5x4-layer { layers: [15, 25, 20, 10, 0, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 12: 15x1, 25x2, 20x3, 10x4-layer { layers: [15, 20, 20, 15, 5, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 13: 15x1, 20x2, 20x3, 15x4, 5x5-layer { layers: [15, 20, 20, 15, 10, 0], layer6Variants: { A: 0, B: 0 } }, // Wave 14: 15x1, 20x2, 20x3, 15x4, 10x5-layer { layers: [15, 20, 20, 15, 15, 5], layer6Variants: { A: 3, B: 2 } } // Wave 15: 15x1, 20x2, 20x3, 15x4, 15x5-layer, 5x6-layer (3x6A, 2x6B) ]; return distributions[waveIndex] || { layers: [10, 0, 0, 0, 0, 0], layer6Variants: { A: 0, B: 0 } }; } // Optimized function to determine enemy health layers function getEnemyHealthLayers(waveIndex, enemyIndex) { var distribution = getWaveDistribution(waveIndex); var layers = distribution.layers; var cumulative = 0; for (var layer = 0; layer < layers.length; layer++) { var previousCumulative = cumulative; cumulative += layers[layer]; if (enemyIndex < cumulative) { return { layer: layer + 1, indexInLayer: enemyIndex - previousCumulative }; } } return { layer: 1, indexInLayer: 0 }; // Default to 1 layer } var waveData = generateWaveData(); var currentWaveIndex = 0; var enemiesSpawned = 0; var waveInProgress = false; var nextSpawnTime = 0; // Enemy color configuration system var enemyColorConfigurations = { layer1: { variants: [{ color: 0xff0000, properties: {} } // Red ] }, layer2: { variants: [{ color: 0x0000ff, properties: {} } // Blue ] }, layer3: { variants: [{ color: 0x00ff00, properties: {} } // Green ] }, layer4: { variants: [{ color: 0xffff00, properties: {} } // Yellow ] }, layer5: { variants: [{ color: 0xff00ff, properties: {} } // Pink ] }, layer6: { variants: [{ color: 0xFFFFFF, properties: { antiHielo: true, antiFuego: false } }, // White for Anti hielo { color: 0x000000, properties: { antiHielo: false, antiFuego: true } } // Black for anti fuego ] } }; // Function to get enemy variant configuration function getEnemyVariant(layer, variantIndex) { var config = enemyColorConfigurations['layer' + layer]; if (!config || !config.variants || config.variants.length === 0) { return { color: 0xff0000, properties: {} }; // Default red } // If no specific variant requested, choose randomly if (variantIndex === undefined || variantIndex === null) { variantIndex = Math.floor(Math.random() * config.variants.length); } // Clamp variant index to available variants variantIndex = Math.max(0, Math.min(variantIndex, config.variants.length - 1)); return config.variants[variantIndex]; } // Function to apply enemy variant properties function applyEnemyVariant(enemy, variant) { // Apply color with smooth transition tween(enemy, { tint: variant.color }, { duration: 200, easing: tween.easeOut }); // Apply special properties if (variant.properties) { for (var prop in variant.properties) { enemy[prop] = variant.properties[prop]; } } } // Base enemy class with common functionality function createEnemyBase(healthLayers, speedMultiplier, color) { return Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('Enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); // Add hitbox to enemy var hitboxGraphics = self.attachAsset('hitbox', { anchorX: 0.5, anchorY: 0.5 }); hitboxGraphics.alpha = shotHitbox ? 0.2 : 0; // Set starting position to first camino waypoint self.x = caminoPositions[0].x; self.y = caminoPositions[0].y; // Path following properties self.currentWaypointIndex = 0; self.isMoving = false; // Health system for enemy self.maxHealth = healthLayers; self.currentHealth = self.maxHealth; self.healthLayers = []; self.canBeDestroyed = false; self.speedMultiplier = speedMultiplier; // Initialize special properties self.antiHielo = false; self.antiFuego = false; // Create health layers based on healthLayers parameter for (var i = 0; i < healthLayers; i++) { var layerVariant = getEnemyVariant(i + 1, 0); // Use first variant for each layer by default self.healthLayers.push({ min: i + 1, max: i + 1, color: layerVariant.color }); } // Method to update enemy color based on health self.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 self.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(); } // Update speed multiplier and movement speed based on new current health var newSpeed = getEnemySpeed(this); this.speedMultiplier = newSpeed / 260; // Calculate multiplier from speed // Immediately update movement speed if enemy is currently moving if (this.isMoving && this.moveSpeed) { // Apply fastGame speed boost if (fastGame) { newSpeed = newSpeed * 1.5; // 50% faster when fastGame is true } this.moveSpeed = newSpeed / 60; // Convert to pixels per frame (60 FPS) } // Check if enemy is dead if (this.currentHealth <= 0) { // Play random enemy death sound (1 of 3) var deathSounds = ['EnemyDerrivado', 'EnemigoDerrivado2', 'EnemigoDerrivado3']; var randomSoundIndex = Math.floor(Math.random() * deathSounds.length); LK.getSound(deathSounds[randomSoundIndex]).play(); // Stop any running animations if enemy is in end animation if (this.canBeDestroyed) { tween.stop(this, { scaleX: true, scaleY: true, alpha: true }); } // 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 self.moveToNextWaypoint = function () { if (this.isMoving) { return; } // Don't start new movement if already moving // Check if we've reached near the end of the path (start animation much earlier) if (this.currentWaypointIndex >= caminoPositions.length - 2) { // Start shrinking and fading animation much earlier (5 waypoints before the end) this.canBeDestroyed = true; // Mark enemy as able to be destroyed during animation this.isEndAnimation = true; this.endAnimationTimer = 3000; // 3 seconds for end animation this.endAnimationStartScale = { x: this.scaleX, y: this.scaleY }; this.endAnimationStartAlpha = this.alpha; return; } this.currentWaypointIndex = (this.currentWaypointIndex + 1) % caminoPositions.length; var nextWaypoint = caminoPositions[this.currentWaypointIndex]; // Calculate movement properties for frame-by-frame movement var dx = nextWaypoint.x - this.x; var dy = nextWaypoint.y - this.y; var distance = Math.sqrt(dx * dx + dy * dy); var speed = getEnemySpeed(this); // Apply fastGame speed boost if (fastGame) { speed = speed * 1.5; // 50% faster when fastGame is true } // Store movement properties this.targetX = nextWaypoint.x; this.targetY = nextWaypoint.y; this.moveDirectionX = dx / distance; // Normalized direction this.moveDirectionY = dy / distance; this.moveSpeed = speed / 60; // Convert to pixels per frame (60 FPS) this.isMoving = true; // Calculate rotation to face the next waypoint var angle = Math.atan2(dy, dx); this.targetRotation = angle; // Calculate rotation difference for smooth rotation var angleDiff = angle - this.rotation; // Normalize angle difference to shortest path while (angleDiff > Math.PI) { angleDiff -= 2 * Math.PI; } while (angleDiff < -Math.PI) { angleDiff += 2 * Math.PI; } // Only rotate if angle difference is significant enough if (Math.abs(angleDiff) > 0.05) { // Stop any existing rotation tween tween.stop(this, { rotation: true }); // Calculate rotation duration based on enemy speed for more responsive turning var baseDuration = 400; // Base duration in milliseconds var speedFactor = this.speedMultiplier || 1; var rotationDuration = Math.max(150, baseDuration / speedFactor); // Faster enemies turn quicker // Adjust duration based on how much we need to turn var angleMagnitude = Math.abs(angleDiff); var angleFactor = angleMagnitude / Math.PI; // 0 to 1 based on how much turn is needed rotationDuration = rotationDuration * (0.3 + 0.7 * angleFactor); // Scale duration with turn amount // Use tween for smooth, velocity-adjusted rotation tween(this, { rotation: angle }, { duration: rotationDuration, easing: tween.easeOut }); } }; // Frame-by-frame update method for movement self.update = function () { // Handle end animation if (this.isEndAnimation) { var animationSpeedMultiplier = fastGame ? 1.5 : 1.0; // 50% faster animation when fastGame is true this.endAnimationTimer -= frameTime * 1000 * animationSpeedMultiplier; // Convert frame time to milliseconds and apply speed multiplier var progress = 1 - this.endAnimationTimer / 3000; // Progress from 0 to 1 // Animate scale and alpha this.scaleX = this.endAnimationStartScale.x * (1 - progress * 0.9); // Scale down to 10% this.scaleY = this.endAnimationStartScale.y * (1 - progress * 0.9); this.alpha = this.endAnimationStartAlpha * (1 - progress); // Fade to 0 if (this.endAnimationTimer <= 0) { // Animation complete if (!this.parent) { return; // Enemy was already destroyed } // 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; } // Rotation is now handled by tween system in moveToNextWaypoint method // Handle movement if (this.isMoving) { // Move towards target this.x += this.moveDirectionX * this.moveSpeed; this.y += this.moveDirectionY * this.moveSpeed; // Check if we've reached the target waypoint var dx = this.targetX - this.x; var dy = this.targetY - this.y; var distanceToTarget = Math.sqrt(dx * dx + dy * dy); if (distanceToTarget <= this.moveSpeed) { // Snap to target position this.x = this.targetX; this.y = this.targetY; this.isMoving = false; // Move to next waypoint immediately this.moveToNextWaypoint(); } } }; // Initialize enemy color self.updateHealthColor(); // Start movement after a short delay LK.setTimeout(function () { self.moveToNextWaypoint(); }, 500); return self; }); } // Enemy Layer 1 (Red, base speed) var EnemyCapa1 = createEnemyBase(1, 1.0, 0xff0000); // Enemy Layer 2 (Blue, 5% faster) var EnemyCapa2 = createEnemyBase(2, 1.05, 0x0000ff); // Enemy Layer 3 (Green, 10% faster) var EnemyCapa3 = createEnemyBase(3, 1.1, 0x00ff00); // Enemy Layer 4 (Yellow, 75% faster) var EnemyCapa4 = createEnemyBase(4, 1.75, 0xffff00); // Enemy Layer 5 (Pink, 150% faster) var EnemyCapa5 = createEnemyBase(5, 2.5, 0xff00ff); // Centralized enemy speed calculation function function getEnemySpeed(enemy) { var baseEnemySpeed = 260; // Base enemy speed in pixels per second var enemySpeedMultiplier = 1.0; if (enemy.currentHealth === 2) { enemySpeedMultiplier = 1.05; // 5% faster for 2 layers } else if (enemy.currentHealth === 3) { enemySpeedMultiplier = 1.1; // 10% faster for 3 layers } else if (enemy.currentHealth === 4) { enemySpeedMultiplier = 1.75; // 75% faster for 4 layers (yellow) } else if (enemy.currentHealth === 5) { enemySpeedMultiplier = 2.5; // 150% faster for 5 layers (pink) } else if (enemy.currentHealth === 6) { enemySpeedMultiplier = 1.1; // 10% faster for 6 layers (white) - same as green } return baseEnemySpeed * enemySpeedMultiplier; } // Function to update all enemy speeds (useful for fastGame toggle) function updateAllEnemySpeeds() { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy && enemy.parent && enemy.isMoving && enemy.moveSpeed) { var newSpeed = getEnemySpeed(enemy); // Apply fastGame speed boost if (fastGame) { newSpeed = newSpeed * 1.5; // 50% faster when fastGame is true } enemy.moveSpeed = newSpeed / 60; // Convert to pixels per frame (60 FPS) } } } // Function to create enemy with specified health layers and optional variant function createEnemy(healthLayers, variantIndex) { var enemy; switch (healthLayers) { case 1: enemy = game.addChild(new EnemyCapa1()); break; case 2: enemy = game.addChild(new EnemyCapa2()); break; case 3: enemy = game.addChild(new EnemyCapa3()); break; case 4: enemy = game.addChild(new EnemyCapa4()); break; case 5: enemy = game.addChild(new EnemyCapa5()); break; case 6: enemy = game.addChild(new EnemyCapa6()); break; default: enemy = game.addChild(new EnemyCapa1()); break; } // Apply variant configuration after enemy creation var variant = getEnemyVariant(healthLayers, variantIndex); applyEnemyVariant(enemy, variant); 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 only if autoStartWaves is true if (autoStartWaves) { 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; // Helper function to update range area visually function updateRangeArea(tower) { if (tower.children[0]) { var areaScale = tower.range * 2 / 100; tower.children[0].scaleX = areaScale; tower.children[0].scaleY = areaScale; } } // Helper function to increment upgrade counter function incrementUpgradeCounter(tower, type) { if (type === 'left') { tower.leftUpgrades = tower.leftUpgrades || 0; tower.leftUpgrades++; } else if (type === 'right') { tower.rightUpgrades = tower.rightUpgrades || 0; tower.rightUpgrades++; } } // Reusable upgrade system var upgradeDefinitions = { speed: { title: 'Rapid Fire', cost: 75, apply: function apply(tower) { tower.cadence = Math.max(100, tower.cadence * 0.8); incrementUpgradeCounter(tower, 'left'); } }, range: { title: 'Eagle Eye', cost: 100, apply: function apply(tower) { tower.range = Math.floor(tower.range * 1.25); tower.rangeSquared = tower.range * tower.range; updateRangeArea(tower); incrementUpgradeCounter(tower, 'left'); } }, multishot: { title: 'Triple Threat', cost: 150, apply: function apply(tower) { tower.hasMultishot = true; incrementUpgradeCounter(tower, 'left'); } }, damage: { title: 'Power Surge', cost: 50, apply: function apply(tower) { tower.damage += 1; } }, left_final: { title: 'Berserker Mode', cost: 200, apply: function apply(tower) { tower.damage += 1; tower.cadence = Math.max(50, tower.cadence * 0.25); incrementUpgradeCounter(tower, 'left'); } }, right1_damage: { title: 'Brutal Force', cost: 50, apply: function apply(tower) { tower.damage += 1; incrementUpgradeCounter(tower, 'right'); } }, right1_cross: { title: 'Chain Lightning', cost: 75, apply: function apply(tower) { tower.cross = (tower.cross || 1) + 2; incrementUpgradeCounter(tower, 'right'); } }, right1_range: { title: 'Sniper Scope', cost: 100, apply: function apply(tower) { tower.range = Math.floor(tower.range * 1.3); tower.rangeSquared = tower.range * tower.range; updateRangeArea(tower); incrementUpgradeCounter(tower, 'right'); } }, right1_cross_final: { title: 'Storm Walker', cost: 125, apply: function apply(tower) { tower.cross = (tower.cross || 1) + 2; tower.bulletSpeedMultiplier = 1.3; tower.bulletDurationMultiplier = 1.5; incrementUpgradeCounter(tower, 'right'); } }, right1_ultimate: { title: 'Apocalypse', cost: 300, apply: function apply(tower) { tower.bulletSpeedMultiplier = (tower.bulletSpeedMultiplier || 1) * 1.6; tower.cross = (tower.cross || 1) + 6; tower.damage += 2; incrementUpgradeCounter(tower, 'right'); } }, targeting_priority: { title: 'Hunter Instinct', cost: 0, apply: function apply(tower) { var priorities = ['first', 'last', 'strongest']; var currentIndex = priorities.indexOf(tower.targetingPriority); tower.targetingPriority = priorities[(currentIndex + 1) % priorities.length]; } }, maxed: { title: 'Legendary', cost: 999999, apply: function apply(tower) { // Do nothing - this upgrade can't be purchased } } }; // Upgrade path configurations var leftUpgradePath = ['speed', 'range', 'multishot', 'left_final']; var rightUpgradePath = ['right1_damage', 'right1_range', 'right1_cross_final', 'right1_ultimate']; // Function to get current left upgrade options for a tower function getLeftUpgradeOptions(tower) { tower.leftUpgrades = tower.leftUpgrades || 0; return leftUpgradePath[tower.leftUpgrades] || 'maxed'; } // Function to get current right upgrade options for a tower function getRightUpgradeOptions(tower) { tower.rightUpgrades = tower.rightUpgrades || 0; return { first: rightUpgradePath[tower.rightUpgrades] || 'maxed', second: null }; } // Left upgrade frame - Damage +1 var leftUpgradeFrame = towerUpgradeContainer.addChild(LK.getAsset('UpgradeBG', { anchorX: 0.5, anchorY: 0.5 })); leftUpgradeFrame.x = -700; leftUpgradeFrame.y = 180; var leftUpgradeTitle = towerUpgradeContainer.addChild(new Text2('', { size: 60, fill: 0xFFFFFF })); leftUpgradeTitle.anchor.set(0.5, 0.5); leftUpgradeTitle.x = -700; leftUpgradeTitle.y = 0; // Right upgrade frames and titles (only 2 slots) var rightUpgradeFrame1 = towerUpgradeContainer.addChild(LK.getAsset('UpgradeBG', { anchorX: 0.5, anchorY: 0.5 })); rightUpgradeFrame1.x = 100; rightUpgradeFrame1.y = 180; var rightUpgradeTitle1 = towerUpgradeContainer.addChild(new Text2('', { size: 60, fill: 0xFFFFFF })); rightUpgradeTitle1.anchor.set(0.5, 0.5); rightUpgradeTitle1.x = 100; rightUpgradeTitle1.y = 0; var rightUpgradeFrame2 = towerUpgradeContainer.addChild(LK.getAsset('UpgradeBG', { anchorX: 0.5, anchorY: 0.5 })); rightUpgradeFrame2.x = 400; rightUpgradeFrame2.y = 180; var rightUpgradeTitle2 = towerUpgradeContainer.addChild(new Text2('', { size: 60, fill: 0xFFFFFF })); rightUpgradeTitle2.anchor.set(0.5, 0.5); rightUpgradeTitle2.x = 400; rightUpgradeTitle2.y = 0; // Priority button frame and title var priorityUpgradeFrame = towerUpgradeContainer.addChild(LK.getAsset('UpgradeBG', { anchorX: 0.5, anchorY: 0.5 })); priorityUpgradeFrame.x = 700; priorityUpgradeFrame.y = 180; var priorityUpgradeTitle = towerUpgradeContainer.addChild(new Text2('', { size: 60, fill: 0xFFFFFF })); priorityUpgradeTitle.anchor.set(0.5, 0.5); priorityUpgradeTitle.x = 700; priorityUpgradeTitle.y = 0; // 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; // Show fast mode button when in tower creation mode if (speedButton && speedButton.parent) { speedButton.visible = true; } // Show auto wave button when in tower creation mode if (autoWaveButton && autoWaveButton.parent) { autoWaveButton.visible = true; } } 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; // Hide fast mode button when in tower upgrade mode if (speedButton && speedButton.parent) { speedButton.visible = false; } // Hide auto wave button when in tower upgrade mode if (autoWaveButton && autoWaveButton.parent) { autoWaveButton.visible = false; } } } // Function to create upgrade button with animation and functionality function createUpgradeButton(upgradeType, x, y, container) { var upgradeDef = upgradeDefinitions[upgradeType]; if (!upgradeDef) { return null; } var upgradeButton = container.addChild(LK.getAsset('uiBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.15, scaleY: 0.15 })); upgradeButton.x = x; upgradeButton.y = y; var upgradeText = container.addChild(new Text2('BUY', { size: 50, fill: 0x000000 })); upgradeText.anchor.set(0.5, 0.5); upgradeText.x = x; upgradeText.y = y; // Add click handler with animation upgradeButton.down = function (x, y, obj) { if (selectedTowerForUpgrade && playerMoney >= upgradeDef.cost && upgradeType !== 'maxed') { // Apply upgrade upgradeDef.apply(selectedTowerForUpgrade); // Deduct cost playerMoney -= upgradeDef.cost; // Animate button press effect tween(upgradeButton, { scaleX: 0.12, scaleY: 0.12 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(upgradeButton, { scaleX: 0.15, scaleY: 0.15 }, { duration: 150, easing: tween.bounceOut }); } }); // Flash button to show upgrade LK.effects.flashObject(upgradeButton, 0xFFFFFF, 300); // Refresh the upgrade display immediately after purchase updateTowerUpgrade(selectedTowerForUpgrade); } }; return { button: upgradeButton, text: upgradeText, updateDisplay: function updateDisplay() { if (upgradeType === 'maxed') { upgradeButton.alpha = 0.5; upgradeText.alpha = 0.5; upgradeText.setText('BUY MAX'); } else { var canAfford = selectedTowerForUpgrade && playerMoney >= upgradeDef.cost; upgradeButton.alpha = canAfford ? 1.0 : 0.5; upgradeText.alpha = canAfford ? 1.0 : 0.5; upgradeText.setText('BUY'); } } }; } // Upgrade button configurations var upgradeButtonConfigs = [{ type: 'left', x: -700, y: 380, title: leftUpgradeTitle, frame: leftUpgradeFrame }, { type: 'right1', x: 100, y: 380, title: rightUpgradeTitle1, frame: rightUpgradeFrame1 }, { type: 'right2', x: 400, y: 380, title: rightUpgradeTitle2, frame: rightUpgradeFrame2 }, { type: 'priority', x: 700, y: 380, title: priorityUpgradeTitle, frame: priorityUpgradeFrame }]; // 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); // Get current upgrade options for this tower var leftOption = getLeftUpgradeOptions(tower); var rightOptions = getRightUpgradeOptions(tower); var upgradeOptions = [leftOption, rightOptions.first, rightOptions.second, 'targeting_priority']; // Clean up existing upgrade buttons and create new ones var upgradeButtons = [leftUpgrade, right1Upgrade, right2Upgrade, priorityUpgrade]; for (var i = 0; i < upgradeButtons.length; i++) { // Clean up existing button if (upgradeButtons[i] && upgradeButtons[i].button && upgradeButtons[i].button.parent) { upgradeButtons[i].button.destroy(); upgradeButtons[i].text.destroy(); } // Create new button var config = upgradeButtonConfigs[i]; var option = upgradeOptions[i]; upgradeButtons[i] = option ? createUpgradeButton(option, config.x, config.y, towerUpgradeContainer) : null; // Update frame visibility if (option && upgradeDefinitions[option]) { var title = option === 'targeting_priority' ? 'Priority: ' + (tower.targetingPriority === 'first' ? 'First' : tower.targetingPriority === 'last' ? 'Last' : 'Strongest') : upgradeDefinitions[option].title; config.title.setText(title); config.frame.visible = true; config.title.visible = true; } else { config.title.setText(''); config.frame.visible = false; config.title.visible = false; } // Update display if (upgradeButtons[i]) { upgradeButtons[i].updateDisplay(); } } // Update references leftUpgrade = upgradeButtons[0]; right1Upgrade = upgradeButtons[1]; right2Upgrade = upgradeButtons[2]; priorityUpgrade = upgradeButtons[3]; } else { // Clear reference to selected tower selectedTowerForUpgrade = null; // Hide priority upgrade frame when no tower selected priorityUpgradeFrame.visible = false; priorityUpgradeTitle.visible = false; // Switch back to tower creation mode when no tower is selected setUIMode(UI_MODE_TOWER_CREATION); } } // Create upgrade buttons using the reusable system var leftUpgrade = null; // Will be created dynamically var right1Upgrade = null; // Will be created dynamically var right2Upgrade = null; // Will be created dynamically var priorityUpgrade = null; // Will be created dynamically // Initialize with no tower selected and tower creation mode updateTowerUpgrade(null); setUIMode(UI_MODE_TOWER_CREATION); // Add money, life and wave UI texts in the top-right corner var moneyText = new Text2('Money: 100', { size: 80, fill: 0xFFFF00 }); moneyText.anchor.set(1, 0); moneyText.x = 1900; moneyText.y = 150; moneyText.visible = false; // Hide initially game.addChild(moneyText); var lifeText = new Text2('Life: 20', { size: 80, fill: 0xFF0000 }); lifeText.anchor.set(1, 0); lifeText.x = 1900; lifeText.y = 250; lifeText.visible = false; // Hide initially game.addChild(lifeText); var waveText = new Text2('Wave: 1', { size: 80, fill: 0x00FF00 }); waveText.anchor.set(1, 0); waveText.x = 1900; waveText.y = 350; waveText.visible = false; // Hide initially game.addChild(waveText); // Add fast mode button to UI towers section var speedButton = ui.addChild(LK.getAsset('x1Speed', { anchorX: 0.5, anchorY: 0.5 })); speedButton.x = 800; speedButton.y = -130; // Add auto wave button below speed button var autoWaveButton = ui.addChild(LK.getAsset('AutoWaveStop', { anchorX: 0.5, anchorY: 0.5 })); autoWaveButton.x = 800; autoWaveButton.y = 70; // Add touch handler to speed button speedButton.down = function (x, y, obj) { // Toggle fastGame variable fastGame = !fastGame; // Update button asset to reflect current speed if (fastGame) { // Remove current button and create new one with FastMode asset speedButton.destroy(); speedButton = ui.addChild(LK.getAsset('FastMode', { anchorX: 0.5, anchorY: 0.5 })); speedButton.x = 800; speedButton.y = -130; // Re-attach the down handler to the new button speedButton.down = arguments.callee; } else { // Remove current button and create new one with x1Speed asset speedButton.destroy(); speedButton = ui.addChild(LK.getAsset('x1Speed', { anchorX: 0.5, anchorY: 0.5 })); speedButton.x = 800; speedButton.y = -130; // Re-attach the down handler to the new button speedButton.down = arguments.callee; } // Immediately update all existing enemies' speeds updateAllEnemySpeeds(); // Immediately update next spawn time to prevent delayed spawning if (waveInProgress && nextSpawnTime > Date.now()) { var currentWaveData = waveData[currentWaveIndex]; var baseSpawnDelay = currentWaveData.spawnDelay; var adjustedSpawnDelay = fastGame ? baseSpawnDelay / 1.5 : baseSpawnDelay; var timeRemaining = nextSpawnTime - Date.now(); var newTimeRemaining = fastGame ? timeRemaining / 1.5 : timeRemaining * 1.5; nextSpawnTime = Date.now() + newTimeRemaining; } }; // Add touch handler to auto wave button autoWaveButton.down = function (x, y, obj) { // Toggle autoStartWaves variable autoStartWaves = !autoStartWaves; // Update button asset to reflect current auto wave state if (autoStartWaves) { // Add rotation animation when auto wave is true var _startAutoWaveAnimation = function startAutoWaveAnimation() { tween(autoWaveButton, { rotation: autoWaveButton.rotation + Math.PI * 4 // Rotate 2 full turns (4 * PI) }, { duration: 5000, // Animation duration in milliseconds easing: tween.easeInOut, // Start slow, speed up, slow down onFinish: function onFinish() { // Animation complete, check if autoStartWaves is still true and repeat the animation if (autoStartWaves && autoWaveButton && autoWaveButton.parent) { _startAutoWaveAnimation(); // Start the animation again } } }); }; // Remove current button and create new one with Wavestart asset autoWaveButton.destroy(); autoWaveButton = ui.addChild(LK.getAsset('Wavestart', { anchorX: 0.5, anchorY: 0.5 })); autoWaveButton.x = 800; autoWaveButton.y = 70; // Re-attach the down handler to the new button autoWaveButton.down = arguments.callee; // Start the first wave if no wave is in progress if (!waveInProgress && currentWaveIndex < waveData.length) { startWave(currentWaveIndex); } _startAutoWaveAnimation(); // Initial call to start the loop } else { // Remove current button and create new one with AutoWaveStop asset autoWaveButton.destroy(); autoWaveButton = ui.addChild(LK.getAsset('AutoWaveStop', { anchorX: 0.5, anchorY: 0.5 })); autoWaveButton.x = 800; autoWaveButton.y = 70; // Re-attach the down handler to the new button autoWaveButton.down = arguments.callee; // Stop the rotation animation when auto wave is false tween.stop(autoWaveButton, { rotation: true }); } }; // Initialize game variables var gameStart = false; // Boolean to control game start state var playerMoney = 99999; 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 var fastGame = false; // Boolean to control 2x speed mode var autoStartWaves = false; // Boolean to control automatic wave starting var antiHielo = false; // Boolean to control anti ice effect var antiFuego = false; // Boolean to control anti fire effect // Initialize menu page on game start LK.setTimeout(function () { switchToMenu(); }, 100); // Camino path dimensions - can be changed while maintaining 200x200 default var caminoWidth = 200; var caminoHeight = 200; // 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; puntero.visible = false; // Hide initially // 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 < gameplayBounds.left + 70) { targetX = gameplayBounds.left + 70; } if (targetX > gameplayBounds.right - 70) { targetX = gameplayBounds.right - 70; } // Check if tower is too close to existing towers var minDistance = 140; // Standardized 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]; // Use dynamic dimensions with scaling var caminoEffectiveWidth = caminoWidth * camino.scaleX; var caminoEffectiveHeight = caminoHeight * camino.scaleY; // Check collision with rotated rectangle if (isPointInRotatedRect(targetX, targetY, camino.x, camino.y, caminoEffectiveWidth, caminoEffectiveHeight, camino.rotation)) { 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; var finalX, finalY; if (cannotPlace) { // Apply red tint to area when cannot place tower if (draggedTower.children[0]) { draggedTower.children[0].tint = 0xff0000; } // If position is invalid, try to find the closest valid position // First check if we have a last valid position if (draggedTower.lastValidX !== undefined && draggedTower.lastValidY !== undefined) { // Calculate distance to see if we should try to interpolate or just use last valid var distanceToLastValid = Math.sqrt((targetX - draggedTower.lastValidX) * (targetX - draggedTower.lastValidX) + (targetY - draggedTower.lastValidY) * (targetY - draggedTower.lastValidY)); // If we're reasonably close to last valid position, try to find a valid position between current and last valid if (distanceToLastValid < 250) { var validPositionFound = false; var steps = 20; // More steps for better precision for (var step = 1; step <= steps; step++) { var ratio = step / steps; var testX = draggedTower.lastValidX + (targetX - draggedTower.lastValidX) * ratio; var testY = draggedTower.lastValidY + (targetY - draggedTower.lastValidY) * ratio; // Test this interpolated position var testTooClose = false; var testMinDistanceSquared = 140 * 140; // Use same standardized distance for (var i = 0; i < placedTowers.length; i++) { var existingTower = placedTowers[i]; var dx = testX - existingTower.x; var dy = testY - existingTower.y; var distanceSquared = dx * dx + dy * dy; if (distanceSquared < testMinDistanceSquared) { testTooClose = true; break; } } var testOnCamino = false; if (!testTooClose) { for (var i = 0; i < caminoObjects.length; i++) { var camino = caminoObjects[i]; // Use dynamic dimensions with scaling var caminoEffectiveWidth = caminoWidth * camino.scaleX; var caminoEffectiveHeight = caminoHeight * camino.scaleY; // Check collision with rotated rectangle if (isPointInRotatedRect(testX, testY, camino.x, camino.y, caminoEffectiveWidth, caminoEffectiveHeight, camino.rotation)) { testOnCamino = true; break; } } } var testPointerInUI = isPointInUI(testX, testY); if (!testTooClose && !testOnCamino && !testPointerInUI) { // Found a valid position finalX = testX; finalY = testY; validPositionFound = true; // Update last valid position to this new position draggedTower.lastValidX = testX; draggedTower.lastValidY = testY; // Remove red tint since we found a valid position if (draggedTower.children[0]) { draggedTower.children[0].tint = 0xFFFFFF; } break; } } if (!validPositionFound) { // Use last valid position finalX = draggedTower.lastValidX; finalY = draggedTower.lastValidY; } } else { // Too far from last valid position, just use it finalX = draggedTower.lastValidX; finalY = draggedTower.lastValidY; } } else { // No last valid position, use target position (shouldn't happen with proper initialization) finalX = targetX; finalY = targetY; } } else { // Remove tint from area when tower can be placed if (draggedTower.children[0]) { draggedTower.children[0].tint = 0xFFFFFF; } // Store this as the last valid position draggedTower.lastValidX = targetX; draggedTower.lastValidY = targetY; finalX = targetX; finalY = targetY; } // Apply different movement behavior based on validity if (cannotPlace) { // In invalid zones, use immediate positioning for quick stops draggedTower.x = finalX; draggedTower.y = finalY; } else { // In valid zones, use instant positioning for immediate tower appearance draggedTower.x = finalX; draggedTower.y = finalY; } } }; // Cross variable to control bullet destruction after hitting enemies - now tower-specific // var Cross = 1; // Removed global Cross, now each tower has its own cross value // Variable to control enemy hitbox visibility var shotHitbox = true; // When true, show enemy hitboxes; when false, hide them // Game update handler game.update = function () { // Update UI texts with current values moneyText.setText('Money: ' + playerMoney); lifeText.setText('Life: ' + playerLife); waveText.setText('Wave: ' + (currentWaveIndex + 1)); // Update path visibility based on PathShow variable for (var i = 0; i < caminoObjects.length; i++) { caminoObjects[i].alpha = PathShow ? 0.2 : 0; } // Update enemy hitbox visibility based on shotHitbox variable for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy && enemy.parent) { // Find the hitbox child in the enemy for (var j = 0; j < enemy.children.length; j++) { var child = enemy.children[j]; // Check if this is a hitbox (either by texture URL or by alpha value) var isHitbox = false; if (child.texture && child.texture.baseTexture && child.texture.baseTexture.resource && child.texture.baseTexture.resource.url && child.texture.baseTexture.resource.url.includes('hitbox')) { isHitbox = true; } else if (child.alpha === 0.2 || child.alpha === 0) { // This might be a hitbox based on alpha value isHitbox = true; } if (isHitbox) { child.alpha = shotHitbox ? 0.2 : 0; break; } } } } // 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 using optimized function var enemyData = getEnemyHealthLayers(currentWaveIndex, enemiesSpawned); var healthLayers = enemyData.layer; // Determine variant for layer 6 enemies based on wave configuration var variantIndex = undefined; if (healthLayers === 6) { var distribution = currentWaveData.distribution; // Check if we should spawn 6A or 6B based on configured amounts if (currentWaveData.layer6SpawnedA < distribution.layer6Variants.A) { variantIndex = 0; // Spawn 6A (Anti hielo - white) currentWaveData.layer6SpawnedA++; } else if (currentWaveData.layer6SpawnedB < distribution.layer6Variants.B) { variantIndex = 1; // Spawn 6B (Anti fuego - black) currentWaveData.layer6SpawnedB++; } } // Spawn enemy with variant createEnemy(healthLayers, variantIndex); enemiesSpawned++; var spawnDelay = currentWaveData.spawnDelay; // Apply fastGame spawn rate boost (50% faster = divide by 1.5) if (fastGame) { spawnDelay = spawnDelay / 1.5; } nextSpawnTime = currentTime + spawnDelay; } // Check if wave is complete if (enemiesSpawned >= currentWaveData.enemyCount && enemies.length === 0) { waveInProgress = false; // Start next wave after a delay only if autoStartWaves is true if (autoStartWaves) { var waveDelay = 500; // 0.5 second delay between waves (much faster) // Apply fastGame wave interval boost (50% faster = divide by 1.5) if (fastGame) { waveDelay = waveDelay / 1.5; } LK.setTimeout(function () { startWave(currentWaveIndex + 1); }, waveDelay); } } } }; // 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; // Check if tower is too close to existing towers at current position var minDistance = 140; // Standardized minimum distance between towers var minDistanceSquared = minDistance * minDistance; var tooCloseToTowers = false; 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) { tooCloseToTowers = true; break; } } // Check if tower would be placed on camino path at current position var onCamino = false; for (var i = 0; i < caminoObjects.length; i++) { var camino = caminoObjects[i]; // Use dynamic dimensions with scaling var caminoEffectiveWidth = caminoWidth * camino.scaleX; var caminoEffectiveHeight = caminoHeight * camino.scaleY; // Check collision with rotated rectangle if (isPointInRotatedRect(draggedTower.x, draggedTower.y, camino.x, camino.y, caminoEffectiveWidth, caminoEffectiveHeight, camino.rotation)) { onCamino = true; break; } } var canPlaceAtCurrentPosition = !pointerInUI && !towerInUI && !tooCloseToTowers && !onCamino && draggedTower.x >= gameplayBounds.left && draggedTower.x <= gameplayBounds.right && draggedTower.y >= gameplayBounds.top && draggedTower.y <= gameplayBounds.bottom; if (canPlaceAtCurrentPosition) { // Can place at current position draggedTower.alpha = 1.0; 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 { // Cannot place at current position - destroy tower if it's in UI or invalid position draggedTower.destroy(); } isDragging = false; draggedTower = null; } };
===================================================================
--- original.js
+++ change.js
@@ -175,8 +175,147 @@
anchorY: 0.5
});
return self;
});
+var LevelSelectionPage = Container.expand(function () {
+ var self = Container.call(this);
+ // Level selection background
+ var levelBG = self.attachAsset('MenuBackground', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Title
+ var titleText = self.addChild(new Text2('SELECT LEVEL', {
+ size: 100,
+ fill: 0xFFFFFF
+ }));
+ titleText.anchor.set(0.5, 0.5);
+ titleText.x = 0;
+ titleText.y = -900;
+ // Back button
+ var backButton = self.attachAsset('BackButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ backButton.x = -800;
+ backButton.y = -900;
+ var backText = self.addChild(new Text2('BACK', {
+ size: 60,
+ fill: 0xFFFFFF
+ }));
+ backText.anchor.set(0.5, 0.5);
+ backText.x = -800;
+ backText.y = -900;
+ // Create level buttons in a grid
+ var levelButtons = [];
+ var levelsPerRow = 4;
+ var totalLevels = 15;
+ var buttonSpacing = 200;
+ var startX = -450;
+ var startY = -500;
+ for (var i = 0; i < totalLevels; i++) {
+ var row = Math.floor(i / levelsPerRow);
+ var col = i % levelsPerRow;
+ var levelButton = self.attachAsset('LevelButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ levelButton.x = startX + col * buttonSpacing;
+ levelButton.y = startY + row * buttonSpacing;
+ var levelText = self.addChild(new Text2((i + 1).toString(), {
+ size: 80,
+ fill: 0xFFFFFF
+ }));
+ levelText.anchor.set(0.5, 0.5);
+ levelText.x = levelButton.x;
+ levelText.y = levelButton.y;
+ // Store level number for button handler
+ levelButton.levelNumber = i + 1;
+ levelButton.down = function (x, y, obj) {
+ // Start game with selected level
+ selectedLevel = this.levelNumber;
+ switchToGameplay();
+ };
+ levelButtons.push(levelButton);
+ }
+ // Back button handler
+ backButton.down = function (x, y, obj) {
+ switchToMenu();
+ };
+ return self;
+});
+var MenuPage = Container.expand(function () {
+ var self = Container.call(this);
+ // Menu background
+ var menuBG = self.attachAsset('MenuBackground', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Game title
+ var titleText = self.addChild(new Text2('TOWER DEFENSE', {
+ size: 120,
+ fill: 0xFFFFFF
+ }));
+ titleText.anchor.set(0.5, 0.5);
+ titleText.x = 0;
+ titleText.y = -800;
+ // Play button
+ var playButton = self.attachAsset('MenuButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ playButton.x = 0;
+ playButton.y = -200;
+ var playText = self.addChild(new Text2('PLAY', {
+ size: 80,
+ fill: 0xFFFFFF
+ }));
+ playText.anchor.set(0.5, 0.5);
+ playText.x = 0;
+ playText.y = -200;
+ // Level selection button
+ var levelsButton = self.attachAsset('MenuButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ levelsButton.x = 0;
+ levelsButton.y = 100;
+ var levelsText = self.addChild(new Text2('LEVELS', {
+ size: 80,
+ fill: 0xFFFFFF
+ }));
+ levelsText.anchor.set(0.5, 0.5);
+ levelsText.x = 0;
+ levelsText.y = 100;
+ // Settings button
+ var settingsButton = self.attachAsset('MenuButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ settingsButton.x = 0;
+ settingsButton.y = 400;
+ var settingsText = self.addChild(new Text2('SETTINGS', {
+ size: 80,
+ fill: 0xFFFFFF
+ }));
+ settingsText.anchor.set(0.5, 0.5);
+ settingsText.x = 0;
+ settingsText.y = 400;
+ // Button handlers
+ playButton.down = function (x, y, obj) {
+ // Start game directly
+ switchToGameplay();
+ };
+ levelsButton.down = function (x, y, obj) {
+ // Switch to level selection
+ switchToLevelSelection();
+ };
+ settingsButton.down = function (x, y, obj) {
+ // Future settings implementation
+ console.log("Settings clicked");
+ };
+ return self;
+});
var UI = Container.expand(function () {
var self = Container.call(this);
var uiGraphics = self.attachAsset('uiBackground', {
anchorX: 0.5,
@@ -194,8 +333,81 @@
/****
* Game Code
****/
+// Game state management
+var GAME_STATE_MENU = 'menu';
+var GAME_STATE_LEVELS = 'levels';
+var GAME_STATE_GAMEPLAY = 'gameplay';
+var currentGameState = GAME_STATE_MENU;
+var selectedLevel = 1;
+// Page containers
+var menuPage = null;
+var levelSelectionPage = null;
+var gameplayContainer = null;
+// Page switching functions
+function switchToMenu() {
+ currentGameState = GAME_STATE_MENU;
+ hideAllPages();
+ if (!menuPage) {
+ menuPage = game.addChild(new MenuPage());
+ menuPage.x = 1024;
+ menuPage.y = 1366;
+ }
+ menuPage.visible = true;
+}
+function switchToLevelSelection() {
+ currentGameState = GAME_STATE_LEVELS;
+ hideAllPages();
+ if (!levelSelectionPage) {
+ levelSelectionPage = game.addChild(new LevelSelectionPage());
+ levelSelectionPage.x = 1024;
+ levelSelectionPage.y = 1366;
+ }
+ levelSelectionPage.visible = true;
+}
+function switchToGameplay() {
+ currentGameState = GAME_STATE_GAMEPLAY;
+ hideAllPages();
+ if (!gameplayContainer) {
+ initializeGameplay();
+ }
+ gameplayContainer.visible = true;
+}
+function hideAllPages() {
+ if (menuPage) menuPage.visible = false;
+ if (levelSelectionPage) levelSelectionPage.visible = false;
+ if (gameplayContainer) gameplayContainer.visible = false;
+}
+function initializeGameplay() {
+ // Create gameplay container to hold all game elements
+ gameplayContainer = game.addChild(new Container());
+ // Move all existing gameplay elements to the container
+ moveGameplayElementsToContainer();
+}
+function moveGameplayElementsToContainer() {
+ // Show all gameplay elements when switching to gameplay
+ if (gameplay) gameplay.visible = true;
+ if (ui) ui.visible = true;
+ // Show gameplay UI elements
+ if (moneyText) moneyText.visible = true;
+ if (lifeText) lifeText.visible = true;
+ if (waveText) waveText.visible = true;
+ if (puntero) puntero.visible = true;
+ // Show camino objects
+ for (var i = 0; i < caminoObjects.length; i++) {
+ if (caminoObjects[i]) caminoObjects[i].visible = true;
+ }
+ // Show end object
+ if (end) end.visible = true;
+ // Show tower buttons
+ for (var i = 0; i < towerButtons.length; i++) {
+ if (towerButtons[i]) {
+ towerButtons[i].visible = true;
+ if (towerButtons[i].buttonBG) towerButtons[i].buttonBG.visible = true;
+ }
+ }
+}
// Reusable tower creation function
7;
function createTowerBase(params) {
return Container.expand(function () {
@@ -334,154 +546,84 @@
return 0;
});
return enemiesInRange;
};
- // Method to perform expanding wave attack for ice tower
- self.performWaveAttack = function () {
- // Find all enemies in range
- var enemiesInRange = self.findEnemiesInRange();
- // Create expanding wave visual effect
- var waveEffect = self.attachAsset('AttackIce', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- waveEffect.alpha = 0.7;
- waveEffect.scaleX = 0.1;
- waveEffect.scaleY = 0.1;
- waveEffect.x = 0;
- waveEffect.y = 0;
- // Calculate final wave scale based on tower range
- var finalScale = self.range * 2 / 100; // AttackIce asset is 100x100
- // Animate wave expansion
- tween(waveEffect, {
- scaleX: finalScale,
- scaleY: finalScale,
- alpha: 0
- }, {
- duration: 800,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- if (waveEffect.parent) {
- waveEffect.destroy();
- }
- }
- });
- // Apply slow effect to all enemies in range
- for (var i = 0; i < enemiesInRange.length; i++) {
- var enemy = enemiesInRange[i];
- if (enemy && enemy.parent) {
- // Apply slow effect (default 20% slow, upgradeable)
- var slowEffect = self.slowEffect || 0.8; // Default 20% slow
- var slowDuration = self.slowDuration || 1000; // Default 1 second
- enemy.slowEffect = slowEffect;
- enemy.slowDuration = slowDuration;
- // Apply damage on freeze if upgrade is purchased
- if (self.damageOnFreeze && enemy.takeDamage) {
- enemy.takeDamage(1); // Remove 1 layer
- }
- // Check for stun effect (100% slow for 1 second)
- if (self.stunChance && Math.random() < self.stunChance) {
- // Create separate stun effect list if not exists
- if (!enemy.stunEffects) {
- enemy.stunEffects = [];
- }
- // Add stun effect
- enemy.stunEffects.push({
- effect: 0.0,
- // 100% slow (enemy stops)
- duration: 1000 // 1 second
- });
- }
- // Update enemy movement speed if currently moving
- if (enemy.isMoving && enemy.moveSpeed) {
- enemy.moveSpeed = enemy.moveSpeed * enemy.slowEffect;
- }
- }
- }
- };
- // Method to shoot at target - ice tower uses expanding wave attack
+ // Method to shoot at target
self.shoot = function (target) {
var currentTime = Date.now();
var effectiveCadence = self.cadence;
// Apply fastGame speed boost to firing rate (50% faster = reduce cadence by 33%)
if (fastGame) {
effectiveCadence = self.cadence / 1.5;
}
if (currentTime - self.lastShotTime >= effectiveCadence) {
- // Ice tower uses expanding wave attack instead of bullets
- if (params.asset === 'Icetower') {
- // Create expanding wave attack for ice tower
- self.performWaveAttack();
- } else {
- // Regular towers use bullet shooting
- // Function to create and fire a bullet
- var createBullet = function createBullet(angleOffset) {
- 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 enemy position based on distance
- var bulletSpeed = 800 * (self.bulletSpeedMultiplier || 1); // Apply speed multiplier if available
- // Apply fastGame speed boost to bullet speed (50% faster)
- if (fastGame) {
- bulletSpeed = bulletSpeed * 1.5;
+ // Function to create and fire a bullet
+ var createBullet = function createBullet(angleOffset) {
+ 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 enemy position based on distance
+ var bulletSpeed = 800 * (self.bulletSpeedMultiplier || 1); // Apply speed multiplier if available
+ // Apply fastGame speed boost to bullet speed (50% faster)
+ if (fastGame) {
+ bulletSpeed = bulletSpeed * 1.5;
+ }
+ var timeToReach = distance / bulletSpeed; // time in seconds for bullet to reach current target position
+ // Calculate enemy movement prediction - farther enemies get more prediction (reduced)
+ var baseEnemySpeed = 260; // Base enemy speed in pixels per second
+ var enemySpeed = getEnemySpeed(target);
+ // Adjust prediction factor based on fastGame mode for better accuracy
+ var predictionFactor = fastGame ? 0.8 : 0.6; // More aggressive prediction in fast mode
+ var predictionDistance = enemySpeed * timeToReach * predictionFactor;
+ // Get enemy's current movement direction
+ var enemyDx = 0;
+ var enemyDy = 0;
+ if (target.currentWaypointIndex < caminoPositions.length - 1) {
+ var nextWaypoint = caminoPositions[target.currentWaypointIndex + 1];
+ enemyDx = nextWaypoint.x - target.x;
+ enemyDy = nextWaypoint.y - target.y;
+ var enemyMoveDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy);
+ if (enemyMoveDistance > 0) {
+ // Normalize enemy direction
+ enemyDx = enemyDx / enemyMoveDistance;
+ enemyDy = enemyDy / enemyMoveDistance;
}
- var timeToReach = distance / bulletSpeed; // time in seconds for bullet to reach current target position
- // Calculate enemy movement prediction - farther enemies get more prediction (reduced)
- var baseEnemySpeed = 260; // Base enemy speed in pixels per second
- var enemySpeed = getEnemySpeed(target);
- // Adjust prediction factor based on fastGame mode for better accuracy
- var predictionFactor = fastGame ? 0.8 : 0.6; // More aggressive prediction in fast mode
- var predictionDistance = enemySpeed * timeToReach * predictionFactor;
- // Get enemy's current movement direction
- var enemyDx = 0;
- var enemyDy = 0;
- if (target.currentWaypointIndex < caminoPositions.length - 1) {
- var nextWaypoint = caminoPositions[target.currentWaypointIndex + 1];
- enemyDx = nextWaypoint.x - target.x;
- enemyDy = nextWaypoint.y - target.y;
- var enemyMoveDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy);
- if (enemyMoveDistance > 0) {
- // Normalize enemy direction
- enemyDx = enemyDx / enemyMoveDistance;
- enemyDy = enemyDy / enemyMoveDistance;
- }
- }
- // Calculate predicted target position
- var predictedX = target.x + enemyDx * predictionDistance;
- var predictedY = target.y + enemyDy * predictionDistance;
- // Calculate direction to predicted position with angle offset
- var predictedDx = predictedX - self.x;
- var predictedDy = predictedY - self.y;
- var baseAngle = Math.atan2(predictedDy, predictedDx);
- var adjustedAngle = baseAngle + angleOffset;
- var offsetDistance = 50; // Distance to spawn bullet ahead of tower
- // Spawn bullet slightly ahead in the adjusted direction
- bullet.x = self.x + Math.cos(adjustedAngle) * offsetDistance;
- bullet.y = self.y + Math.sin(adjustedAngle) * offsetDistance;
- // Set bullet direction for straight line movement in adjusted direction
- bullet.directionX = Math.cos(adjustedAngle);
- bullet.directionY = Math.sin(adjustedAngle);
- bullet.target = target;
- bullet.damage = self.damage;
- bullet.speed = bulletSpeed; // Use calculated speed with multiplier
- bullet.towerRef = self; // Store reference to the tower that fired this bullet
- // Rotate bullet to face adjusted direction
- bullet.rotation = adjustedAngle;
- self.bullets.push(bullet);
- game.addChild(bullet);
- };
- createBullet(0);
- // Fire side bullets if multishot upgrade is active
- if (self.hasMultishot) {
- createBullet(-Math.PI / 12); // 15 degrees to the left
- createBullet(Math.PI / 12); // 15 degrees to the right
}
- // Play bullet shooting sound
- LK.getSound('Balababa').play();
+ // Calculate predicted target position
+ var predictedX = target.x + enemyDx * predictionDistance;
+ var predictedY = target.y + enemyDy * predictionDistance;
+ // Calculate direction to predicted position with angle offset
+ var predictedDx = predictedX - self.x;
+ var predictedDy = predictedY - self.y;
+ var baseAngle = Math.atan2(predictedDy, predictedDx);
+ var adjustedAngle = baseAngle + angleOffset;
+ var offsetDistance = 50; // Distance to spawn bullet ahead of tower
+ // Spawn bullet slightly ahead in the adjusted direction
+ bullet.x = self.x + Math.cos(adjustedAngle) * offsetDistance;
+ bullet.y = self.y + Math.sin(adjustedAngle) * offsetDistance;
+ // Set bullet direction for straight line movement in adjusted direction
+ bullet.directionX = Math.cos(adjustedAngle);
+ bullet.directionY = Math.sin(adjustedAngle);
+ bullet.target = target;
+ bullet.damage = self.damage;
+ bullet.speed = bulletSpeed; // Use calculated speed with multiplier
+ bullet.towerRef = self; // Store reference to the tower that fired this bullet
+ // Rotate bullet to face adjusted direction
+ bullet.rotation = adjustedAngle;
+ self.bullets.push(bullet);
+ game.addChild(bullet);
+ };
+ createBullet(0);
+ // Fire side bullets if multishot upgrade is active
+ if (self.hasMultishot) {
+ createBullet(-Math.PI / 12); // 15 degrees to the left
+ createBullet(Math.PI / 12); // 15 degrees to the right
}
self.lastShotTime = currentTime;
+ // Play bullet shooting sound
+ LK.getSound('Balababa').play();
// Initialize attack sprites if not already created
// Use ice-specific attack sprites if this is an ice tower
var attackAsset1 = params.asset === 'Icetower' ? 'IceTowerAttack1' : 'TorreinicialAttack';
var attackAsset2 = params.asset === 'Icetower' ? 'IceTowerAttack2' : 'TorreinicialAttack2';
@@ -603,78 +745,71 @@
}
// Find and shoot at enemies
var enemies = self.findEnemiesInRange();
if (enemies.length > 0) {
- // Ice tower uses wave attack - no targeting needed
- if (params.asset === 'Icetower') {
- // Ice tower attacks all enemies in range with wave
- self.shoot(null); // Pass null since ice tower doesn't need specific target
- } else {
- // Regular towers need targeting
- var target = enemies[0];
- // Set current target
- self.currentTarget = target;
- // Calculate predicted target position for tower rotation
- var distance = Math.sqrt((target.x - self.x) * (target.x - self.x) + (target.y - self.y) * (target.y - self.y));
- var bulletSpeed = 800; // pixels per second
- // Apply fastGame speed boost to bullet speed for prediction calculations
- if (fastGame) {
- bulletSpeed = bulletSpeed * 1.5;
+ var target = enemies[0];
+ // Set current target
+ self.currentTarget = target;
+ // Calculate predicted target position for tower rotation
+ var distance = Math.sqrt((target.x - self.x) * (target.x - self.x) + (target.y - self.y) * (target.y - self.y));
+ var bulletSpeed = 800; // pixels per second
+ // Apply fastGame speed boost to bullet speed for prediction calculations
+ if (fastGame) {
+ bulletSpeed = bulletSpeed * 1.5;
+ }
+ var timeToReach = distance / bulletSpeed;
+ var enemySpeed = getEnemySpeed(target);
+ // Adjust prediction factor based on fastGame mode for better accuracy
+ var predictionFactor = fastGame ? 0.8 : 0.6; // More aggressive prediction in fast mode
+ var predictionDistance = enemySpeed * timeToReach * predictionFactor;
+ // Get enemy's current movement direction
+ var enemyDx = 0;
+ var enemyDy = 0;
+ if (target.currentWaypointIndex < caminoPositions.length - 1) {
+ var nextWaypoint = caminoPositions[target.currentWaypointIndex + 1];
+ enemyDx = nextWaypoint.x - target.x;
+ enemyDy = nextWaypoint.y - target.y;
+ var enemyMoveDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy);
+ if (enemyMoveDistance > 0) {
+ // Normalize enemy direction
+ enemyDx = enemyDx / enemyMoveDistance;
+ enemyDy = enemyDy / enemyMoveDistance;
}
- var timeToReach = distance / bulletSpeed;
- var enemySpeed = getEnemySpeed(target);
- // Adjust prediction factor based on fastGame mode for better accuracy
- var predictionFactor = fastGame ? 0.8 : 0.6; // More aggressive prediction in fast mode
- var predictionDistance = enemySpeed * timeToReach * predictionFactor;
- // Get enemy's current movement direction
- var enemyDx = 0;
- var enemyDy = 0;
- if (target.currentWaypointIndex < caminoPositions.length - 1) {
- var nextWaypoint = caminoPositions[target.currentWaypointIndex + 1];
- enemyDx = nextWaypoint.x - target.x;
- enemyDy = nextWaypoint.y - target.y;
- var enemyMoveDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy);
- if (enemyMoveDistance > 0) {
- // Normalize enemy direction
- enemyDx = enemyDx / enemyMoveDistance;
- enemyDy = enemyDy / enemyMoveDistance;
- }
- }
- // Calculate predicted target position
- var predictedX = target.x + enemyDx * predictionDistance;
- var predictedY = target.y + enemyDy * predictionDistance;
- // Calculate angle to predicted target position
- var dx = predictedX - self.x;
- var dy = predictedY - self.y;
- var targetAngle = Math.atan2(dy, dx) + Math.PI / 2; // Add 90° rotation offset
- // Smooth rotation using tween animation
- var angleDiff = targetAngle - towerGraphics.rotation;
- // Normalize angle difference to shortest path
- while (angleDiff > Math.PI) {
- angleDiff -= 2 * Math.PI;
- }
- while (angleDiff < -Math.PI) {
- angleDiff += 2 * Math.PI;
- }
- // Only rotate if angle difference is significant (> 0.1 radians)
- if (Math.abs(angleDiff) > 0.1) {
- // Stop any existing rotation tween
- tween.stop(towerGraphics, {
- rotation: true
- });
- // Animate rotation smoothly
- tween(towerGraphics, {
- rotation: targetAngle
- }, {
- duration: 200,
- easing: tween.easeOut
- });
- }
- // Keep tower orientation consistent - no flipping based on target position
- towerGraphics.scaleY = -1; // Always maintain normal orientation
- // Shoot at the first enemy found
- self.shoot(target);
}
+ // Calculate predicted target position
+ var predictedX = target.x + enemyDx * predictionDistance;
+ var predictedY = target.y + enemyDy * predictionDistance;
+ // Calculate angle to predicted target position
+ var dx = predictedX - self.x;
+ var dy = predictedY - self.y;
+ var targetAngle = Math.atan2(dy, dx) + Math.PI / 2; // Add 90° rotation offset
+ // Smooth rotation using tween animation
+ var angleDiff = targetAngle - towerGraphics.rotation;
+ // Normalize angle difference to shortest path
+ while (angleDiff > Math.PI) {
+ angleDiff -= 2 * Math.PI;
+ }
+ while (angleDiff < -Math.PI) {
+ angleDiff += 2 * Math.PI;
+ }
+ // Only rotate if angle difference is significant (> 0.1 radians)
+ if (Math.abs(angleDiff) > 0.1) {
+ // Stop any existing rotation tween
+ tween.stop(towerGraphics, {
+ rotation: true
+ });
+ // Animate rotation smoothly
+ tween(towerGraphics, {
+ rotation: targetAngle
+ }, {
+ duration: 200,
+ easing: tween.easeOut
+ });
+ }
+ // Keep tower orientation consistent - no flipping based on target position
+ towerGraphics.scaleY = -1; // Always maintain normal orientation
+ // Shoot at the first enemy found
+ self.shoot(target);
} else {
// No enemies in range, clear current target
self.currentTarget = null;
}
@@ -754,10 +889,12 @@
buttonBG.y = y;
var button = game.addChild(new TowerClass());
button.x = x;
button.y = y;
+ button.visible = false; // Hide initially
// Store reference to button background for easy access
button.buttonBG = buttonBG;
+ buttonBG.visible = false; // Hide initially
button.down = function (x, y, obj) {
isDragging = true;
draggedTower = game.addChild(new TowerClass());
draggedTower.x = button.x;
@@ -857,13 +994,15 @@
// Background layer
var gameplay = game.addChild(new Gameplay());
gameplay.x = 1024;
gameplay.y = 1093;
+gameplay.visible = false; // Hide initially
// Game objects layer (towers will be added here)
// UI layer
var ui = game.addChild(new UI());
ui.x = 1024;
ui.y = 2459;
+ui.visible = false; // Hide initially
// Create tower creation buttons
var towerButtons = [];
var startX = 400; // Position button on the left side
towerButtons.push(createTowerButton(0xffffff, startX, 2459, Tower)); // White tower (normal)
@@ -994,8 +1133,9 @@
var caminoHeight = caminoPositions[i].height || 200; // Use height from array or default to 200
camino.scaleX = caminoWidth / 200; // Asset is 200x200 by default
camino.scaleY = caminoHeight / 200;
camino.alpha = 0.2; // Set transparency to 0.2
+ camino.visible = false; // Hide initially
caminoObjects.push(camino);
}
// Create end object and place it at the final waypoint
var end = game.addChild(LK.getAsset('End', {
@@ -1015,8 +1155,9 @@
end.y = finalPosition.y - dy * moveBackRatio;
var directionAngle = Math.atan2(dy, dx);
end.rotation = directionAngle;
end.alpha = 1.0; // Remove transparency
+end.visible = false; // Hide initially
// Wave system variables
var enemies = [];
// Dynamic wave generation function
function generateWaveData() {
@@ -1421,12 +1562,8 @@
// Apply fastGame speed boost
if (fastGame) {
speed = speed * 1.5; // 50% faster when fastGame is true
}
- // Apply slow effect if active
- if (this.slowEffect) {
- speed = speed * this.slowEffect;
- }
// Store movement properties
this.targetX = nextWaypoint.x;
this.targetY = nextWaypoint.y;
this.moveDirectionX = dx / distance; // Normalized direction
@@ -1469,52 +1606,8 @@
}
};
// Frame-by-frame update method for movement
self.update = function () {
- // Handle slow effect countdown
- if (this.slowEffect && this.slowDuration > 0) {
- this.slowDuration -= frameTime * 1000; // Convert frame time to milliseconds
- if (this.slowDuration <= 0) {
- // Slow effect expired, restore normal speed
- this.slowEffect = null;
- this.slowDuration = 0;
- // Recalculate movement speed if currently moving
- if (this.isMoving && this.moveSpeed) {
- var normalSpeed = getEnemySpeed(this);
- if (fastGame) {
- normalSpeed = normalSpeed * 1.5;
- }
- this.moveSpeed = normalSpeed / 60;
- }
- }
- }
- // Handle stun effects countdown
- if (this.stunEffects && this.stunEffects.length > 0) {
- var activeStunEffect = null;
- for (var s = this.stunEffects.length - 1; s >= 0; s--) {
- var stunEffect = this.stunEffects[s];
- stunEffect.duration -= frameTime * 1000;
- if (stunEffect.duration <= 0) {
- // Stun effect expired, remove it
- this.stunEffects.splice(s, 1);
- } else {
- // Keep the strongest stun effect (lowest value = more severe)
- if (!activeStunEffect || stunEffect.effect < activeStunEffect.effect) {
- activeStunEffect = stunEffect;
- }
- }
- }
- // Apply the strongest active stun effect
- if (activeStunEffect && this.isMoving && this.moveSpeed) {
- var baseSpeed = getEnemySpeed(this);
- if (fastGame) {
- baseSpeed = baseSpeed * 1.5;
- }
- // Apply both slow and stun effects (multiplicative)
- var totalEffect = (this.slowEffect || 1.0) * activeStunEffect.effect;
- this.moveSpeed = baseSpeed * totalEffect / 60;
- }
- }
// Handle end animation
if (this.isEndAnimation) {
var animationSpeedMultiplier = fastGame ? 1.5 : 1.0; // 50% faster animation when fastGame is true
this.endAnimationTimer -= frameTime * 1000 * animationSpeedMultiplier; // Convert frame time to milliseconds and apply speed multiplier
@@ -1694,334 +1787,128 @@
tower.rightUpgrades = tower.rightUpgrades || 0;
tower.rightUpgrades++;
}
}
-// Tower-specific upgrade definitions for flexible individual abilities
+// Reusable upgrade system
var upgradeDefinitions = {
- // Normal Tower (torreInicial) upgrades
- normal: {
- speed: {
- title: 'Rapid Fire',
- cost: 75,
- apply: function apply(tower) {
- tower.cadence = Math.max(100, tower.cadence * 0.8);
- incrementUpgradeCounter(tower, 'left');
- }
- },
- range: {
- title: 'Eagle Eye',
- cost: 100,
- apply: function apply(tower) {
- tower.range = Math.floor(tower.range * 1.25);
- tower.rangeSquared = tower.range * tower.range;
- updateRangeArea(tower);
- incrementUpgradeCounter(tower, 'left');
- }
- },
- multishot: {
- title: 'Triple Threat',
- cost: 150,
- apply: function apply(tower) {
- tower.hasMultishot = true;
- incrementUpgradeCounter(tower, 'left');
- }
- },
- left_final: {
- title: 'Berserker Mode',
- cost: 200,
- apply: function apply(tower) {
- tower.damage += 1;
- tower.cadence = Math.max(50, tower.cadence * 0.25);
- incrementUpgradeCounter(tower, 'left');
- }
- },
- right1_damage: {
- title: 'Brutal Force',
- cost: 50,
- apply: function apply(tower) {
- tower.damage += 1;
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_cross: {
- title: 'Chain Lightning',
- cost: 75,
- apply: function apply(tower) {
- tower.cross = (tower.cross || 1) + 2;
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_range: {
- title: 'Sniper Scope',
- cost: 100,
- apply: function apply(tower) {
- tower.range = Math.floor(tower.range * 1.3);
- tower.rangeSquared = tower.range * tower.range;
- updateRangeArea(tower);
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_cross_final: {
- title: 'Storm Walker',
- cost: 125,
- apply: function apply(tower) {
- tower.cross = (tower.cross || 1) + 2;
- tower.bulletSpeedMultiplier = 1.3;
- tower.bulletDurationMultiplier = 1.5;
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_ultimate: {
- title: 'Apocalypse',
- cost: 300,
- apply: function apply(tower) {
- tower.bulletSpeedMultiplier = (tower.bulletSpeedMultiplier || 1) * 1.6;
- tower.cross = (tower.cross || 1) + 6;
- tower.damage += 2;
- incrementUpgradeCounter(tower, 'right');
- }
+ speed: {
+ title: 'Rapid Fire',
+ cost: 75,
+ apply: function apply(tower) {
+ tower.cadence = Math.max(100, tower.cadence * 0.8);
+ incrementUpgradeCounter(tower, 'left');
}
},
- // Ice Tower (Icetower) upgrades
- ice: {
- range: {
- title: '+10% Range',
- cost: 100,
- apply: function apply(tower) {
- tower.range = Math.floor(tower.range * 1.1);
- tower.rangeSquared = tower.range * tower.range;
- updateRangeArea(tower);
- incrementUpgradeCounter(tower, 'left');
- }
- },
- slow_duration: {
- title: '+2 Sec Freeze',
- cost: 75,
- apply: function apply(tower) {
- tower.slowDuration = (tower.slowDuration || 1000) + 2000; // Add 2 seconds
- incrementUpgradeCounter(tower, 'left');
- }
- },
- damage_freeze: {
- title: 'Damage Freeze',
- cost: 125,
- apply: function apply(tower) {
- tower.damageOnFreeze = true; // Remove 1 layer when freezing
- incrementUpgradeCounter(tower, 'left');
- }
- },
- left_final: {
- title: 'Stun Field',
- cost: 250,
- apply: function apply(tower) {
- tower.stunChance = 0.3; // 30% chance to stun (100% slow) for 1 second
- incrementUpgradeCounter(tower, 'left');
- }
- },
- right1_slow_duration: {
- title: 'Lingering Frost',
- cost: 60,
- apply: function apply(tower) {
- tower.slowDuration = 1500; // Increase from 1 to 1.5 seconds
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_freeze_chance: {
- title: 'Ice Shards',
- cost: 100,
- apply: function apply(tower) {
- tower.freezeChance = 0.3; // 30% chance to freeze for 1 second
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_ice_storm: {
- title: 'Ice Storm',
- cost: 150,
- apply: function apply(tower) {
- tower.hasIceStorm = true; // Creates additional ice waves
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_ultimate: {
- title: 'Glacier Prison',
- cost: 300,
- apply: function apply(tower) {
- tower.slowEffect = 0.2; // 80% slow
- tower.slowDuration = 3000; // 3 seconds
- tower.freezeChance = 0.5; // 50% freeze chance
- tower.hasIceStorm = true;
- incrementUpgradeCounter(tower, 'right');
- }
+ range: {
+ title: 'Eagle Eye',
+ cost: 100,
+ apply: function apply(tower) {
+ tower.range = Math.floor(tower.range * 1.25);
+ tower.rangeSquared = tower.range * tower.range;
+ updateRangeArea(tower);
+ incrementUpgradeCounter(tower, 'left');
}
},
- // Fire Tower (FireTower) upgrades
- fire: {
- burn_damage: {
- title: 'Ignite',
- cost: 80,
- apply: function apply(tower) {
- tower.hasBurn = true;
- tower.burnDamage = 0.5; // Damage over time
- tower.burnDuration = 2000; // 2 seconds
- incrementUpgradeCounter(tower, 'left');
- }
- },
- explosive_shots: {
- title: 'Explosive Rounds',
- cost: 120,
- apply: function apply(tower) {
- tower.hasExplosion = true;
- tower.explosionRadius = 100;
- incrementUpgradeCounter(tower, 'left');
- }
- },
- inferno: {
- title: 'Inferno Blast',
- cost: 150,
- apply: function apply(tower) {
- tower.explosionRadius = Math.floor((tower.explosionRadius || 100) * 1.5);
- tower.burnDamage = (tower.burnDamage || 0.5) * 1.5;
- incrementUpgradeCounter(tower, 'left');
- }
- },
- left_final: {
- title: 'Phoenix Wrath',
- cost: 280,
- apply: function apply(tower) {
- tower.damage += 2;
- tower.hasExplosion = true;
- tower.explosionRadius = 200;
- tower.burnDamage = 1.0;
- tower.burnDuration = 3000;
- incrementUpgradeCounter(tower, 'left');
- }
- },
- right1_damage: {
- title: 'Blazing Power',
- cost: 50,
- apply: function apply(tower) {
- tower.damage += 1;
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_speed: {
- title: 'Rapid Burn',
- cost: 75,
- apply: function apply(tower) {
- tower.cadence = Math.max(100, tower.cadence * 0.8);
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_flame_spread: {
- title: 'Flame Spread',
- cost: 125,
- apply: function apply(tower) {
- tower.hasFlameSpread = true; // Burns spread to nearby enemies
- incrementUpgradeCounter(tower, 'right');
- }
- },
- right1_ultimate: {
- title: 'Solar Flare',
- cost: 350,
- apply: function apply(tower) {
- tower.damage += 3;
- tower.cadence = Math.max(50, tower.cadence * 0.5);
- tower.hasFlameSpread = true;
- tower.explosionRadius = 250;
- tower.burnDamage = 1.5;
- incrementUpgradeCounter(tower, 'right');
- }
+ multishot: {
+ title: 'Triple Threat',
+ cost: 150,
+ apply: function apply(tower) {
+ tower.hasMultishot = true;
+ incrementUpgradeCounter(tower, 'left');
}
},
- // Universal upgrades (available to all towers)
- universal: {
- targeting_priority: {
- title: 'Hunter Instinct',
- cost: 0,
- apply: function apply(tower) {
- var priorities = ['first', 'last', 'strongest'];
- var currentIndex = priorities.indexOf(tower.targetingPriority);
- tower.targetingPriority = priorities[(currentIndex + 1) % priorities.length];
- }
- },
- maxed: {
- title: 'Legendary',
- cost: 999999,
- apply: function apply(tower) {
- // Do nothing - this upgrade can't be purchased
- }
+ damage: {
+ title: 'Power Surge',
+ cost: 50,
+ apply: function apply(tower) {
+ tower.damage += 1;
}
- }
-};
-// Tower-specific upgrade path configurations
-var upgradePathConfigurations = {
- // Normal Tower paths
- normal: {
- left: ['speed', 'range', 'multishot', 'left_final'],
- right: ['right1_damage', 'right1_range', 'right1_cross_final', 'right1_ultimate']
},
- // Ice Tower paths
- ice: {
- left: ['range', 'slow_duration', 'damage_freeze', 'left_final'],
- right: ['right1_slow_duration', 'right1_freeze_chance', 'right1_ice_storm', 'right1_ultimate']
+ left_final: {
+ title: 'Berserker Mode',
+ cost: 200,
+ apply: function apply(tower) {
+ tower.damage += 1;
+ tower.cadence = Math.max(50, tower.cadence * 0.25);
+ incrementUpgradeCounter(tower, 'left');
+ }
},
- // Fire Tower paths
- fire: {
- left: ['burn_damage', 'explosive_shots', 'inferno', 'left_final'],
- right: ['right1_damage', 'right1_speed', 'right1_flame_spread', 'right1_ultimate']
- }
-};
-// Function to detect tower type from asset
-function getTowerType(tower) {
- // Check tower's asset type by looking at its children
- for (var i = 0; i < tower.children.length; i++) {
- var child = tower.children[i];
- if (child.texture && child.texture.baseTexture && child.texture.baseTexture.resource && child.texture.baseTexture.resource.url) {
- var url = child.texture.baseTexture.resource.url;
- if (url.includes('torreInicial')) {
- return 'normal';
- } else if (url.includes('Icetower')) {
- return 'ice';
- } else if (url.includes('FireTower')) {
- return 'fire';
- }
+ right1_damage: {
+ title: 'Brutal Force',
+ cost: 50,
+ apply: function apply(tower) {
+ tower.damage += 1;
+ incrementUpgradeCounter(tower, 'right');
}
+ },
+ right1_cross: {
+ title: 'Chain Lightning',
+ cost: 75,
+ apply: function apply(tower) {
+ tower.cross = (tower.cross || 1) + 2;
+ incrementUpgradeCounter(tower, 'right');
+ }
+ },
+ right1_range: {
+ title: 'Sniper Scope',
+ cost: 100,
+ apply: function apply(tower) {
+ tower.range = Math.floor(tower.range * 1.3);
+ tower.rangeSquared = tower.range * tower.range;
+ updateRangeArea(tower);
+ incrementUpgradeCounter(tower, 'right');
+ }
+ },
+ right1_cross_final: {
+ title: 'Storm Walker',
+ cost: 125,
+ apply: function apply(tower) {
+ tower.cross = (tower.cross || 1) + 2;
+ tower.bulletSpeedMultiplier = 1.3;
+ tower.bulletDurationMultiplier = 1.5;
+ incrementUpgradeCounter(tower, 'right');
+ }
+ },
+ right1_ultimate: {
+ title: 'Apocalypse',
+ cost: 300,
+ apply: function apply(tower) {
+ tower.bulletSpeedMultiplier = (tower.bulletSpeedMultiplier || 1) * 1.6;
+ tower.cross = (tower.cross || 1) + 6;
+ tower.damage += 2;
+ incrementUpgradeCounter(tower, 'right');
+ }
+ },
+ targeting_priority: {
+ title: 'Hunter Instinct',
+ cost: 0,
+ apply: function apply(tower) {
+ var priorities = ['first', 'last', 'strongest'];
+ var currentIndex = priorities.indexOf(tower.targetingPriority);
+ tower.targetingPriority = priorities[(currentIndex + 1) % priorities.length];
+ }
+ },
+ maxed: {
+ title: 'Legendary',
+ cost: 999999,
+ apply: function apply(tower) {
+ // Do nothing - this upgrade can't be purchased
+ }
}
- return 'normal'; // Default to normal if can't detect
-}
-// Function to get upgrade definition for a tower
-function getUpgradeDefinition(tower, upgradeKey) {
- var towerType = getTowerType(tower);
- var towerUpgrades = upgradeDefinitions[towerType];
- if (towerUpgrades && towerUpgrades[upgradeKey]) {
- return towerUpgrades[upgradeKey];
- }
- // Fall back to universal upgrades
- return upgradeDefinitions.universal[upgradeKey] || null;
-}
+};
+// Upgrade path configurations
+var leftUpgradePath = ['speed', 'range', 'multishot', 'left_final'];
+var rightUpgradePath = ['right1_damage', 'right1_range', 'right1_cross_final', 'right1_ultimate'];
// Function to get current left upgrade options for a tower
function getLeftUpgradeOptions(tower) {
tower.leftUpgrades = tower.leftUpgrades || 0;
- var towerType = getTowerType(tower);
- var pathConfig = upgradePathConfigurations[towerType];
- if (pathConfig && pathConfig.left) {
- return pathConfig.left[tower.leftUpgrades] || 'maxed';
- }
- return 'maxed';
+ return leftUpgradePath[tower.leftUpgrades] || 'maxed';
}
// Function to get current right upgrade options for a tower
function getRightUpgradeOptions(tower) {
tower.rightUpgrades = tower.rightUpgrades || 0;
- var towerType = getTowerType(tower);
- var pathConfig = upgradePathConfigurations[towerType];
- if (pathConfig && pathConfig.right) {
- return {
- first: pathConfig.right[tower.rightUpgrades] || 'maxed',
- second: null
- };
- }
return {
- first: 'maxed',
+ first: rightUpgradePath[tower.rightUpgrades] || 'maxed',
second: null
};
}
// Left upgrade frame - Damage +1
@@ -2123,9 +2010,9 @@
}
}
// Function to create upgrade button with animation and functionality
function createUpgradeButton(upgradeType, x, y, container) {
- var upgradeDef = getUpgradeDefinition(selectedTowerForUpgrade, upgradeType);
+ var upgradeDef = upgradeDefinitions[upgradeType];
if (!upgradeDef) {
return null;
}
var upgradeButton = container.addChild(LK.getAsset('uiBackground', {
@@ -2144,14 +2031,13 @@
upgradeText.x = x;
upgradeText.y = y;
// Add click handler with animation
upgradeButton.down = function (x, y, obj) {
- var currentUpgradeDef = getUpgradeDefinition(selectedTowerForUpgrade, upgradeType);
- if (selectedTowerForUpgrade && currentUpgradeDef && playerMoney >= currentUpgradeDef.cost && upgradeType !== 'maxed') {
+ if (selectedTowerForUpgrade && playerMoney >= upgradeDef.cost && upgradeType !== 'maxed') {
// Apply upgrade
- currentUpgradeDef.apply(selectedTowerForUpgrade);
+ upgradeDef.apply(selectedTowerForUpgrade);
// Deduct cost
- playerMoney -= currentUpgradeDef.cost;
+ playerMoney -= upgradeDef.cost;
// Animate button press effect
tween(upgradeButton, {
scaleX: 0.12,
scaleY: 0.12
@@ -2182,19 +2068,12 @@
upgradeButton.alpha = 0.5;
upgradeText.alpha = 0.5;
upgradeText.setText('BUY MAX');
} else {
- var currentUpgradeDef = getUpgradeDefinition(selectedTowerForUpgrade, upgradeType);
- if (currentUpgradeDef) {
- var canAfford = selectedTowerForUpgrade && playerMoney >= currentUpgradeDef.cost;
- upgradeButton.alpha = canAfford ? 1.0 : 0.5;
- upgradeText.alpha = canAfford ? 1.0 : 0.5;
- upgradeText.setText('BUY');
- } else {
- upgradeButton.alpha = 0.5;
- upgradeText.alpha = 0.5;
- upgradeText.setText('N/A');
- }
+ var canAfford = selectedTowerForUpgrade && playerMoney >= upgradeDef.cost;
+ upgradeButton.alpha = canAfford ? 1.0 : 0.5;
+ upgradeText.alpha = canAfford ? 1.0 : 0.5;
+ upgradeText.setText('BUY');
}
}
};
}
@@ -2247,20 +2126,13 @@
var config = upgradeButtonConfigs[i];
var option = upgradeOptions[i];
upgradeButtons[i] = option ? createUpgradeButton(option, config.x, config.y, towerUpgradeContainer) : null;
// Update frame visibility
- if (option) {
- var upgradeDef = getUpgradeDefinition(tower, option);
- if (upgradeDef) {
- var title = option === 'targeting_priority' ? 'Priority: ' + (tower.targetingPriority === 'first' ? 'First' : tower.targetingPriority === 'last' ? 'Last' : 'Strongest') : upgradeDef.title;
- config.title.setText(title);
- config.frame.visible = true;
- config.title.visible = true;
- } else {
- config.title.setText('');
- config.frame.visible = false;
- config.title.visible = false;
- }
+ if (option && upgradeDefinitions[option]) {
+ var title = option === 'targeting_priority' ? 'Priority: ' + (tower.targetingPriority === 'first' ? 'First' : tower.targetingPriority === 'last' ? 'Last' : 'Strongest') : upgradeDefinitions[option].title;
+ config.title.setText(title);
+ config.frame.visible = true;
+ config.title.visible = true;
} else {
config.title.setText('');
config.frame.visible = false;
config.title.visible = false;
@@ -2300,24 +2172,27 @@
});
moneyText.anchor.set(1, 0);
moneyText.x = 1900;
moneyText.y = 150;
+moneyText.visible = false; // Hide initially
game.addChild(moneyText);
var lifeText = new Text2('Life: 20', {
size: 80,
fill: 0xFF0000
});
lifeText.anchor.set(1, 0);
lifeText.x = 1900;
lifeText.y = 250;
+lifeText.visible = false; // Hide initially
game.addChild(lifeText);
var waveText = new Text2('Wave: 1', {
size: 80,
fill: 0x00FF00
});
waveText.anchor.set(1, 0);
waveText.x = 1900;
waveText.y = 350;
+waveText.visible = false; // Hide initially
game.addChild(waveText);
// Add fast mode button to UI towers section
var speedButton = ui.addChild(LK.getAsset('x1Speed', {
anchorX: 0.5,
@@ -2438,8 +2313,12 @@
var fastGame = false; // Boolean to control 2x speed mode
var autoStartWaves = false; // Boolean to control automatic wave starting
var antiHielo = false; // Boolean to control anti ice effect
var antiFuego = false; // Boolean to control anti fire effect
+// Initialize menu page on game start
+LK.setTimeout(function () {
+ switchToMenu();
+}, 100);
// Camino path dimensions - can be changed while maintaining 200x200 default
var caminoWidth = 200;
var caminoHeight = 200;
// Top overlay layer (pointer on top of everything)
@@ -2449,8 +2328,9 @@
}));
puntero.x = 1024;
puntero.y = 1366;
puntero.alpha = 0;
+puntero.visible = false; // Hide initially
// Game move handler for dragging
game.move = function (x, y, obj) {
puntero.x = x;
puntero.y = y;