User prompt
Make level2 wave2 enemies smaller ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move alien head down 10
User prompt
Make level2 enemy a slightly lighter grey
User prompt
Move alien head left 10
User prompt
Remove tint from aliens head
User prompt
Make level2 enemy dark grey ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
When water tower attacks play water sound
User prompt
When water tower attacks play water sound
User prompt
Please fix this
User prompt
When tower buttons are pressed play towerselect sound
User prompt
Click tower buttons to play towerselect sound
User prompt
When probedroid 2 is spawned play probedroid2 sound
User prompt
When probedroid 1 is spawned play probedroid sound
User prompt
2 seconds after probedroid appears on screen play probedroid sound ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make alien head move left when asset 3 is visible on enemy ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make alien head animation only move 5 pixels left and right ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Change alien head animation to move every .4 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move alien head right 15 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make alien head slightly move right and left only by 15 pixels as alien walks forward. And a .3 seconds pause every .5 seconds to enhance the walking effect ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move alien head left 20
User prompt
Make alien head move back and forth in time with animation ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Move alien head left 50 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Separate cutscene and gameplay loops** - Create distinct update methods for cutscenes vs gameplay - Only call the gameplay update (including enemy spawning) when not in a cutscene
User prompt
Add explosive effect when level2 enemy laserfire destroys a tower ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make level2 enemy laserfire powerful
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Button class var Button = Container.expand(function () { var self = Container.call(this); // Button properties self.text = ''; self.backgroundColor = 0x333333; self.textColor = 0xffffff; self.width = 200; self.height = 80; self.fontSize = 40; self.isPressed = false; // Create button background self.background = self.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, tint: self.backgroundColor }); // Create button text self.buttonText = new Text2(self.text, { size: self.fontSize, fill: self.textColor }); self.buttonText.anchor.set(0.5, 0.5); self.buttonText.x = 0; self.buttonText.y = 0; self.addChild(self.buttonText); // Method to set button properties self.setup = function (options) { if (options.text !== undefined) { self.text = options.text; self.buttonText.setText(self.text); } if (options.backgroundColor !== undefined) { self.backgroundColor = options.backgroundColor; self.background.tint = self.backgroundColor; } if (options.textColor !== undefined) { self.textColor = options.textColor; // Recreate text with new color since Text2 doesn't have a modifiable style property self.removeChild(self.buttonText); self.buttonText = new Text2(self.text, { size: self.fontSize, fill: self.textColor }); self.buttonText.anchor.set(0.5, 0.5); self.buttonText.x = 0; self.buttonText.y = 0; self.addChild(self.buttonText); } if (options.width !== undefined) { self.width = options.width; self.background.width = self.width; } if (options.height !== undefined) { self.height = options.height; self.background.height = self.height; } if (options.fontSize !== undefined) { self.fontSize = options.fontSize; // Recreate text with new font size since Text2 doesn't have a modifiable style property self.removeChild(self.buttonText); self.buttonText = new Text2(self.text, { size: self.fontSize, fill: self.textColor }); self.buttonText.anchor.set(0.5, 0.5); self.buttonText.x = 0; self.buttonText.y = 0; self.addChild(self.buttonText); } }; // Touch down event self.down = function (x, y, obj) { self.isPressed = true; // Visual feedback - darken button self.background.alpha = 0.7; // Call custom down handler if defined if (self.onDown) { self.onDown(x, y, obj); } }; // Touch up event self.up = function (x, y, obj) { if (self.isPressed) { self.isPressed = false; // Reset visual state self.background.alpha = 1.0; // Call custom up handler if defined if (self.onUp) { self.onUp(x, y, obj); } // Call click handler if defined if (self.onClick) { self.onClick(x, y, obj); } } }; return self; }); // Drone class for wave 2 var Drone = Container.expand(function () { var self = Container.call(this); // Attach drone as the main enemy graphic var droneGraphics = self.attachAsset('Drone', { anchorX: 0.5, anchorY: 0.5 }); // Add flashing lights to drone var droneLight1 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: -30, y: 5, scaleX: 0.025, scaleY: 0.025, tint: 0xff0000, alpha: 0.6 }); var droneLight2 = self.attachAsset('squareLight', { anchorX: 0.5, anchorY: 0.5, x: 40, y: 0, scaleX: 0.7, scaleY: 0.7, tint: 0x00ff00, alpha: 0.6 }); var droneLight3 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -90, scaleX: 0.02, scaleY: 0.02, tint: 0x0000ff, alpha: 0.6 }); // Start flashing animations for drone lights function startDroneLightFlashing() { // Red light flashing tween(droneLight1, { alpha: 0.1 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(droneLight1, { alpha: 0.7 }, { duration: 600, easing: tween.easeInOut, onFinish: startDroneLightFlashing }); } }); // Green light flashing (offset timing) LK.setTimeout(function () { tween(droneLight2, { alpha: 0.15 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(droneLight2, { alpha: 0.7 }, { duration: 500, easing: tween.easeInOut }); } }); }, 200); // Blue light flashing (different timing) LK.setTimeout(function () { tween(droneLight3, { alpha: 0.2 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(droneLight3, { alpha: 0.7 }, { duration: 800, easing: tween.easeInOut }); } }); }, 400); } // Start the flashing animation startDroneLightFlashing(); // Initialize drone properties self.speed = -2; // Same speed as probedroids self.health = 80; // Less health than probedroids self.maxHealth = 80; self.pathLine = 1; self.lastX = undefined; self.electricEffectVisible = false; // Add number text self.numberText = new Text2('1', { size: 60, fill: 0x000000 }); self.numberText.anchor.set(0.5, 0.5); self.numberText.x = 0; self.numberText.y = 0; self.addChild(self.numberText); // Add health bar var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150, tint: 0x00ff00 }); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { healthBar.tint = 0xffff00; // Yellow } else { healthBar.tint = 0xff0000; // Red } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; } self.updateHealthBar(); }; // Function to make electric effect visible self.showElectricEffect = function () { // Drones don't have electric effect visuals like probedroids // This is implemented for consistency with tower interactions }; // Add laser fire cooldown tracking self.lastLaserFireTime = 0; // Add laser fire back attack for drone when hit self.fireBackAtTower = function (attackingTower) { // Check if tower is on the same line as drone (within 50 pixels tolerance) if (Math.abs(attackingTower.y - self.y) > 50) { return; // Tower not on same line, don't fire } // Check if there are any towers blocking the line of sight to the attacking tower for (var i = 0; i < towers.length; i++) { var blockingTower = towers[i]; if (blockingTower === attackingTower) continue; // Skip the target tower // Check if blocking tower is on the same line (within 50 pixels tolerance) if (Math.abs(blockingTower.y - self.y) > 50) continue; // Check if blocking tower is between drone and attacking tower if (blockingTower.x > self.x && blockingTower.x < attackingTower.x) { return; // Tower is blocked, don't fire } } // Check cooldown - fire every 45 ticks (0.75 seconds) instead of immediately if (LK.ticks - self.lastLaserFireTime < 45) { return; // Still in cooldown } self.lastLaserFireTime = LK.ticks; // Create laser fire back projectile var laser = game.addChild(LK.getAsset('Laserfire', { anchorX: 0.5, anchorY: 0.5, x: self.x - 200, y: self.y, alpha: 0.9, tint: 0xff0000 // Red laser for drones })); // Calculate direction to attacking tower var deltaX = attackingTower.x - laser.x; var deltaY = attackingTower.y - laser.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var speed = 6; laser.velocityX = deltaX / distance * speed; laser.velocityY = deltaY / distance * speed; laser.damage = 15; // Increased damage from 5 to 15 // Set initial rotation to point at tower var initialAngle = Math.atan2(deltaY, deltaX); laser.rotation = initialAngle; // Animate laser movement var laserInterval = LK.setInterval(function () { laser.x += laser.velocityX; laser.y += laser.velocityY; // Check if laser hit the attacking tower var laserDistance = Math.sqrt(Math.pow(laser.x - attackingTower.x, 2) + Math.pow(laser.y - attackingTower.y, 2)); if (laserDistance < 100) { // Hit tower - deal damage attackingTower.takeDamage(laser.damage); LK.effects.flashObject(attackingTower, 0xff0000, 300); // Remove laser LK.clearInterval(laserInterval); laser.destroy(); return; } // Check if laser went off screen if (laser.x > 2048 + 100 || laser.x < -100 || laser.y > 2732 + 100 || laser.y < -100) { LK.clearInterval(laserInterval); laser.destroy(); } }, 16); }; self.update = function () { // Initialize lastX if not set if (self.lastX === undefined) self.lastX = self.x; // Update health bar self.updateHealthBar(); }; return self; }); // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); // Attach probedroid as the main enemy graphic var enemyGraphics = self.attachAsset('Probedroid', { anchorX: 0.5, anchorY: 0.5 }); // Add flashing lights to probe droid var light1 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: -25, y: -85, scaleX: 0.03, scaleY: 0.03, tint: 0xff0000, alpha: 0.5 }); var light2 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: 50, y: -80, scaleX: 0.03, scaleY: 0.03, tint: 0x00ff00, alpha: 0.5 }); var light3 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -100, scaleX: 0.02, scaleY: 0.02, tint: 0x0000ff, alpha: 0.5 }); // Add electric effect to probe droid var electricEffect = self.attachAsset('Electriceffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -50, scaleX: 1.0, scaleY: 1.0, alpha: 0.7, visible: false }); // Start flashing animations for the lights function startLightFlashing() { // Red light flashing tween(light1, { alpha: 0.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(light1, { alpha: 0.6 }, { duration: 800, easing: tween.easeInOut, onFinish: startLightFlashing }); } }); // Green light flashing (offset timing) LK.setTimeout(function () { tween(light2, { alpha: 0.1 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(light2, { alpha: 0.6 }, { duration: 600, easing: tween.easeInOut }); } }); }, 300); // Blue light flashing (different timing) LK.setTimeout(function () { tween(light3, { alpha: 0.15 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(light3, { alpha: 0.6 }, { duration: 1000, easing: tween.easeInOut }); } }); }, 600); } // Start the flashing animation startLightFlashing(); // Start electric effect animation function startElectricEffect() { // Animate electric effect fading in and out tween(electricEffect, { alpha: 0.2 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(electricEffect, { alpha: 0.9 }, { duration: 600, easing: tween.easeInOut, onFinish: startElectricEffect }); } }); } // Store electric effect reference self.electricEffect = electricEffect; // Function to make electric effect visible self.showElectricEffect = function () { if (!self.electricEffectVisible) { self.electricEffectVisible = true; self.electricEffect.visible = true; } }; // Start the electric effect animation startElectricEffect(); // Initialize enemy properties self.speed = -2; // Default movement speed self.health = 100; // Default health self.maxHealth = 100; // Default max health self.pathLine = 1; // Default path line self.lastX = undefined; // Track last X position self.electricEffectVisible = false; // Track if electric effect is visible // Add number text self.numberText = new Text2('1', { size: 60, fill: 0x000000 }); self.numberText.anchor.set(0.5, 0.5); self.numberText.x = 0; self.numberText.y = 0; self.addChild(self.numberText); // Add health bar var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150, tint: 0x00ff00 }); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { healthBar.tint = 0xffff00; // Yellow } else { healthBar.tint = 0xff0000; // Red } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; } self.updateHealthBar(); }; self.update = function () { // Initialize lastX if not set if (self.lastX === undefined) self.lastX = self.x; // Update health bar self.updateHealthBar(); }; return self; }); // Level2Enemy class for level 2 waves - uses 1,2,3,4 assets with switching visibility var Level2Enemy = Container.expand(function () { var self = Container.call(this); // Attach all 4 enemy graphics, initially hidden var enemy1 = self.attachAsset('1', { anchorX: 0.5, anchorY: 0.5, visible: true, tint: 0x606060 }); var enemy2 = self.attachAsset('2', { anchorX: 0.5, anchorY: 0.5, visible: false, tint: 0x606060 }); var enemy3 = self.attachAsset('3', { anchorX: 0.5, anchorY: 0.5, visible: false, tint: 0x606060 }); var enemy4 = self.attachAsset('4', { anchorX: 0.5, anchorY: 0.5, visible: false, tint: 0x606060 }); // Add alien head as a fixed visual element (always visible) var alienHead = self.attachAsset('Alien', { anchorX: 0.5, anchorY: 0.5, x: 15, y: -105 }); // Store references to all graphics self.enemyGraphics = [enemy1, enemy2, enemy3, enemy4]; self.currentGraphicIndex = 0; // Add laser fire cooldown tracking self.lastLaserFireTime = 0; // Start visibility switching animation function startVisibilitySwitching() { // Switch every 0.3 seconds (18 ticks at 60fps) LK.setInterval(function () { if (self.parent) { // Hide current graphic self.enemyGraphics[self.currentGraphicIndex].visible = false; // Move to next graphic self.currentGraphicIndex = (self.currentGraphicIndex + 1) % 4; // Show next graphic self.enemyGraphics[self.currentGraphicIndex].visible = true; } }, 300); // 0.3 seconds } startVisibilitySwitching(); // Initialize enemy properties - extremely strong health for level 2 self.speed = -1.2; // Even slower but more persistent movement self.health = 1000; // Extremely high health - extremely hard to kill self.maxHealth = 1000; self.pathLine = 1; self.lastX = undefined; self.electricEffectVisible = false; // Add number text self.numberText = new Text2('1', { size: 60, fill: 0x000000 }); self.numberText.anchor.set(0.5, 0.5); self.numberText.x = 0; self.numberText.y = 0; self.addChild(self.numberText); // Add health bar var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -300 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -300, tint: 0xff0000 // Red health bar for level 2 enemies }); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; healthBar.scaleX = healthPercentage; // Change color based on health - darker colors for stronger enemy if (healthPercentage > 0.7) { healthBar.tint = 0x800000; // Dark red } else if (healthPercentage > 0.4) { healthBar.tint = 0xcc0000; // Medium red } else { healthBar.tint = 0x660000; // Very dark red } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; } self.updateHealthBar(); }; // Function to make electric effect visible self.showElectricEffect = function () { // Level 2 enemies don't have electric effect visuals }; // Add laser fire method for Level2Enemy when assets 1,2 are visible self.fireAtTowers = function () { // Only shoot when asset 1 or 2 is visible (index 0 or 1) if (self.currentGraphicIndex !== 0 && self.currentGraphicIndex !== 1) { return; // Don't fire when assets 3 or 4 are visible } // Check cooldown - fire every 90 ticks (1.5 seconds) if (LK.ticks - self.lastLaserFireTime < 90) { return; // Still in cooldown } // Find towers to shoot at var validTargets = []; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower.health > 0) { // Check if tower is on the same line or adjacent lines var lineDifference = Math.abs(tower.y - self.y); if (lineDifference < 200) { // Within shooting range // Check if tower is to the left of the enemy (in shooting direction) if (tower.x < self.x) { validTargets.push(tower); } } } } // If we have valid targets, shoot at the closest one if (validTargets.length > 0) { // Find closest tower var closestTower = validTargets[0]; var closestDistance = Math.sqrt(Math.pow(closestTower.x - self.x, 2) + Math.pow(closestTower.y - self.y, 2)); for (var i = 1; i < validTargets.length; i++) { var tower = validTargets[i]; var distance = Math.sqrt(Math.pow(tower.x - self.x, 2) + Math.pow(tower.y - self.y, 2)); if (distance < closestDistance) { closestTower = tower; closestDistance = distance; } } self.lastLaserFireTime = LK.ticks; // Create laser fire projectile var laser = game.addChild(LK.getAsset('Laserfire', { anchorX: 0.5, anchorY: 0.5, x: self.x - 100, y: self.y - 20, alpha: 0.9, tint: 0x00ff00 // Green laser for Level2Enemy })); // Calculate direction to target tower var deltaX = closestTower.x - laser.x; var deltaY = closestTower.y - laser.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var speed = 8; // Fast laser speed laser.velocityX = deltaX / distance * speed; laser.velocityY = deltaY / distance * speed; laser.damage = 50; // Very high damage for level 2 enemies - more powerful // Set initial rotation to point at tower var initialAngle = Math.atan2(deltaY, deltaX); laser.rotation = initialAngle; // Animate laser movement var laserInterval = LK.setInterval(function () { laser.x += laser.velocityX; laser.y += laser.velocityY; // Check if laser hit the target tower var laserDistance = Math.sqrt(Math.pow(laser.x - closestTower.x, 2) + Math.pow(laser.y - closestTower.y, 2)); if (laserDistance < 100) { // Hit tower - deal damage closestTower.takeDamage(laser.damage); // Check if tower is destroyed and create explosive effect if (closestTower.health <= 0) { // Create explosive effect when level2 enemy laser destroys a tower var towerExplosion = game.addChild(LK.getAsset('Explosion', { anchorX: 0.5, anchorY: 0.5, x: closestTower.x, y: closestTower.y, scaleX: 0.4, scaleY: 0.4 })); // Animate explosion with tween tween(towerExplosion, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { towerExplosion.destroy(); } }); } LK.effects.flashObject(closestTower, 0xff0000, 500); // Remove laser LK.clearInterval(laserInterval); laser.destroy(); return; } // Check if laser went off screen if (laser.x > 2048 + 100 || laser.x < -100 || laser.y > 2732 + 100 || laser.y < -100) { LK.clearInterval(laserInterval); laser.destroy(); } }, 16); } }; self.update = function () { // Initialize lastX if not set if (self.lastX === undefined) self.lastX = self.x; // Update health bar self.updateHealthBar(); // Fire lasers when assets 1,2 are visible self.fireAtTowers(); // Alien head walking animation - initialize walking properties if (!self.walkingInitialized) { self.walkingInitialized = true; self.walkingTimer = 0; self.walkingDirection = 1; // 1 for right, -1 for left self.isPaused = false; self.pauseTimer = 0; self.alienHeadBaseX = alienHead.x; // Store original position } // Update walking timer self.walkingTimer++; self.pauseTimer++; // Check if we need to pause (every 0.4 seconds = 24 ticks) if (!self.isPaused && self.walkingTimer >= 24) { self.isPaused = true; self.pauseTimer = 0; self.walkingTimer = 0; } // Check if pause duration is complete (0.3 seconds = 18 ticks) if (self.isPaused && self.pauseTimer >= 18) { self.isPaused = false; self.pauseTimer = 0; self.walkingTimer = 0; // Switch direction self.walkingDirection *= -1; } // Apply walking movement when not paused if (!self.isPaused) { // Check if asset 3 is currently visible var asset3Visible = self.currentGraphicIndex === 2; // Asset 3 is at index 2 var targetX; if (asset3Visible) { // When asset 3 is visible, always move left targetX = self.alienHeadBaseX - 5; } else { // Normal walking animation with direction switching targetX = self.alienHeadBaseX + self.walkingDirection * 5; } // Smoothly animate to target position tween(alienHead, { x: targetX }, { duration: 100, easing: tween.linear }); } }; return self; }); // Robot class for wave 3 var Robot = Container.expand(function () { var self = Container.call(this); // Attach robot as the main enemy graphic var robotGraphics = self.attachAsset('Robot', { anchorX: 0.5, anchorY: 0.5 }); // Add wheel to robot var wheel = self.attachAsset('Wheel', { anchorX: 0.5, anchorY: 0.5, x: -25, y: 100, scaleX: 0.8, scaleY: 0.8 }); // Add second wheel to robot var wheel2 = self.attachAsset('wheel2', { anchorX: 0.5, anchorY: 0.5, x: 25, y: 100, scaleX: 0.8, scaleY: 0.8 }); // Add roboarm to robot var roboarm = self.attachAsset('Roboarm', { anchorX: 0.5, anchorY: 0.5, x: -5, y: 0, scaleX: 0.6, scaleY: 0.6 }); // Initialize robot properties self.speed = -6; // Faster speed self.health = 120; // High health self.maxHealth = 120; self.pathLine = 1; self.lastX = undefined; self.electricEffectVisible = false; // Add health bar var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -200 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -200, tint: 0x00ff00 }); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; healthBar.scaleX = healthPercentage; if (healthPercentage > 0.6) { healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { healthBar.tint = 0xffff00; // Yellow } else { healthBar.tint = 0xff0000; // Red } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; } self.updateHealthBar(); }; // Add showElectricEffect function for wave 3 robots self.showElectricEffect = function () { // Wave 3 robots don't have electric effect visuals, so this is a no-op }; // Add laser fire cooldown tracking self.lastLaserFireTime = 0; // Add laser fire back attack for robot when hit self.fireBackAtTower = function (attackingTower) { // Check if tower is on the same line as robot (within 50 pixels tolerance) if (Math.abs(attackingTower.y - self.y) > 50) { return; // Tower not on same line, don't fire } // Check if there are any towers blocking the line of sight to the attacking tower for (var i = 0; i < towers.length; i++) { var blockingTower = towers[i]; if (blockingTower === attackingTower) continue; // Skip the target tower // Check if blocking tower is on the same line (within 50 pixels tolerance) if (Math.abs(blockingTower.y - self.y) > 50) continue; // Check if blocking tower is between robot and attacking tower if (blockingTower.x > self.x && blockingTower.x < attackingTower.x) { return; // Tower is blocked, don't fire } } // Check cooldown - fire every 30 ticks (0.5 seconds) instead of immediately if (LK.ticks - self.lastLaserFireTime < 30) { return; // Still in cooldown } self.lastLaserFireTime = LK.ticks; // Create laser fire back projectile var laser = game.addChild(LK.getAsset('Laserfire', { anchorX: 0.5, anchorY: 0.5, x: self.x - 200, y: self.y, alpha: 0.9, tint: 0x00ff00 // Green laser for robots })); // Calculate direction to attacking tower var deltaX = attackingTower.x - laser.x; var deltaY = attackingTower.y - laser.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var speed = 6; laser.velocityX = deltaX / distance * speed; laser.velocityY = deltaY / distance * speed; laser.damage = 15; // Increased damage from 5 to 15 // Set initial rotation to point at tower var initialAngle = Math.atan2(deltaY, deltaX); laser.rotation = initialAngle; // Animate laser movement var laserInterval = LK.setInterval(function () { laser.x += laser.velocityX; laser.y += laser.velocityY; // Update laser rotation to continuously point at tower var currentDeltaX = attackingTower.x - laser.x; var currentDeltaY = attackingTower.y - laser.y; var currentAngle = Math.atan2(currentDeltaY, currentDeltaX); // Animate rotation change smoothly tween(laser, { rotation: currentAngle }, { duration: 100, easing: tween.easeOut }); // Check if laser hit the attacking tower var laserDistance = Math.sqrt(Math.pow(laser.x - attackingTower.x, 2) + Math.pow(laser.y - attackingTower.y, 2)); if (laserDistance < 100) { // Hit tower - deal damage attackingTower.takeDamage(laser.damage); LK.effects.flashObject(attackingTower, 0xff0000, 300); // Remove laser LK.clearInterval(laserInterval); laser.destroy(); return; } // Check if laser went off screen if (laser.x > 2048 + 100 || laser.x < -100 || laser.y > 2732 + 100 || laser.y < -100) { LK.clearInterval(laserInterval); laser.destroy(); } }, 16); }; self.update = function () { if (self.lastX === undefined) self.lastX = self.x; self.updateHealthBar(); // Rotate wheel slowly anticlockwise if (wheel) { tween(wheel, { rotation: wheel.rotation - Math.PI / 8 }, { duration: 200, easing: tween.linear }); } // Rotate second wheel slowly anticlockwise if (wheel2) { tween(wheel2, { rotation: wheel2.rotation - Math.PI / 8 }, { duration: 200, easing: tween.linear }); } }; return self; }); // SpaceDrone class for wave 4 var SpaceDrone = Container.expand(function () { var self = Container.call(this); // Attach SpaceDrone as the main enemy graphic var spaceDroneGraphics = self.attachAsset('SpaceDrone', { anchorX: 0.5, anchorY: 0.5 }); // Add flashing lights to space drone var droneLight1 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: -40, y: 10, scaleX: 0.03, scaleY: 0.03, tint: 0xff0000, alpha: 0.7 }); var droneLight2 = self.attachAsset('squareLight', { anchorX: 0.5, anchorY: 0.5, x: 50, y: 5, scaleX: 0.8, scaleY: 0.8, tint: 0x00ff00, alpha: 0.7 }); var droneLight3 = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -80, scaleX: 0.025, scaleY: 0.025, tint: 0x0000ff, alpha: 0.7 }); // Start flashing animations for space drone lights function startSpaceDroneLightFlashing() { // Red light flashing tween(droneLight1, { alpha: 0.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(droneLight1, { alpha: 0.8 }, { duration: 500, easing: tween.easeInOut, onFinish: startSpaceDroneLightFlashing }); } }); // Green light flashing (offset timing) LK.setTimeout(function () { tween(droneLight2, { alpha: 0.2 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(droneLight2, { alpha: 0.8 }, { duration: 400, easing: tween.easeInOut }); } }); }, 150); // Blue light flashing (different timing) LK.setTimeout(function () { tween(droneLight3, { alpha: 0.3 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { tween(droneLight3, { alpha: 0.8 }, { duration: 700, easing: tween.easeInOut }); } }); }, 300); } // Start the flashing animation startSpaceDroneLightFlashing(); // Initialize space drone properties self.speed = -4; // Increased speed from -3 to -4 (faster) self.health = 150; // Higher health than previous enemies self.maxHealth = 150; self.pathLine = 1; self.lastX = undefined; self.electricEffectVisible = false; // Add rotation properties self.rotationTimer = 0; self.isRotating = false; self.rotationStartTime = 0; // Add number text self.numberText = new Text2('1', { size: 60, fill: 0x000000 }); self.numberText.anchor.set(0.5, 0.5); self.numberText.x = 0; self.numberText.y = 0; self.addChild(self.numberText); // Add longer health bar for SpaceDrone var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -130, scaleX: 1.5, // Make health bar longer scaleY: 1.2 // Make health bar slightly taller }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -130, tint: 0x00ff00, scaleX: 1.5, // Make health bar longer scaleY: 1.2 // Make health bar slightly taller }); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; healthBar.scaleX = healthPercentage * 1.5; // Maintain longer scale // Change color based on health if (healthPercentage > 0.6) { healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { healthBar.tint = 0xffff00; // Yellow } else { healthBar.tint = 0xff0000; // Red } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; } self.updateHealthBar(); }; // Function to make electric effect visible self.showElectricEffect = function () { // SpaceDrones don't have electric effect visuals like probedroids // This is implemented for consistency with tower interactions }; // Add laser fire cooldown tracking self.lastLaserFireTime = 0; // Add laser fire back attack for SpaceDrone when hit self.fireBackAtTower = function (attackingTower) { // Check if tower is on the same line as SpaceDrone (within 50 pixels tolerance) if (Math.abs(attackingTower.y - self.y) > 50) { return; // Tower not on same line, don't fire } // Check if there are any towers blocking the line of sight to the attacking tower for (var i = 0; i < towers.length; i++) { var blockingTower = towers[i]; if (blockingTower === attackingTower) continue; // Skip the target tower // Check if blocking tower is on the same line (within 50 pixels tolerance) if (Math.abs(blockingTower.y - self.y) > 50) continue; // Check if blocking tower is between SpaceDrone and attacking tower if (blockingTower.x > self.x && blockingTower.x < attackingTower.x) { return; // Tower is blocked, don't fire } } // Check cooldown - fire every 30 ticks (0.5 seconds) instead of immediately if (LK.ticks - self.lastLaserFireTime < 30) { return; // Still in cooldown } self.lastLaserFireTime = LK.ticks; // Create laser fire back projectile var laser = game.addChild(LK.getAsset('Laserfire', { anchorX: 0.5, anchorY: 0.5, x: self.x - 200, y: self.y, alpha: 0.9, tint: 0x0000ff // Blue laser for SpaceDrones })); // Calculate direction to attacking tower var deltaX = attackingTower.x - laser.x; var deltaY = attackingTower.y - laser.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var speed = 7; // Slightly faster than other enemies laser.velocityX = deltaX / distance * speed; laser.velocityY = deltaY / distance * speed; laser.damage = 20; // Increased damage from 8 to 20 // Set initial rotation to point at tower var initialAngle = Math.atan2(deltaY, deltaX); laser.rotation = initialAngle; // Animate laser movement var laserInterval = LK.setInterval(function () { laser.x += laser.velocityX; laser.y += laser.velocityY; // Update laser rotation to continuously point at tower var currentDeltaX = attackingTower.x - laser.x; var currentDeltaY = attackingTower.y - laser.y; var currentAngle = Math.atan2(currentDeltaY, currentDeltaX); // Animate rotation change smoothly tween(laser, { rotation: currentAngle }, { duration: 100, easing: tween.easeOut }); // Check if laser hit the attacking tower var laserDistance = Math.sqrt(Math.pow(laser.x - attackingTower.x, 2) + Math.pow(laser.y - attackingTower.y, 2)); if (laserDistance < 100) { // Hit tower - deal damage attackingTower.takeDamage(laser.damage); LK.effects.flashObject(attackingTower, 0xff0000, 300); // Remove laser LK.clearInterval(laserInterval); laser.destroy(); return; } // Check if laser went off screen if (laser.x > 2048 + 100 || laser.x < -100 || laser.y > 2732 + 100 || laser.y < -100) { LK.clearInterval(laserInterval); laser.destroy(); } }, 16); }; self.update = function () { // Initialize lastX if not set if (self.lastX === undefined) self.lastX = self.x; // Update health bar self.updateHealthBar(); // Handle rotation every 3 seconds self.rotationTimer += gameSpeed; if (self.rotationTimer >= 180 && !self.isRotating) { // 3 seconds at 60fps self.isRotating = true; self.rotationStartTime = LK.ticks; self.rotationTimer = 0; // Start 180 degree rotation tween(spaceDroneGraphics, { rotation: spaceDroneGraphics.rotation + Math.PI }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { self.isRotating = false; } }); } }; return self; }); // UFO boss class for wave 5 var UFO = Container.expand(function () { var self = Container.call(this); // Attach UFO as the main boss graphic var ufoGraphics = self.attachAsset('UFO', { anchorX: 0.5, anchorY: 0.5 }); // Add blowing asset (initially invisible) var blowingEffect = self.attachAsset('Blowing', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 100, scaleX: 0.8, scaleY: 0.8, alpha: 0.8, visible: false }); // Store blowing effect reference self.blowingEffect = blowingEffect; // Initialize UFO boss properties self.speed = -1; // Slow movement speed self.health = 1200; // High health - takes around 120 hits to kill self.maxHealth = 1200; self.pathLine = 1; self.lastX = undefined; self.electricEffectVisible = false; // UFO specific properties self.hoverTarget = null; // Currently targeted tower self.isHovering = false; // Is UFO hovering over a tower self.hoverStartTime = 0; // When hovering started self.pauseTimer = 0; // Timer for initial pause self.hasPaused = false; // Has UFO completed initial pause self.abductionTimer = 0; // Timer for tower abduction self.isAbducting = false; // Is UFO currently abducting a tower self.allTowersDefeatedTime = null; // Timer for 3-second wait after defeating all towers // Add number text self.numberText = new Text2('', { size: 80, fill: 0xff0000 }); self.numberText.anchor.set(0.5, 0.5); self.numberText.x = 0; self.numberText.y = 0; self.addChild(self.numberText); // Add much longer health bar for UFO boss var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -200, scaleX: 4.0, // Make health bar much longer scaleY: 2.0 // Make health bar taller }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -200, tint: 0xff0000, // Red health bar for boss scaleX: 4.0, // Make health bar much longer scaleY: 2.0 // Make health bar taller }); self.updateHealthBar = function () { var healthPercentage = self.health / self.maxHealth; healthBar.scaleX = healthPercentage * 4.0; // Maintain longer scale // Change color based on health if (healthPercentage > 0.6) { healthBar.tint = 0xff0000; // Red } else if (healthPercentage > 0.3) { healthBar.tint = 0xff8800; // Orange } else { healthBar.tint = 0xff4400; // Dark orange } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; } self.updateHealthBar(); }; // Function to select random tower to hover over self.selectRandomTower = function () { // Filter for towers with health > 0 var validTowers = []; for (var i = 0; i < towers.length; i++) { if (towers[i].health > 0) { validTowers.push(towers[i]); } } if (validTowers.length === 0) return null; var randomIndex = Math.floor(Math.random() * validTowers.length); return validTowers[randomIndex]; }; // Function to start tower abduction self.startAbduction = function (tower) { if (!tower || tower.health <= 0) return; self.isAbducting = true; self.abductionTimer = 0; self.blowingEffect.visible = true; // Position UFO on the same row as the tower for abduction self.pathLine = tower.pathLine || 1; // Use tower's line or default to line 1 // Position blowing effect over the tower self.blowingEffect.x = 0; // Relative to UFO position self.blowingEffect.y = 150; // Below UFO // Make blowing effect more transparent for better tower visibility self.blowingEffect.alpha = 0.3; // Ensure blowing effect renders in front of towers by re-adding to game game.removeChild(self.blowingEffect); game.addChild(self.blowingEffect); // Update blowing effect position relative to UFO during abduction var updateBlowingPosition = function updateBlowingPosition() { if (self.blowingEffect.visible && self.isAbducting) { self.blowingEffect.x = self.x; self.blowingEffect.y = self.y + 150; } }; // Start position update interval var positionInterval = LK.setInterval(updateBlowingPosition, 16); // Add pulsing effect to blowing asset to enhance tractor beam look var _pulseBlowingEffect = function pulseBlowingEffect() { if (self.blowingEffect.visible && self.isAbducting) { tween(self.blowingEffect, { alpha: 0.15, scaleX: 0.9, scaleY: 0.9 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { if (self.blowingEffect.visible && self.isAbducting) { tween(self.blowingEffect, { alpha: 0.3, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, easing: tween.easeInOut, onFinish: _pulseBlowingEffect }); } } }); } }; // Start pulsing effect _pulseBlowingEffect(); // Animate tower being sucked up into UFO tween(tower, { y: tower.y - 200, scaleX: 0.5, scaleY: 0.5, alpha: 0.3 }, { duration: 2000, easing: tween.easeIn, onFinish: function onFinish() { // Clear position update interval LK.clearInterval(positionInterval); // Remove tower completely after abduction // Remove health bar components if (tower.healthBarOutline) { tower.healthBarOutline.destroy(); } if (tower.healthBar) { tower.healthBar.destroy(); } // Remove blades if this is a fan tower if (tower.blades) { tower.blades.destroy(); } // Remove plasma if this is a plasma tower if (tower.plasma) { tower.plasma.destroy(); } // Remove tower from game and towers array tower.destroy(); for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === tower) { towers.splice(i, 1); break; } } // Hide blowing effect after abduction self.blowingEffect.visible = false; self.isAbducting = false; self.isHovering = false; self.hoverTarget = null; // Resume UFO movement after abduction is complete if (towers.length === 0) { self.speed = -1; // Resume movement toward goal if no towers left } } }); }; // Function to make electric effect visible self.showElectricEffect = function () { // UFOs don't have electric effect visuals like other enemies // This is implemented for consistency with tower interactions }; self.update = function () { // Initialize lastX if not set if (self.lastX === undefined) self.lastX = self.x; // Update health bar self.updateHealthBar(); // Get count of valid towers (towers with health > 0) var validTowerCount = 0; for (var i = 0; i < towers.length; i++) { if (towers[i].health > 0) { validTowerCount++; } } // Stop UFO movement when abducting towers or hovering if (self.isAbducting || self.isHovering) { self.speed = 0; // Stop horizontal movement during abduction/hovering } // Check if UFO has reached the goal (left side of screen) if (self.lastX >= 0 && self.x < 0) { // UFO reached goal - check if all valid towers have been abducted if (validTowerCount > 0) { // There are still valid towers, UFO cannot win yet // Push UFO back to right side and continue abducting self.x = 200; // Move UFO back to a position where it can continue // Reset UFO state to continue abduction process self.isHovering = false; self.isAbducting = false; self.hoverTarget = null; self.speed = 0; // Stop movement until new target selected return; } else { // All valid towers abducted - check if 3-second wait period has completed if (!self.allTowersDefeatedTime) { // First time all towers are defeated, start the 3-second timer self.allTowersDefeatedTime = LK.ticks; // Push UFO back to prevent immediate victory self.x = 200; self.speed = 0; // Stop movement during wait period return; } // Check if 3 seconds have passed since all towers were defeated var waitTime = 180; // 3 seconds at 60fps if (LK.ticks - self.allTowersDefeatedTime < waitTime) { // Still waiting, push UFO back self.x = 200; self.speed = 0; // Keep UFO stationary during wait return; } // 3 seconds have passed, UFO wins // Track wave completion for UFO boss if (currentWave === 5) { // UFO has successfully abducted all towers and reached the goal // Player loses - show game over LK.showGameOver(); return; } } } // UFO behavior: hover down from above, pause, then select towers to abduct if (!self.hasPaused) { // Initial pause for 2 seconds after appearing self.pauseTimer += gameSpeed; if (self.pauseTimer >= 120) { // 2 seconds at 60fps self.hasPaused = true; } return; // Don't do anything else during initial pause } // Main UFO logic - only proceed if not currently busy if (!self.isHovering && !self.isAbducting) { // Check if current target is still valid if (self.hoverTarget && self.hoverTarget.health <= 0) { self.hoverTarget = null; // Clear invalid target } // Select new target if needed and valid towers exist if (!self.hoverTarget && validTowerCount > 0) { self.hoverTarget = self.selectRandomTower(); } // If we have a valid target, start hovering if (self.hoverTarget && self.hoverTarget.health > 0) { self.isHovering = true; self.hoverStartTime = LK.ticks; // Set UFO's path line to match the tower's row self.pathLine = self.hoverTarget.pathLine || 1; // Calculate the row Y position based on tower's position var targetY = self.hoverTarget.y - 300; // Stop UFO horizontal movement during positioning self.speed = 0; // Move UFO to hover over the selected tower on the same row tween(self, { x: self.hoverTarget.x, y: targetY }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Verify target is still valid before starting abduction if (self.hoverTarget && self.hoverTarget.health > 0) { self.startAbduction(self.hoverTarget); } else { // Target became invalid, reset state self.isHovering = false; self.hoverTarget = null; } } }); // Update UFO's position immediately to enable tower attacks during movement self.y = targetY; } else if (validTowerCount === 0) { // No valid towers remaining, move toward goal if (!self.allTowersDefeatedTime) { self.allTowersDefeatedTime = LK.ticks; } // Only start moving toward goal after 3-second wait var waitTime = 180; // 3 seconds at 60fps if (LK.ticks - self.allTowersDefeatedTime >= waitTime) { self.speed = -1; // Resume movement toward goal } else { self.speed = 0; // Wait before moving to goal } } } // Update last position self.lastX = self.x; // Handle UFO rendering order - render in front of towers above it, behind towers below it // Sort all display objects (towers and UFO) by Y position for proper depth ordering var allTowersAndUFO = towers.slice(); // Copy towers array allTowersAndUFO.push(self); // Add UFO to the list // Sort by Y position (lower Y values should be behind higher Y values) allTowersAndUFO.sort(function (a, b) { return a.y - b.y; }); // Re-add all objects to game in sorted order to ensure proper rendering depth for (var i = 0; i < allTowersAndUFO.length; i++) { var obj = allTowersAndUFO[i]; if (obj.parent) { game.removeChild(obj); // Find the first wave asset in children to insert before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert before wave asset game.addChildAt(obj, waveAssetIndex); } else { // No wave asset found, add normally game.addChild(obj); } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 //Init game with black background }); /**** * Game Code ****/ // Cutscene update loop - handles story sequences function updateCutscene() { // Handle cutscene-specific logic here // Currently cutscenes are handled by their own tween animations // This function exists for future cutscene-specific updates } // Gameplay update loop - handles tower defense mechanics function updateGameplay() { // Spawn enemies enemySpawnTimer += gameSpeed; if (enemySpawnTimer >= enemySpawnInterval) { spawnEnemy(); enemySpawnTimer = 0; } // Update fan tower cooldowns for (var i = 0; i < towers.length; i++) { var tower = towers[i]; // Check if this is a fan tower if (tower.blades) { tower.cooldownTimer += gameSpeed; // Check if cooldown should start if (tower.isRunning && tower.cooldownTimer >= tower.cooldownDuration) { // Start cooldown - stop fan tower.cooldownActive = true; tower.isRunning = false; tower.cooldownTimer = 0; // Stop blade spinning if (tower.blades) { tween.stop(tower.blades); } // Flash fan red to indicate cooldown LK.effects.flashObject(tower, 0xff0000, 1000); } // Check if cooldown should end (10 seconds cooldown) if (tower.cooldownActive && tower.cooldownTimer >= 10 * 60 / gameSpeed) { // End cooldown - restart fan tower.cooldownActive = false; tower.isRunning = true; tower.cooldownTimer = 0; // Resume blade spinning var _spinBlades2 = function spinBlades() { if (tower.blades && tower.isRunning) { tween(tower.blades, { rotation: tower.blades.rotation + Math.PI * 2 }, { duration: 500, easing: tween.linear, onFinish: _spinBlades2 }); } }; _spinBlades2(); // Flash fan green to indicate restart LK.effects.flashObject(tower, 0x00ff00, 500); } } } // Handle plasma tower attacks for (var i = 0; i < towers.length; i++) { var tower = towers[i]; if (tower._assetId === 'Plasmaball' && tower.health > 0) { handlePlasmaAttack(tower); } } // Update enemies updateEnemies(); // Consolidate all cleanup operations into single function that runs less frequently if (LK.ticks % 120 === 0) { // Run cleanup every 2 seconds instead of multiple times per second performConsolidatedCleanup(); } } // Main game update loop - routes to appropriate update method game.update = function () { if (!gameStarted) return; // Check if we're in a cutscene by looking for active story assets var inCutscene = false; for (var i = 0; i < game.children.length; i++) { var child = game.children[i]; if (child.assetId === 'Story1' || child.assetId === 'intro' || child.assetId === 'Title') { inCutscene = true; break; } } // Set gameplay state gameplayActive = !inCutscene; // Route to appropriate update method if (gameplayActive) { updateGameplay(); } else { updateCutscene(); } // Check wave 1 completion - different for level 1 vs level 2 if (currentWave === 1 && (!window.isLevel2 && wave1EnemiesCompleted >= 10 || window.isLevel2 && wave1EnemiesCompleted >= 40)) { // Award player $50 for completing wave 1 playerCash += 50; cashText.setText('Cash: $' + playerCash); // Initialize wave 2 currentWave = 2; enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; // Reset defeated counter for new wave // Level 2 wave 2 has 80 enemies, level 1 wave 2 has 30 drones maxEnemiesInWave = window.isLevel2 ? 80 : 30; enemySpawnInterval = 180; // 3 seconds between spawns at 60fps // Fade in wave2 asset in middle of screen var wave2Asset = game.addChild(LK.getAsset('Wave2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave2 asset tween(wave2Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave2 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave2Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave2Asset.destroy(); } }); }, 2000); } }); } // Check wave 2 completion and initialize wave 3 - different for level 1 vs level 2 if (currentWave === 2 && (!window.isLevel2 && wave2EnemiesCompleted >= 30 || window.isLevel2 && wave2EnemiesCompleted >= 80)) { // Award player $50 for completing wave 2 playerCash += 50; cashText.setText('Cash: $' + playerCash); // Initialize wave 3 currentWave = 3; enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; // Reset defeated counter for new wave maxEnemiesInWave = 20; // Wave 3 has 20 robots enemySpawnInterval = 180; // 3 seconds between spawns at 60fps // Fade in wave3 asset in middle of screen var wave3Asset = game.addChild(LK.getAsset('Wave3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave3 asset tween(wave3Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave3 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave3Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave3Asset.destroy(); } }); }, 2000); } }); } // Check wave 3 completion and initialize wave 4 if (currentWave === 3 && enemiesSpawned >= maxEnemiesInWave && enemies.length === 0) { // Award player $50 for completing wave 3 playerCash += 50; cashText.setText('Cash: $' + playerCash); // Initialize wave 4 currentWave = 4; enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; // Reset defeated counter for new wave maxEnemiesInWave = 50; // Wave 4 has 50 SpaceDrones enemySpawnInterval = 120; // 2 seconds between spawns at 60fps // Fade in wave4 asset in middle of screen var wave4Asset = game.addChild(LK.getAsset('Wave4', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave4 asset tween(wave4Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave4 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave4Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave4Asset.destroy(); } }); }, 2000); } }); } // Check wave 4 completion and initialize wave 5 if (currentWave === 4 && enemiesSpawned >= maxEnemiesInWave && enemies.length === 0) { // Award player $200 for completing wave 4 playerCash += 200; cashText.setText('Cash: $' + playerCash); // Initialize wave 5 currentWave = 5; enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; // Reset defeated counter for new wave maxEnemiesInWave = 1; // Wave 5 has 1 UFO boss enemySpawnInterval = 300; // 5 seconds before boss appears // Boss level - player only has 1 life playerLives = 1; if (livesText) { livesText.setText('Lives: ' + playerLives); } // Fade in wave5 asset in middle of screen var wave5Asset = game.addChild(LK.getAsset('Wave5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave5 asset tween(wave5Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave5 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave5Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave5Asset.destroy(); } }); }, 2000); } }); } // Check wave 5 completion (final win condition) - only for level 1 if (currentWave === 5 && !window.isLevel2 && enemiesSpawned >= maxEnemiesInWave && enemies.length === 0) { // Award player $500 for completing wave 5 playerCash += 500; cashText.setText('Cash: $' + playerCash); // Restore all 3 lives playerLives = 3; if (livesText) { livesText.setText('Lives: ' + playerLives); } // End of level 1: Start level 2 with a cutscene (story1 scene) // Set cutscene mode for Story1 transition gameplayActive = false; // Fade in Story1 asset for level 2 transition var story1Asset = game.addChild(LK.getAsset('Story1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); tween(story1Asset, { alpha: 1 }, { duration: 1200, easing: tween.easeInOut, onFinish: function onFinish() { // Function to proceed to level 2 var proceedToLevel2 = function proceedToLevel2() { // Initialize level 2 variables here currentWave = 1; // Reset to wave 1 for level 2 enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; maxEnemiesInWave = 40; // Level 2 wave 1 has 40 enemies enemySpawnInterval = 900; // Start with 15 seconds (900 ticks at 60fps) wave1EnemiesCompleted = 0; wave2EnemiesCompleted = 0; wave3EnemiesCompleted = 0; // Set level 2 flag window.isLevel2 = true; // Fade out story asset and continue level 2 tween(story1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story1Asset.destroy(); // Restore gameplay state after cutscene gameplayActive = true; // Start level 2 wave 1 with wave asset animation var wave1Asset = game.addChild(LK.getAsset('Wave1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave1 asset tween(wave1Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave1 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave1Asset.destroy(); // Level 2 is now ready to continue } }); }, 2000); } }); } }); }; // Add click handler to story1Asset to dismiss it manually story1Asset.down = function (x, y, obj) { proceedToLevel2(); }; // Add automatic timeout after 5 seconds to prevent getting stuck LK.setTimeout(function () { if (story1Asset && story1Asset.parent) { proceedToLevel2(); } }, 5000); } }); // Set flag to track level 2 start var isLevel2 = true; } // Final check - destroy all towers with 0 or less health at end of update cycle for (var i = towers.length - 1; i >= 0; i--) { var tower = towers[i]; if (tower.health <= 0) { // Remove health bar components if (tower.healthBarOutline) { tower.healthBarOutline.destroy(); } if (tower.healthBar) { tower.healthBar.destroy(); } // Remove blades if this is a fan tower if (tower.blades) { tower.blades.destroy(); } // Remove plasma if this is a plasma tower if (tower.plasma) { tower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (tower.plasmaRays) { for (var rayIndex = 0; rayIndex < tower.plasmaRays.length; rayIndex++) { if (tower.plasmaRays[rayIndex] && tower.plasmaRays[rayIndex].parent) { tower.plasmaRays[rayIndex].destroy(); } } tower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (tower.activeZaps) { for (var zapIndex = 0; zapIndex < tower.activeZaps.length; zapIndex++) { tower.activeZaps[zapIndex].destroy(); } tower.activeZaps = []; tower.zapTargets = []; } // Clean up fireball animations if this is a fire tower if (tower._assetId === 'Fireworks') { // Find and stop all active fireballs launched by this tower for (var fireballIndex = game.children.length - 1; fireballIndex >= 0; fireballIndex--) { var child = game.children[fireballIndex]; if (child.assetId === 'Fireball') { // Stop all tween animations on this fireball tween.stop(child); // Destroy the fireball child.destroy(); } } } // Remove tower from the game tower.destroy(); towers.splice(i, 1); } } }; // Mouse move handler for tower preview game.move = function (x, y, obj) { if (isPlacingTower && towerPreview) { // Snap preview to nearest guideline X position (columns) var snappedX = getNearestGuidelineX(x); towerPreview.x = snappedX; // Snap preview to nearest guideline Y position var snappedY = getNearestGuidelineY(y); towerPreview.y = snappedY; // Update blades position if fan preview has blades if (towerPreview.blades) { towerPreview.blades.x = towerPreview.x + 50; towerPreview.blades.y = towerPreview.y - 40 - 100 + 25 + 25; } // Update plasma position if plasma preview has plasma if (towerPreview.plasma) { towerPreview.plasma.x = towerPreview.x; towerPreview.plasma.y = towerPreview.y - 20; } } }; var enemies = []; var towers = []; var waterSquirts = []; // Track all water squirts for cleanup var gasClouds = []; // Track all gas clouds for cleanup // Consolidated cleanup function for all visual effects function performConsolidatedCleanup() { // Clean up gas clouds for (var i = gasClouds.length - 1; i >= 0; i--) { var gasCloud = gasClouds[i]; if (!gasCloud || !gasCloud.parent || gasCloud.createdTime && LK.ticks - gasCloud.createdTime > 180) { if (gasCloud && gasCloud.parent) gasCloud.destroy(); gasClouds.splice(i, 1); } } // Clean up water squirts - limit to max 15 simultaneous for (var i = waterSquirts.length - 1; i >= 0; i--) { var squirt = waterSquirts[i]; if (!squirt || !squirt.parent || squirt.createdTime && LK.ticks - squirt.createdTime > 300 || waterSquirts.length > 15) { if (squirt && squirt.parent) squirt.destroy(); waterSquirts.splice(i, 1); } } // Clean up plasma rays - limit to max 50 simultaneous var plasmaRayCount = 0; for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Plasmaray1') { plasmaRayCount++; if (!child.createdTime) child.createdTime = LK.ticks; if (LK.ticks - child.createdTime > 300 || plasmaRayCount > 50) { child.destroy(); } } } // Clean up fireballs for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Fireball') { if (!child.createdTime) child.createdTime = LK.ticks; if (child.x > 2248 || child.x < -200 || child.y > 2932 || child.y < -200 || LK.ticks - child.createdTime > 300) { tween.stop(child); child.destroy(); } } } // Clean up orphaned blades and zap effects for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Blades' || child.assetId === 'Zap3') { var hasOwner = false; for (var j = 0; j < towers.length; j++) { if (towers[j].blades === child || towers[j].activeZaps && towers[j].activeZaps.indexOf(child) !== -1) { hasOwner = true; break; } } if (!hasOwner) child.destroy(); } } } // Function to clean up gas clouds that have exceeded their lifetime function cleanupGasClouds() { // Moved to consolidated cleanup for better performance } var gameStarted = false; var enemySpawnTimer = 0; var enemySpawnInterval = 600; // Spawn every 10 seconds at 60fps var enemiesSpawned = 0; var maxEnemiesInWave = 10; // Wave 1 has exactly 10 probedroids var currentWave = 1; // Track current wave number var wave1EnemiesCompleted = 0; // Track completed enemies in wave 1 var wave2EnemiesCompleted = 0; // Track completed enemies in wave 2 var wave3EnemiesCompleted = 0; // Track completed enemies in wave 3 var enemiesDefeated = 0; // Track enemies actually defeated by player (not reached goal) var playerCash = 50; // Player starts with $50 var playerLives = 3; // Player starts with 3 lives var playerScore = 0; // Player starts with 0 score var livesText = null; // Global reference to lives text display var isPlacingTower = false; var selectedTowerType = null; var towerPreview = null; var selectedTower = null; // Track currently selected tower for deletion var deleteButton = null; // Red X button for tower deletion var guidelines = []; var verticalGuidelines = []; var cashText = null; var confirmButton = null; var gameSpeed = 1; // Global game speed multiplier var gameplayActive = true; // Track if we're in active gameplay vs cutscene // Define path positions for different lines var pathPositions = { line6: 2732 / 7 * 6 - 310 + 50, // Line 6 position line2: 2732 / 7 * 2 + 320 + 50, // Line 2 position line3: 2732 / 7 * 3 + 150 + 50, // Line 3 position line4: 2732 / 7 * 4 + 10 + 50, // Line 4 position line5: 2732 / 7 * 5 - 150 + 50, // Line 5 position line1: 2732 / 7 * 1 + 480 + 50 // Line 1 position }; // Helper function to get nearest guideline Y position function getNearestGuidelineY(y) { var nearestY = y; var minDistance = Infinity; for (var i = 0; i < guidelines.length; i++) { var guideline = guidelines[i]; var distance = Math.abs(guideline.y - y); if (distance < minDistance) { minDistance = distance; nearestY = guideline.y; } } return nearestY; } // Helper function to get nearest vertical guideline X position function getNearestGuidelineX(x) { var nearestX = x; var minDistance = Infinity; for (var i = 0; i < verticalGuidelines.length; i++) { var guideline = verticalGuidelines[i]; var distance = Math.abs(guideline.x - x); if (distance < minDistance) { minDistance = distance; nearestX = guideline.x; } } return nearestX; } // Clean up tower placement mode function cleanupPlacementMode() { isPlacingTower = false; selectedTowerType = null; // Clear tower selection when entering placement mode if (selectedTower) { selectedTower.alpha = 1.0; selectedTower = null; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Destroy preview blades if they exist - check both tower reference and global tracking if (towerPreview && towerPreview.blades) { towerPreview.blades.destroy(); towerPreview.blades = null; } // Also check for orphaned blade previews in game children for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Blades' && child.alpha === 0.5) { // This is likely a preview blade that got orphaned child.destroy(); } } // Destroy preview plasma if it exists if (towerPreview && towerPreview.plasma) { towerPreview.plasma.destroy(); towerPreview.plasma = null; } // Clean up any remaining water squirts for (var waterIndex = game.children.length - 1; waterIndex >= 0; waterIndex--) { var child = game.children[waterIndex]; if (child.assetId === 'Water') { child.destroy(); } } if (towerPreview) { towerPreview.destroy(); towerPreview = null; } if (confirmButton) { confirmButton.destroy(); confirmButton = null; } } // Helper function to check if tower position is too close to existing towers function canPlaceTowerAt(x, y) { var minDistance = 200; // Minimum distance between towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var distance = Math.sqrt(Math.pow(x - tower.x, 2) + Math.pow(y - tower.y, 2)); if (distance < minDistance) { return false; } } return true; } // Function to clean up water squirts when tower is not attacking function cleanupWaterSquirts() { // Only run cleanup every 30 ticks to reduce performance impact if (LK.ticks % 30 !== 0) return; for (var i = waterSquirts.length - 1; i >= 0; i--) { var squirt = waterSquirts[i]; if (!squirt || !squirt.parent) { // Water squirt already destroyed, remove from tracking waterSquirts.splice(i, 1); continue; } // Check if water squirt has exceeded maximum lifetime (5 seconds) if (!squirt.createdTime) { squirt.createdTime = LK.ticks; } if (LK.ticks - squirt.createdTime > 300) { // 5 seconds at 60fps if (squirt.parent) { squirt.destroy(); } waterSquirts.splice(i, 1); continue; } // Check if water squirt's tower is still active and attacking var towerStillActive = false; for (var j = 0; j < towers.length; j++) { var tower = towers[j]; if (tower._assetId === 'Tower1' && tower.health > 0) { // Check if tower has enemies in range var hasEnemiesInRange = false; for (var k = 0; k < enemies.length; k++) { var enemy = enemies[k]; if (Math.abs(enemy.y - tower.y) < 50 && enemy.x > tower.x && enemy.x - tower.x < 700) { hasEnemiesInRange = true; break; } } if (hasEnemiesInRange) { towerStillActive = true; break; } } } // If no towers are actively attacking, clean up this water squirt if (!towerStillActive) { if (squirt.parent) { squirt.destroy(); } waterSquirts.splice(i, 1); } } } // Function to clean up all zap3 effects when electric towers are destroyed or not targeting function cleanupZapEffects() { // Only run cleanup every 15 ticks to reduce performance impact if (LK.ticks % 15 !== 0) return; for (var i = towers.length - 1; i >= 0; i--) { var tower = towers[i]; if (tower._assetId === 'Bugzapper' && tower.activeZaps) { // Check if tower is destroyed (health <= 0) if (tower.health <= 0) { // Clean up all zap effects for destroyed tower for (var j = tower.activeZaps.length - 1; j >= 0; j--) { var zap = tower.activeZaps[j]; if (zap && zap.parent) { zap.destroy(); } } // Clear arrays completely tower.activeZaps = []; tower.zapTargets = []; continue; } for (var j = tower.activeZaps.length - 1; j >= 0; j--) { var zap = tower.activeZaps[j]; var target = tower.zapTargets[j]; // Check if zap effect still exists and target is still valid if (!zap || !zap.parent || !target || !target.parent) { // Clean up invalid zap effect if (zap && zap.parent) { zap.destroy(); } tower.activeZaps.splice(j, 1); tower.zapTargets.splice(j, 1); continue; } // Check if target is still in range var targetInRange = false; if (Math.abs(target.y - tower.y) < 100 && target.x > tower.x && target.x - tower.x < 800) { targetInRange = true; } // If target is not in range, clean up zap effect if (!targetInRange) { if (zap.parent) { zap.destroy(); } tower.activeZaps.splice(j, 1); tower.zapTargets.splice(j, 1); } } } } } // Function to clean up all fireball projectiles that are off-screen or have invalid targets function cleanupFireballs() { // Find all fireball assets in the game for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Fireball') { // Check if fireball is off-screen if (child.x > 2048 + 200 || child.x < -200 || child.y > 2732 + 200 || child.y < -200) { tween.stop(child); child.destroy(); continue; } // Check if fireball has been on screen for too long (5 seconds) if (!child.createdTime) { child.createdTime = LK.ticks; } if (LK.ticks - child.createdTime > 300) { // 5 seconds at 60fps tween.stop(child); child.destroy(); } } } } // Function to clean up plasma rays that are orphaned or have exceeded their lifetime function cleanupPlasmaBeams() { // Only run cleanup every 30 ticks to reduce performance impact if (LK.ticks % 30 !== 0) return; // Find all plasma ray assets in the game for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Plasmaray1') { // Check if plasma ray has been on screen for too long (5 seconds - reduced from 10) if (!child.createdTime) { child.createdTime = LK.ticks; } if (LK.ticks - child.createdTime > 300) { // 5 seconds at 60fps (reduced from 10 seconds) child.destroy(); continue; } // Check if the plasma ray is orphaned (no tower owns it or tower is destroyed) var isOrphaned = true; for (var j = 0; j < towers.length; j++) { var tower = towers[j]; // Check if tower has plasma (is a plasma tower) and is alive if (tower.plasma && tower.health > 0 && tower.plasmaRays) { for (var k = 0; k < tower.plasmaRays.length; k++) { if (tower.plasmaRays[k] === child) { isOrphaned = false; break; } } } if (!isOrphaned) break; } // If orphaned, destroy it if (isOrphaned) { child.destroy(); continue; } } } } // Function to clean up blade assets that are orphaned function cleanupBlades() { // Only run cleanup every 30 ticks to reduce performance impact if (LK.ticks % 30 !== 0) return; // Find all blade assets in the game for (var i = game.children.length - 1; i >= 0; i--) { var child = game.children[i]; if (child.assetId === 'Blades') { // Check if the blade is orphaned (no tower owns it) var isOrphaned = true; for (var j = 0; j < towers.length; j++) { var tower = towers[j]; if (tower.blades === child) { isOrphaned = false; break; } } // If orphaned, destroy it if (isOrphaned) { child.destroy(); continue; } } } } // Tower defense functions function startTowerDefenseWave1() { gameStarted = true; enemySpawnTimer = 0; enemiesSpawned = 0; // Add score display to top of screen var scoreText = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.x = 2048 / 2; scoreText.y = 50; game.addChild(scoreText); // Store scoreText reference globally for updating window.scoreText = scoreText; // Add cash display to top of screen cashText = new Text2('Cash: $' + playerCash, { size: 80, fill: 0xFFD700 }); cashText.anchor.set(0.5, 0); cashText.x = 2048 / 2 + 300 + 200; cashText.y = 50; game.addChild(cashText); // Add speed up button under cash display var speedUpButton = new Button(); speedUpButton.setup({ text: '2X SPEED', backgroundColor: 0x00AA00, textColor: 0xFFFFFF, width: 250, height: 80, fontSize: 35 }); speedUpButton.x = 2048 / 2 + 300 + 200; speedUpButton.y = 180; game.addChild(speedUpButton); // Speed up functionality speedUpButton.onClick = function () { if (gameSpeed === 1) { gameSpeed = 2; speedUpButton.setup({ text: '1X SPEED', backgroundColor: 0xFF6600, textColor: 0xFFFFFF, width: 250, height: 80, fontSize: 35 }); } else { gameSpeed = 1; speedUpButton.setup({ text: '2X SPEED', backgroundColor: 0x00AA00, textColor: 0xFFFFFF, width: 250, height: 80, fontSize: 35 }); } }; // Add lives display to top of screen livesText = new Text2('Lives: ' + playerLives, { size: 80, fill: 0xFF4444 }); livesText.anchor.set(0.5, 0); livesText.x = 2048 / 2 - 300 - 200; livesText.y = 50; game.addChild(livesText); // Add skip wave button to top of screen var skipWaveButton = new Text2('SKIP WAVE', { size: 60, fill: 0xFFFFFF }); skipWaveButton.anchor.set(0.5, 0); skipWaveButton.x = 2048 / 2; skipWaveButton.y = 150; game.addChild(skipWaveButton); // Add touch event to skip wave button skipWaveButton.down = function (x, y, obj) { // Skip to next wave immediately if (currentWave === 1) { // Skip to wave 2 // Award player $50 for completing wave 1 playerCash += 50; cashText.setText('Cash: $' + playerCash); // Initialize wave 2 currentWave = 2; enemiesSpawned = 0; enemySpawnTimer = 0; // Level 2 wave 2 has 80 enemies, level 1 wave 2 has 30 drones maxEnemiesInWave = window.isLevel2 ? 80 : 30; enemySpawnInterval = 180; // 3 seconds between spawns at 60fps wave1EnemiesCompleted = window.isLevel2 ? 40 : 10; // Mark wave 1 as completed // Clear existing enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } enemies = []; // Fade in wave2 asset in middle of screen var wave2Asset = game.addChild(LK.getAsset('Wave2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave2 asset tween(wave2Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave2 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave2Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave2Asset.destroy(); } }); }, 2000); } }); } else if (currentWave === 2) { // Skip to wave 3 // Award player $50 for completing wave 2 playerCash += 50; cashText.setText('Cash: $' + playerCash); // Initialize wave 3 currentWave = 3; enemiesSpawned = 0; enemySpawnTimer = 0; maxEnemiesInWave = 20; // Wave 3 has 20 robots enemySpawnInterval = 180; // 3 seconds between spawns at 60fps wave2EnemiesCompleted = window.isLevel2 ? 80 : 30; // Mark wave 2 as completed // Clear existing enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } enemies = []; // Fade in wave3 asset in middle of screen var wave3Asset = game.addChild(LK.getAsset('Wave3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave3 asset tween(wave3Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave3 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave3Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave3Asset.destroy(); } }); }, 2000); } }); } else if (currentWave === 3) { // Skip to wave 4 // Award player $50 for completing wave 3 playerCash += 50; cashText.setText('Cash: $' + playerCash); // Initialize wave 4 currentWave = 4; enemiesSpawned = 0; enemySpawnTimer = 0; maxEnemiesInWave = 50; // Wave 4 has 50 SpaceDrones enemySpawnInterval = 120; // 2 seconds between spawns at 60fps wave3EnemiesCompleted = 20; // Mark wave 3 as completed // Clear existing enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } enemies = []; // Fade in wave4 asset in middle of screen var wave4Asset = game.addChild(LK.getAsset('Wave4', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave4 asset tween(wave4Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave4 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave4Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave4Asset.destroy(); } }); }, 2000); } }); } else if (currentWave === 4) { // Skip to wave 5 // Award player $200 for completing wave 4 playerCash += 200; cashText.setText('Cash: $' + playerCash); // Initialize wave 5 currentWave = 5; enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; // Reset defeated counter for new wave maxEnemiesInWave = 1; // Wave 5 has 1 UFO boss enemySpawnInterval = 300; // 5 seconds before boss appears // Boss level - player only has 1 life playerLives = 1; if (livesText) { livesText.setText('Lives: ' + playerLives); } // Clear existing enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } enemies = []; // Fade in wave5 asset in middle of screen var wave5Asset = game.addChild(LK.getAsset('Wave5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave5 asset tween(wave5Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave5 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave5Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave5Asset.destroy(); } }); }, 2000); } }); } else if (currentWave === 5) { // Skip to level 2 cutscene directly from wave 5 skip // Award player $500 for completing wave 5 playerCash += 500; cashText.setText('Cash: $' + playerCash); // Restore all 3 lives for next level playerLives = 3; if (livesText) { livesText.setText('Lives: ' + playerLives); } // Clear existing enemies for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); } enemies = []; // Set cutscene mode for Story1 transition gameplayActive = false; // Fade in Story1 asset for level 2 transition var story1Asset = game.addChild(LK.getAsset('Story1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); tween(story1Asset, { alpha: 1 }, { duration: 1200, easing: tween.easeInOut, onFinish: function onFinish() { // Function to proceed to level 2 var proceedToLevel2 = function proceedToLevel2() { // Initialize level 2 variables here currentWave = 1; // Reset to wave 1 for level 2 enemiesSpawned = 0; enemySpawnTimer = 0; enemiesDefeated = 0; maxEnemiesInWave = 40; // Level 2 wave 1 has 40 enemies enemySpawnInterval = 900; // Start with 15 seconds wave1EnemiesCompleted = 0; wave2EnemiesCompleted = 0; wave3EnemiesCompleted = 0; // Set level 2 flag window.isLevel2 = true; // Fade out story asset and continue level 2 tween(story1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story1Asset.destroy(); // Restore gameplay state after cutscene gameplayActive = true; // Start level 2 wave 1 with wave asset animation var wave1Asset = game.addChild(LK.getAsset('Wave1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave1 asset tween(wave1Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave1 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave1Asset.destroy(); // Level 2 is now ready to continue } }); }, 2000); } }); } }); }; // Add click handler to story1Asset to dismiss it manually story1Asset.down = function (x, y, obj) { proceedToLevel2(); }; // Add automatic timeout after 5 seconds to prevent getting stuck LK.setTimeout(function () { if (story1Asset && story1Asset.parent) { proceedToLevel2(); } }, 5000); } }); } }; // Add Gasbutton to bottom of game scene var gasButton = game.addChild(LK.getAsset('Gasbutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 426.67 + 50 + 50, y: 2732 - 350 })); // Add Waterbutton to bottom left of game scene var waterButton = game.addChild(LK.getAsset('Waterbutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 50, y: 2732 - 350 })); // Water button click handler waterButton.down = function (x, y, obj) { // Play tower select sound LK.getSound('Towerselect').play(); // Check if player has enough cash if (playerCash >= 10) { // Start tower placement mode isPlacingTower = true; selectedTowerType = 'water'; // Create tower preview if (towerPreview) { towerPreview.destroy(); } towerPreview = game.addChild(LK.getAsset('Tower1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.5 })); // Create green tick button for confirming placement if (confirmButton) { confirmButton.destroy(); } confirmButton = game.addChild(new Button()); confirmButton.setup({ text: '✓', backgroundColor: 0x00FF00, textColor: 0xFFFFFF, width: 120, height: 120, fontSize: 80 }); confirmButton.x = 2048 - 150; confirmButton.y = 2732 / 2; confirmButton.onClick = function () { // Place tower at current preview position if (towerPreview) { // Align tower position to nearest guidelines var alignedX = getNearestGuidelineX(towerPreview.x); var alignedY = getNearestGuidelineY(towerPreview.y); // Check if tower can be placed at this position if (!canPlaceTowerAt(alignedX, alignedY)) { // Flash red to indicate invalid placement LK.effects.flashObject(towerPreview, 0xff0000, 500); return; } var newTower = game.addChild(LK.getAsset('Tower1', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY, alpha: 1 })); newTower._assetId = 'Tower1'; // Mark this as a water tower newTower.health = 100; newTower.maxHealth = 100; newTower.isBeingDamaged = false; newTower.lastDamageTime = 0; newTower.lastFireTime = 0; // Initialize fire rate tracking // Determine which path line this tower is on based on its Y position for (var lineKey in pathPositions) { if (Math.abs(alignedY - pathPositions[lineKey]) < 100) { newTower.pathLine = parseInt(lineKey.replace('line', '')); break; } } if (!newTower.pathLine) newTower.pathLine = 1; // Default to line 1 // Add health bar to tower var healthBarOutline = game.addChild(LK.getAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY - 120 })); var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY - 120, tint: 0x00ff00 })); newTower.healthBarOutline = healthBarOutline; newTower.healthBar = healthBar; newTower.takeDamage = function (damage) { newTower.health -= damage; if (newTower.health <= 0) { newTower.health = 0; } // Update health bar var healthPercentage = newTower.health / newTower.maxHealth; newTower.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { newTower.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { newTower.healthBar.tint = 0xffff00; // Yellow } else { newTower.healthBar.tint = 0xff0000; // Red } }; // Add tower selection functionality newTower.down = function (x, y, obj) { // Only allow selection if not placing a tower if (!isPlacingTower) { // Clear previous selection if (selectedTower) { selectedTower.alpha = 1.0; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Select this tower selectedTower = newTower; selectedTower.alpha = 0.7; // Dim selected tower // Create red X delete button deleteButton = game.addChild(new Button()); deleteButton.setup({ text: '✖', backgroundColor: 0xFF0000, textColor: 0xFFFFFF, width: 80, height: 80, fontSize: 50 }); deleteButton.x = selectedTower.x + 120; deleteButton.y = selectedTower.y - 80; deleteButton.onClick = function () { // Delete the selected tower if (selectedTower) { // Remove health bar components if (selectedTower.healthBarOutline) { selectedTower.healthBarOutline.destroy(); } if (selectedTower.healthBar) { selectedTower.healthBar.destroy(); } // Remove blades if this is a fan tower if (selectedTower.blades) { selectedTower.blades.destroy(); } // Remove plasma if this is a plasma tower if (selectedTower.plasma) { selectedTower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (selectedTower.plasmaRays) { for (var rayIndex = 0; rayIndex < selectedTower.plasmaRays.length; rayIndex++) { if (selectedTower.plasmaRays[rayIndex] && selectedTower.plasmaRays[rayIndex].parent) { selectedTower.plasmaRays[rayIndex].destroy(); } } selectedTower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (selectedTower.activeZaps) { for (var zapIndex = 0; zapIndex < selectedTower.activeZaps.length; zapIndex++) { selectedTower.activeZaps[zapIndex].destroy(); } selectedTower.activeZaps = []; selectedTower.zapTargets = []; } // Remove tower from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === selectedTower) { towers.splice(i, 1); break; } } // Destroy the tower selectedTower.destroy(); selectedTower = null; // Remove delete button deleteButton.destroy(); deleteButton = null; } }; } }; towers.push(newTower); // Check for enemies in range immediately after placing for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) if (Math.abs(enemy.y - newTower.y) < 50) { // Check if enemy is to the right of the tower and within range of 700 pixels if (enemy.x > newTower.x && enemy.x - newTower.x < 700) { // Fire immediately newTower.lastFireTime = LK.ticks; // Create water squirt visual var waterSquirt = game.addChild(LK.getAsset('Water', { anchorX: 0, anchorY: 0.5, x: newTower.x, y: newTower.y, scaleX: 0.3, scaleY: 0.1, alpha: 0.7, tint: 0x4169E1 })); // Track creation time and add to cleanup tracking waterSquirt.createdTime = LK.ticks; waterSquirts.push(waterSquirt); // Animate water squirt tween(waterSquirt, { scaleX: 0.35, alpha: 0.3 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Remove from tracking array when destroyed for (var squirtIndex = 0; squirtIndex < waterSquirts.length; squirtIndex++) { if (waterSquirts[squirtIndex] === waterSquirt) { waterSquirts.splice(squirtIndex, 1); break; } } waterSquirt.destroy(); } }); // Deal damage to enemy enemy.takeDamage(8); // Make electric effect visible when hit by water enemy.showElectricEffect(); // Robot fires back when hit by water tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(newTower); } break; // Found an enemy in range } } } // Sort all towers by Y position (higher Y values should be in front) towers.sort(function (a, b) { return a.y - b.y; }); // Re-add towers to game in sorted order, but ensure they render behind wave assets for (var i = 0; i < towers.length; i++) { game.removeChild(towers[i]); // Find the first wave asset in children to insert towers before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert tower before wave asset game.addChildAt(towers[i], waveAssetIndex); } else { // No wave asset found, add normally game.addChild(towers[i]); } } // Deduct cost and update display playerCash -= 10; cashText.setText('Cash: $' + playerCash); // Clean up placement mode cleanupPlacementMode(); } }; } }; // Gas button click handler gasButton.down = function (x, y, obj) { // Play tower select sound LK.getSound('Towerselect').play(); // Check if player has enough cash if (playerCash >= 20) { // Start tower placement mode isPlacingTower = true; selectedTowerType = 'gas'; // Create tower preview if (towerPreview) { towerPreview.destroy(); } towerPreview = game.addChild(LK.getAsset('Dog', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.5 })); // Create green tick button for confirming placement if (confirmButton) { confirmButton.destroy(); } confirmButton = game.addChild(new Button()); confirmButton.setup({ text: '✓', backgroundColor: 0x00FF00, textColor: 0xFFFFFF, width: 120, height: 120, fontSize: 80 }); confirmButton.x = 2048 - 150; confirmButton.y = 2732 / 2; confirmButton.onClick = function () { // Place tower at current preview position if (towerPreview) { // Align tower position to nearest guidelines var alignedX = getNearestGuidelineX(towerPreview.x); var alignedY = getNearestGuidelineY(towerPreview.y); // Check if tower can be placed at this position if (!canPlaceTowerAt(alignedX, alignedY)) { // Flash red to indicate invalid placement LK.effects.flashObject(towerPreview, 0xff0000, 500); return; } var newTower = game.addChild(LK.getAsset('Dog', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY, alpha: 1 })); newTower._assetId = 'Dog'; // Mark this as a gas tower newTower.health = 100; newTower.maxHealth = 100; newTower.isBeingDamaged = false; newTower.lastDamageTime = 0; newTower.lastFireTime = 0; // Initialize fire rate tracking // Add health bar to tower var healthBarOutline = game.addChild(LK.getAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 120 })); var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 120, tint: 0x00ff00 })); newTower.healthBarOutline = healthBarOutline; newTower.healthBar = healthBar; newTower.takeDamage = function (damage) { newTower.health -= damage; if (newTower.health <= 0) { newTower.health = 0; } // Update health bar var healthPercentage = newTower.health / newTower.maxHealth; newTower.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { newTower.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { newTower.healthBar.tint = 0xffff00; // Yellow } else { newTower.healthBar.tint = 0xff0000; // Red } }; // Add tower selection functionality newTower.down = function (x, y, obj) { // Only allow selection if not placing a tower if (!isPlacingTower) { // Clear previous selection if (selectedTower) { selectedTower.alpha = 1.0; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Select this tower selectedTower = newTower; selectedTower.alpha = 0.7; // Dim selected tower // Create red X delete button deleteButton = game.addChild(new Button()); deleteButton.setup({ text: '✖', backgroundColor: 0xFF0000, textColor: 0xFFFFFF, width: 80, height: 80, fontSize: 50 }); deleteButton.x = selectedTower.x + 120; deleteButton.y = selectedTower.y - 80; deleteButton.onClick = function () { // Delete the selected tower if (selectedTower) { // Remove health bar components if (selectedTower.healthBarOutline) { selectedTower.healthBarOutline.destroy(); } if (selectedTower.healthBar) { selectedTower.healthBar.destroy(); } // Remove blades if this is a fan tower if (selectedTower.blades) { selectedTower.blades.destroy(); } // Remove plasma if this is a plasma tower if (selectedTower.plasma) { selectedTower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (selectedTower.plasmaRays) { for (var rayIndex = 0; rayIndex < selectedTower.plasmaRays.length; rayIndex++) { if (selectedTower.plasmaRays[rayIndex] && selectedTower.plasmaRays[rayIndex].parent) { selectedTower.plasmaRays[rayIndex].destroy(); } } selectedTower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (selectedTower.activeZaps) { for (var zapIndex = 0; zapIndex < selectedTower.activeZaps.length; zapIndex++) { selectedTower.activeZaps[zapIndex].destroy(); } selectedTower.activeZaps = []; selectedTower.zapTargets = []; } // Remove tower from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === selectedTower) { towers.splice(i, 1); break; } } // Destroy the tower selectedTower.destroy(); selectedTower = null; // Remove delete button deleteButton.destroy(); deleteButton = null; } }; } }; towers.push(newTower); // Check for enemies in range immediately after placing for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) if (Math.abs(enemy.y - newTower.y) < 50) { // Check if enemy is to the right of the tower and within range of 600 pixels if (enemy.x > newTower.x && enemy.x - newTower.x < 600) { // Fire immediately newTower.lastFireTime = LK.ticks; // Switch dog asset to dog1 for fart animation var fartingDog = game.addChild(LK.getAsset('Dog1', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y, alpha: 1 })); // Copy tower properties to the new farting dog fartingDog._assetId = 'Dog'; fartingDog.health = newTower.health; fartingDog.maxHealth = newTower.maxHealth; fartingDog.isBeingDamaged = newTower.isBeingDamaged; fartingDog.lastDamageTime = newTower.lastDamageTime; fartingDog.lastFireTime = newTower.lastFireTime; fartingDog.healthBarOutline = newTower.healthBarOutline; fartingDog.healthBar = newTower.healthBar; fartingDog.takeDamage = newTower.takeDamage; // Replace tower in towers array for (var towerIndex = 0; towerIndex < towers.length; towerIndex++) { if (towers[towerIndex] === newTower) { towers[towerIndex] = fartingDog; break; } } // Remove original tower newTower.destroy(); // Create gas cloud visual var gasCloud = game.addChild(LK.getAsset('GasCloud', { anchorX: 0.5, anchorY: 0.5, x: fartingDog.x + 150, y: fartingDog.y, scaleX: 1.5, scaleY: 1.5, alpha: 0.6, tint: 0x90EE90 })); // Track gas cloud creation time and add to cleanup tracking gasCloud.createdTime = LK.ticks; gasClouds.push(gasCloud); // Animate gas cloud expanding and fading tween(gasCloud, { scaleX: 2.5, scaleY: 2.5, alpha: 0.2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { gasCloud.destroy(); } }); // Switch back to normal dog after 500ms LK.setTimeout(function () { fartingDog.destroy(); var normalDog = game.addChild(LK.getAsset('Dog', { anchorX: 0.5, anchorY: 0.5, x: fartingDog.x, y: fartingDog.y, alpha: 1 })); // Copy properties back to normal dog normalDog._assetId = 'Dog'; normalDog.health = fartingDog.health; normalDog.maxHealth = fartingDog.maxHealth; normalDog.isBeingDamaged = fartingDog.isBeingDamaged; normalDog.lastDamageTime = fartingDog.lastDamageTime; normalDog.lastFireTime = fartingDog.lastFireTime; normalDog.healthBarOutline = fartingDog.healthBarOutline; normalDog.healthBar = fartingDog.healthBar; normalDog.takeDamage = fartingDog.takeDamage; // Replace in towers array for (var towerIndex = 0; towerIndex < towers.length; towerIndex++) { if (towers[towerIndex] === fartingDog) { towers[towerIndex] = normalDog; break; } } }, 500); // Deal damage to enemy if affected by gas var isAffectedByGas = true; if (enemy.attachedAssets && enemy.attachedAssets.length > 0) { var enemyAssetId = enemy.attachedAssets[0].assetId; if (enemyAssetId === 'Robot' || enemyAssetId === 'Drone' || enemyAssetId === 'SpaceDrone' || enemyAssetId === 'Probedroid') { isAffectedByGas = false; } } if (isAffectedByGas) { enemy.takeDamage(2); // Move enemy to random adjacent row on guideline var currentLine = enemy.pathLine; var availableLines = []; // Add adjacent lines (above and below current line) if (currentLine > 1) availableLines.push(currentLine - 1); if (currentLine < 6) availableLines.push(currentLine + 1); // If we have adjacent lines available, move to one randomly if (availableLines.length > 0) { var randomIndex = Math.floor(Math.random() * availableLines.length); var newLine = availableLines[randomIndex]; enemy.pathLine = newLine; // Get the new Y position for the new line var newY = pathPositions['line' + newLine]; // Animate the enemy to the new line position tween(enemy, { y: newY }, { duration: 500, easing: tween.easeInOut }); } // Robot fires back when hit by gas tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(fartingDog); } } break; // Found an enemy in range } } } // Sort all towers by Y position (higher Y values should be in front) towers.sort(function (a, b) { return a.y - b.y; }); // Re-add towers to game in sorted order, but ensure they render behind wave assets for (var i = 0; i < towers.length; i++) { game.removeChild(towers[i]); // Find the first wave asset in children to insert towers before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert tower before wave asset game.addChildAt(towers[i], waveAssetIndex); } else { // No wave asset found, add normally game.addChild(towers[i]); } } // Deduct cost and update display playerCash -= 20; cashText.setText('Cash: $' + playerCash); // Clean up placement mode cleanupPlacementMode(); } }; } }; // Add Airbutton to the right side of gasbutton var airButton = game.addChild(LK.getAsset('Airbutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 426.67 + 50 + 420 + 50 + 50, y: 2732 - 350 })); // Air button click handler airButton.down = function (x, y, obj) { // Play tower select sound LK.getSound('Towerselect').play(); // Check if player has enough cash if (playerCash >= 30) { // Start tower placement mode isPlacingTower = true; selectedTowerType = 'air'; // Create tower preview if (towerPreview) { towerPreview.destroy(); } towerPreview = game.addChild(LK.getAsset('Fan', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.5 })); // Add blades to fan preview var previewBlades = game.addChild(LK.getAsset('Blades', { anchorX: 0.5, anchorY: 0.5, x: towerPreview.x + 50, y: towerPreview.y - 40 - 100 + 25 + 25, alpha: 0.5 })); // Store blades reference in tower preview towerPreview.blades = previewBlades; // Create green tick button for confirming placement if (confirmButton) { confirmButton.destroy(); } confirmButton = game.addChild(new Button()); confirmButton.setup({ text: '✓', backgroundColor: 0x00FF00, textColor: 0xFFFFFF, width: 120, height: 120, fontSize: 80 }); confirmButton.x = 2048 - 150; confirmButton.y = 2732 / 2; confirmButton.onClick = function () { // Place tower at current preview position if (towerPreview) { // Align tower position to nearest guidelines var alignedX = getNearestGuidelineX(towerPreview.x); var alignedY = getNearestGuidelineY(towerPreview.y); // Check if tower can be placed at this position if (!canPlaceTowerAt(alignedX, alignedY)) { // Flash red to indicate invalid placement LK.effects.flashObject(towerPreview, 0xff0000, 500); return; } // Create spinning animation for blades var _spinBlades = function spinBlades() { tween(blades, { rotation: blades.rotation + Math.PI * 2 }, { duration: 500, easing: tween.linear, onFinish: _spinBlades }); }; var newTower = game.addChild(LK.getAsset('Fan', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY, alpha: 1 })); // Add blades asset to fan tower var blades = game.addChild(LK.getAsset('Blades', { anchorX: 0.5, anchorY: 0.5, x: newTower.x + 50, y: newTower.y - 40 - 100 + 25 + 25 })); _spinBlades(); // Store blades reference in tower newTower.blades = blades; newTower.health = 100; newTower.maxHealth = 100; newTower.isBeingDamaged = false; newTower.lastDamageTime = 0; // Add cooldown properties for fan newTower.cooldownTimer = 0; newTower.cooldownDuration = 20 * 60; // 20 seconds at 60fps newTower.cooldownActive = false; newTower.isRunning = true; // Add health bar to tower var healthBarOutline = game.addChild(LK.getAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 170 })); var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 170, tint: 0x00ff00 })); newTower.healthBarOutline = healthBarOutline; newTower.healthBar = healthBar; newTower.takeDamage = function (damage) { newTower.health -= damage; if (newTower.health <= 0) { newTower.health = 0; } // Update health bar var healthPercentage = newTower.health / newTower.maxHealth; newTower.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { newTower.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { newTower.healthBar.tint = 0xffff00; // Yellow } else { newTower.healthBar.tint = 0xff0000; // Red } }; // Add tower selection functionality newTower.down = function (x, y, obj) { // Only allow selection if not placing a tower if (!isPlacingTower) { // Clear previous selection if (selectedTower) { selectedTower.alpha = 1.0; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Select this tower selectedTower = newTower; selectedTower.alpha = 0.7; // Dim selected tower // Create red X delete button deleteButton = game.addChild(new Button()); deleteButton.setup({ text: '✖', backgroundColor: 0xFF0000, textColor: 0xFFFFFF, width: 80, height: 80, fontSize: 50 }); deleteButton.x = selectedTower.x + 120; deleteButton.y = selectedTower.y - 80; deleteButton.onClick = function () { // Delete the selected tower if (selectedTower) { // Remove health bar components if (selectedTower.healthBarOutline) { selectedTower.healthBarOutline.destroy(); } if (selectedTower.healthBar) { selectedTower.healthBar.destroy(); } // Remove blades if this is a fan tower if (selectedTower.blades) { selectedTower.blades.destroy(); } // Remove plasma if this is a plasma tower if (selectedTower.plasma) { selectedTower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (selectedTower.plasmaRays) { for (var rayIndex = 0; rayIndex < selectedTower.plasmaRays.length; rayIndex++) { if (selectedTower.plasmaRays[rayIndex] && selectedTower.plasmaRays[rayIndex].parent) { selectedTower.plasmaRays[rayIndex].destroy(); } } selectedTower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (selectedTower.activeZaps) { for (var zapIndex = 0; zapIndex < selectedTower.activeZaps.length; zapIndex++) { selectedTower.activeZaps[zapIndex].destroy(); } selectedTower.activeZaps = []; selectedTower.zapTargets = []; } // Remove tower from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === selectedTower) { towers.splice(i, 1); break; } } // Destroy the tower selectedTower.destroy(); selectedTower = null; // Remove delete button deleteButton.destroy(); deleteButton = null; } }; } }; towers.push(newTower); // Sort all towers by Y position (higher Y values should be in front) towers.sort(function (a, b) { return a.y - b.y; }); // Re-add towers to game in sorted order, but ensure they render behind wave assets for (var i = 0; i < towers.length; i++) { game.removeChild(towers[i]); // Find the first wave asset in children to insert towers before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert tower before wave asset game.addChildAt(towers[i], waveAssetIndex); } else { // No wave asset found, add normally game.addChild(towers[i]); } // Re-add blades if this tower has them to ensure they render in front if (towers[i].blades) { game.removeChild(towers[i].blades); game.addChild(towers[i].blades); } } // Deduct cost and update display playerCash -= 30; cashText.setText('Cash: $' + playerCash); // Clean up placement mode isPlacingTower = false; selectedTowerType = null; // Clean up placement mode cleanupPlacementMode(); } }; } }; // Add Firebutton to the right side of airbutton var fireButton = game.addChild(LK.getAsset('Firebutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 426.67 + 50 + 420 + 50 + 426 + 50 + 50, y: 2732 - 350 })); // Fire button click handler fireButton.down = function (x, y, obj) { // Play tower select sound LK.getSound('Towerselect').play(); // Check if player has enough cash if (playerCash >= 40) { // Start tower placement mode isPlacingTower = true; selectedTowerType = 'fire'; // Create tower preview if (towerPreview) { towerPreview.destroy(); } towerPreview = game.addChild(LK.getAsset('Fireworks', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.5 })); // Create green tick button for confirming placement if (confirmButton) { confirmButton.destroy(); } confirmButton = game.addChild(new Button()); confirmButton.setup({ text: '✓', backgroundColor: 0x00FF00, textColor: 0xFFFFFF, width: 120, height: 120, fontSize: 80 }); confirmButton.x = 2048 - 150; confirmButton.y = 2732 / 2; confirmButton.onClick = function () { // Place tower at current preview position if (towerPreview) { // Align tower position to nearest guidelines var alignedX = getNearestGuidelineX(towerPreview.x); var alignedY = getNearestGuidelineY(towerPreview.y); // Check if tower can be placed at this position if (!canPlaceTowerAt(alignedX, alignedY)) { // Flash red to indicate invalid placement LK.effects.flashObject(towerPreview, 0xff0000, 500); return; } var newTower = game.addChild(LK.getAsset('Fireworks', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY, alpha: 1 })); newTower._assetId = 'Fireworks'; // Mark this as a fire tower newTower.health = 100; newTower.maxHealth = 100; newTower.isBeingDamaged = false; newTower.lastDamageTime = 0; // Add health bar to tower var healthBarOutline = game.addChild(LK.getAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 210 })); var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 210, tint: 0x00ff00 })); newTower.healthBarOutline = healthBarOutline; newTower.healthBar = healthBar; newTower.takeDamage = function (damage) { newTower.health -= damage; if (newTower.health <= 0) { newTower.health = 0; } // Update health bar var healthPercentage = newTower.health / newTower.maxHealth; newTower.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { newTower.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { newTower.healthBar.tint = 0xffff00; // Yellow } else { newTower.healthBar.tint = 0xff0000; // Red } }; // Add tower selection functionality newTower.down = function (x, y, obj) { // Only allow selection if not placing a tower if (!isPlacingTower) { // Clear previous selection if (selectedTower) { selectedTower.alpha = 1.0; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Select this tower selectedTower = newTower; selectedTower.alpha = 0.7; // Dim selected tower // Create red X delete button deleteButton = game.addChild(new Button()); deleteButton.setup({ text: '✖', backgroundColor: 0xFF0000, textColor: 0xFFFFFF, width: 80, height: 80, fontSize: 50 }); deleteButton.x = selectedTower.x + 120; deleteButton.y = selectedTower.y - 80; deleteButton.onClick = function () { // Delete the selected tower if (selectedTower) { // Remove health bar components if (selectedTower.healthBarOutline) { selectedTower.healthBarOutline.destroy(); } if (selectedTower.healthBar) { selectedTower.healthBar.destroy(); } // Remove blades if this is a fan tower if (selectedTower.blades) { selectedTower.blades.destroy(); } // Remove plasma if this is a plasma tower if (selectedTower.plasma) { selectedTower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (selectedTower.plasmaRays) { for (var rayIndex = 0; rayIndex < selectedTower.plasmaRays.length; rayIndex++) { if (selectedTower.plasmaRays[rayIndex] && selectedTower.plasmaRays[rayIndex].parent) { selectedTower.plasmaRays[rayIndex].destroy(); } } selectedTower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (selectedTower.activeZaps) { for (var zapIndex = 0; zapIndex < selectedTower.activeZaps.length; zapIndex++) { selectedTower.activeZaps[zapIndex].destroy(); } selectedTower.activeZaps = []; selectedTower.zapTargets = []; } // Remove tower from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === selectedTower) { towers.splice(i, 1); break; } } // Destroy the tower selectedTower.destroy(); selectedTower = null; // Remove delete button deleteButton.destroy(); deleteButton = null; } }; } }; towers.push(newTower); // Check for enemies in range immediately after placing for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) if (Math.abs(enemy.y - newTower.y) < 50) { // Check if enemy is to the right of the tower and within range of 1500 pixels if (enemy.x > newTower.x && enemy.x - newTower.x < 1500) { // Fire immediately newTower.lastFireTime = LK.ticks; // Create fireball projectile var fireball = game.addChild(LK.getAsset('Fireball', { anchorX: 0.5, anchorY: 0.5, x: newTower.x + 50, y: newTower.y, scaleX: 0.5, scaleY: 0.5, rotation: 35 * Math.PI / 180 })); fireball.createdTime = LK.ticks; // Track creation time for cleanup fireball.targetX = enemy.x; fireball.targetY = enemy.y; fireball.speed = 8; fireball.damage = 10; // Calculate direction to enemy var deltaX = enemy.x - fireball.x; var deltaY = enemy.y - fireball.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); fireball.velocityX = deltaX / distance * fireball.speed; fireball.velocityY = deltaY / distance * fireball.speed; // Animate fireball movement var fireballInterval = LK.setInterval(function () { fireball.x += fireball.velocityX; fireball.y += fireball.velocityY; // Check if fireball hit any enemy for (var enemyIndex = 0; enemyIndex < enemies.length; enemyIndex++) { var targetEnemy = enemies[enemyIndex]; var fireballDistance = Math.sqrt(Math.pow(fireball.x - targetEnemy.x, 2) + Math.pow(fireball.y - targetEnemy.y, 2)); if (fireballDistance < 80) { // Check if this is a SpaceDrone that is currently rotating if (targetEnemy._assetId === 'SpaceDrone' && targetEnemy.isRotating) { // Deflect fireball back at the original firing tower var deflectedFireball = game.addChild(LK.getAsset('Fireball', { anchorX: 0.5, anchorY: 0.5, x: fireball.x, y: fireball.y, scaleX: 0.5, scaleY: 0.5, rotation: fireball.rotation + Math.PI, tint: 0x0088FF // Blue tint to show it's deflected })); // Store reference to the original firing tower for precise targeting deflectedFireball.targetTower = newTower; // Calculate direction back to the exact same tower that fired this fireball var deltaX = newTower.x - deflectedFireball.x; var deltaY = newTower.y - deflectedFireball.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); deflectedFireball.velocityX = deltaX / distance * fireball.speed; deflectedFireball.velocityY = deltaY / distance * fireball.speed; deflectedFireball.damage = fireball.damage; // Animate deflected fireball movement var deflectedInterval = LK.setInterval(function () { deflectedFireball.x += deflectedFireball.velocityX; deflectedFireball.y += deflectedFireball.velocityY; // Check if deflected fireball hit the original firing tower var deflectedDistance = Math.sqrt(Math.pow(deflectedFireball.x - deflectedFireball.targetTower.x, 2) + Math.pow(deflectedFireball.y - deflectedFireball.targetTower.y, 2)); if (deflectedDistance < 100) { // Hit the same tower that originally fired this fireball - deal damage deflectedFireball.targetTower.takeDamage(deflectedFireball.damage); LK.effects.flashObject(deflectedFireball.targetTower, 0xff0000, 300); // Remove deflected fireball LK.clearInterval(deflectedInterval); tween.stop(deflectedFireball); deflectedFireball.destroy(); return; } // Check if deflected fireball went off screen if (deflectedFireball.x > 2048 + 100 || deflectedFireball.x < -100 || deflectedFireball.y > 2732 + 100 || deflectedFireball.y < -100) { LK.clearInterval(deflectedInterval); tween.stop(deflectedFireball); deflectedFireball.destroy(); } }, 16); // Remove original fireball LK.clearInterval(fireballInterval); tween.stop(fireball); fireball.destroy(); return; } // Hit enemy - deal damage targetEnemy.takeDamage(fireball.damage); // Robot fires back when hit by fire tower if (targetEnemy.fireBackAtTower) { targetEnemy.fireBackAtTower(newTower); } // Remove fireball LK.clearInterval(fireballInterval); tween.stop(fireball); fireball.destroy(); return; } } // Check if fireball went off screen if (fireball.x > 2048 + 100 || fireball.x < -100 || fireball.y > 2732 + 100 || fireball.y < -100) { LK.clearInterval(fireballInterval); tween.stop(fireball); fireball.destroy(); } }, 16); break; // Found an enemy in range } } } // Sort all towers by Y position (higher Y values should be in front) towers.sort(function (a, b) { return a.y - b.y; }); // Re-add towers to game in sorted order, but ensure they render behind wave assets for (var i = 0; i < towers.length; i++) { game.removeChild(towers[i]); // Find the first wave asset in children to insert towers before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert tower before wave asset game.addChildAt(towers[i], waveAssetIndex); } else { // No wave asset found, add normally game.addChild(towers[i]); } } // Deduct cost and update display playerCash -= 40; cashText.setText('Cash: $' + playerCash); // Clean up placement mode cleanupPlacementMode(); } }; } }; // Add mystery button under air button var mysteryButton1 = game.addChild(LK.getAsset('Mysterybutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 426.67 + 50 + 420 + 50 + 50, y: 2732 - 350 + 200 + 20 })); // Add Electricbutton to bottom of screen under water button var electricButton = game.addChild(LK.getAsset('Electricbutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 50, y: 2732 - 350 + 200 + 20 })); // Electric button click handler electricButton.down = function (x, y, obj) { // Play tower select sound LK.getSound('Towerselect').play(); // Check if player has enough cash if (playerCash >= 50) { // Start tower placement mode isPlacingTower = true; selectedTowerType = 'electric'; // Create tower preview if (towerPreview) { towerPreview.destroy(); } towerPreview = game.addChild(LK.getAsset('Bugzapper', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.5 })); // Create green tick button for confirming placement if (confirmButton) { confirmButton.destroy(); } confirmButton = game.addChild(new Button()); confirmButton.setup({ text: '✓', backgroundColor: 0x00FF00, textColor: 0xFFFFFF, width: 120, height: 120, fontSize: 80 }); confirmButton.x = 2048 - 150; confirmButton.y = 2732 / 2; confirmButton.onClick = function () { // Place tower at current preview position if (towerPreview) { // Align tower position to nearest guidelines var alignedX = getNearestGuidelineX(towerPreview.x); var alignedY = getNearestGuidelineY(towerPreview.y); // Check if tower can be placed at this position if (!canPlaceTowerAt(alignedX, alignedY)) { // Flash red to indicate invalid placement LK.effects.flashObject(towerPreview, 0xff0000, 500); return; } var newTower = game.addChild(LK.getAsset('Bugzapper', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY, alpha: 1 })); newTower._assetId = 'Bugzapper'; // Mark this as an electric tower newTower.health = 100; newTower.maxHealth = 100; newTower.isBeingDamaged = false; newTower.lastDamageTime = 0; // Add health bar to tower var healthBarOutline = game.addChild(LK.getAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 210 })); var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 210, tint: 0x00ff00 })); newTower.healthBarOutline = healthBarOutline; newTower.healthBar = healthBar; newTower.takeDamage = function (damage) { newTower.health -= damage; if (newTower.health <= 0) { newTower.health = 0; } // Update health bar var healthPercentage = newTower.health / newTower.maxHealth; newTower.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { newTower.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { newTower.healthBar.tint = 0xffff00; // Yellow } else { newTower.healthBar.tint = 0xff0000; // Red } }; // Add tower selection functionality newTower.down = function (x, y, obj) { // Only allow selection if not placing a tower if (!isPlacingTower) { // Clear previous selection if (selectedTower) { selectedTower.alpha = 1.0; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Select this tower selectedTower = newTower; selectedTower.alpha = 0.7; // Dim selected tower // Create red X delete button deleteButton = game.addChild(new Button()); deleteButton.setup({ text: '✖', backgroundColor: 0xFF0000, textColor: 0xFFFFFF, width: 80, height: 80, fontSize: 50 }); deleteButton.x = selectedTower.x + 120; deleteButton.y = selectedTower.y - 80; deleteButton.onClick = function () { // Delete the selected tower if (selectedTower) { // Remove health bar components if (selectedTower.healthBarOutline) { selectedTower.healthBarOutline.destroy(); } if (selectedTower.healthBar) { selectedTower.healthBar.destroy(); } // Remove blades if this is a fan tower if (selectedTower.blades) { selectedTower.blades.destroy(); } // Remove plasma if this is a plasma tower if (selectedTower.plasma) { selectedTower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (selectedTower.plasmaRays) { for (var rayIndex = 0; rayIndex < selectedTower.plasmaRays.length; rayIndex++) { if (selectedTower.plasmaRays[rayIndex] && selectedTower.plasmaRays[rayIndex].parent) { selectedTower.plasmaRays[rayIndex].destroy(); } } selectedTower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (selectedTower.activeZaps) { for (var zapIndex = 0; zapIndex < selectedTower.activeZaps.length; zapIndex++) { selectedTower.activeZaps[zapIndex].destroy(); } selectedTower.activeZaps = []; selectedTower.zapTargets = []; } // Remove tower from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === selectedTower) { towers.splice(i, 1); break; } } // Destroy the tower selectedTower.destroy(); selectedTower = null; // Remove delete button deleteButton.destroy(); deleteButton = null; } }; } }; towers.push(newTower); // Initialize zap arrays for electric tower newTower.activeZaps = []; newTower.zapTargets = []; // Check for enemies in range immediately after placing for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) if (Math.abs(enemy.y - newTower.y) < 50) { // Check if enemy is to the right of the tower and within range of 800 pixels if (enemy.x > newTower.x && enemy.x - newTower.x < 800) { // Create zap3 visual positioned at enemy's location var newZap = game.addChild(LK.getAsset('Zap3', { anchorX: 0.5, anchorY: 0.5, x: enemy.x - 400, y: enemy.y, scaleX: 1.0, scaleY: 1.0, alpha: 0.9, tint: 0x00FFFF })); // Store zap effect and target newTower.activeZaps.push(newZap); newTower.zapTargets.push(enemy); // Deal damage to enemy enemy.takeDamage(5); // Make electric effect visible when hit by electricity enemy.showElectricEffect(); // Robot fires back when hit by electric tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(newTower); } break; // Found an enemy in range } } } // Sort all towers by Y position (higher Y values should be in front) towers.sort(function (a, b) { return a.y - b.y; }); // Re-add towers to game in sorted order, but ensure they render behind wave assets for (var i = 0; i < towers.length; i++) { game.removeChild(towers[i]); // Find the first wave asset in children to insert towers before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert tower before wave asset game.addChildAt(towers[i], waveAssetIndex); } else { // No wave asset found, add normally game.addChild(towers[i]); } } // Deduct cost and update display playerCash -= 50; cashText.setText('Cash: $' + playerCash); // Clean up placement mode cleanupPlacementMode(); } }; } }; // Add Plasmabutton to bottom of screen under gas button var plasmaButton = game.addChild(LK.getAsset('Plasmabutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 426.67 + 50 + 50, y: 2732 - 350 + 200 + 20 })); // Plasma button click handler plasmaButton.down = function (x, y, obj) { // Play tower select sound LK.getSound('Towerselect').play(); // Check if player has enough cash if (playerCash >= 60) { // Start tower placement mode isPlacingTower = true; selectedTowerType = 'plasma'; // Create tower preview if (towerPreview) { towerPreview.destroy(); } towerPreview = game.addChild(LK.getAsset('Plasmaball', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.5 })); // Add plasma asset to plasma tower preview and render it in front var previewPlasma = game.addChild(LK.getAsset('Plasma', { anchorX: 0.5, anchorY: 0.5, x: towerPreview.x, y: towerPreview.y - 20, alpha: 0.5 })); // Store plasma reference in tower preview towerPreview.plasma = previewPlasma; // Start plasma animation for preview animatePlasma(previewPlasma); // Create green tick button for confirming placement if (confirmButton) { confirmButton.destroy(); } confirmButton = game.addChild(new Button()); confirmButton.setup({ text: '✓', backgroundColor: 0x00FF00, textColor: 0xFFFFFF, width: 120, height: 120, fontSize: 80 }); confirmButton.x = 2048 - 150; confirmButton.y = 2732 / 2; confirmButton.onClick = function () { // Place tower at current preview position if (towerPreview) { // Align tower position to nearest guidelines var alignedX = getNearestGuidelineX(towerPreview.x); var alignedY = getNearestGuidelineY(towerPreview.y); // Check if tower can be placed at this position if (!canPlaceTowerAt(alignedX, alignedY)) { // Flash red to indicate invalid placement LK.effects.flashObject(towerPreview, 0xff0000, 500); return; } var newTower = game.addChild(LK.getAsset('Plasmaball', { anchorX: 0.5, anchorY: 0.5, x: alignedX, y: alignedY, alpha: 1 })); newTower._assetId = 'Plasmaball'; // Mark this as a plasma tower // Add plasma asset to plasmaball tower and render it in front var plasma = game.addChild(LK.getAsset('Plasma', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 20, alpha: 1 })); // Store plasma reference in tower newTower.plasma = plasma; // Start plasma animation animatePlasma(plasma); newTower.health = 100; newTower.maxHealth = 100; newTower.isBeingDamaged = false; newTower.lastDamageTime = 0; // Add health bar to tower var healthBarOutline = game.addChild(LK.getAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 150 })); var healthBar = game.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: newTower.x, y: newTower.y - 150, tint: 0x00ff00 })); newTower.healthBarOutline = healthBarOutline; newTower.healthBar = healthBar; newTower.takeDamage = function (damage) { newTower.health -= damage; if (newTower.health <= 0) { newTower.health = 0; } // Update health bar var healthPercentage = newTower.health / newTower.maxHealth; newTower.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { newTower.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { newTower.healthBar.tint = 0xffff00; // Yellow } else { newTower.healthBar.tint = 0xff0000; // Red } }; // Add tower selection functionality newTower.down = function (x, y, obj) { // Only allow selection if not placing a tower if (!isPlacingTower) { // Clear previous selection if (selectedTower) { selectedTower.alpha = 1.0; } if (deleteButton) { deleteButton.destroy(); deleteButton = null; } // Select this tower selectedTower = newTower; selectedTower.alpha = 0.7; // Dim selected tower // Create red X delete button deleteButton = game.addChild(new Button()); deleteButton.setup({ text: '✖', backgroundColor: 0xFF0000, textColor: 0xFFFFFF, width: 80, height: 80, fontSize: 50 }); deleteButton.x = selectedTower.x + 120; deleteButton.y = selectedTower.y - 80; deleteButton.onClick = function () { // Delete the selected tower if (selectedTower) { // Remove health bar components if (selectedTower.healthBarOutline) { selectedTower.healthBarOutline.destroy(); } if (selectedTower.healthBar) { selectedTower.healthBar.destroy(); } // Remove blades if this is a fan tower if (selectedTower.blades) { selectedTower.blades.destroy(); } // Remove plasma if this is a plasma tower if (selectedTower.plasma) { selectedTower.plasma.destroy(); } // Clean up plasma rays when stopping plasma attack if (selectedTower.plasmaRays) { for (var rayIndex = 0; rayIndex < selectedTower.plasmaRays.length; rayIndex++) { if (selectedTower.plasmaRays[rayIndex] && selectedTower.plasmaRays[rayIndex].parent) { selectedTower.plasmaRays[rayIndex].destroy(); } } selectedTower.plasmaRays = []; } // Remove active zaps if this is an electric tower if (selectedTower.activeZaps) { for (var zapIndex = 0; zapIndex < selectedTower.activeZaps.length; zapIndex++) { selectedTower.activeZaps[zapIndex].destroy(); } selectedTower.activeZaps = []; selectedTower.zapTargets = []; } // Remove tower from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === selectedTower) { towers.splice(i, 1); break; } } // Destroy the tower selectedTower.destroy(); selectedTower = null; // Remove delete button deleteButton.destroy(); deleteButton = null; } }; } }; towers.push(newTower); // Check for enemies in range immediately after placing var enemiesInRange = []; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(enemy.x - newTower.x, 2) + Math.pow(enemy.y - newTower.y, 2)); if (distance <= 800) { enemiesInRange.push(enemy); } } // If enemies are in range, start attack immediately if (enemiesInRange.length > 0) { newTower.plasmaAttackActive = true; newTower.plasmaAttackStartTime = LK.ticks; newTower.plasmaRays = []; // Create 10 plasma ray assets for (var j = 0; j < 10; j++) { var angle = j * 36 * Math.PI / 180; var rayX = newTower.x + Math.cos(angle) * 150; var rayY = newTower.y + Math.sin(angle) * 150; var plasmaRay = game.addChild(LK.getAsset('Plasmaray1', { anchorX: 0.5, anchorY: 0.5, x: rayX, y: rayY, rotation: angle + Math.PI / 2, alpha: 0.8, scaleX: 0.8, scaleY: 0.8 })); plasmaRay.createdTime = LK.ticks; // Track creation time for cleanup newTower.plasmaRays.push(plasmaRay); } // Deal damage to enemies in range for (var i = 0; i < enemiesInRange.length; i++) { var enemy = enemiesInRange[i]; enemy.takeDamage(15); // Make electric effect visible when hit by plasma enemy.showElectricEffect(); // Robot fires back when hit by plasma tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(newTower); } } } // Sort all towers by Y position (higher Y values should be in front) towers.sort(function (a, b) { return a.y - b.y; }); // Re-add towers to game in sorted order, but ensure they render behind wave assets for (var i = 0; i < towers.length; i++) { game.removeChild(towers[i]); // Find the first wave asset in children to insert towers before it var waveAssetIndex = -1; for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child.assetId === 'Wave1' || child.assetId === 'Wave2' || child.assetId === 'Wave3' || child.assetId === 'Wave4' || child.assetId === 'Wave5') { waveAssetIndex = j; break; } } if (waveAssetIndex >= 0) { // Insert tower before wave asset game.addChildAt(towers[i], waveAssetIndex); } else { // No wave asset found, add normally game.addChild(towers[i]); } // Re-add plasma if this tower has it to ensure it renders in front if (towers[i].plasma) { game.removeChild(towers[i].plasma); game.addChild(towers[i].plasma); // Restart plasma animation after re-adding animatePlasma(towers[i].plasma); } } // Deduct cost and update display playerCash -= 60; cashText.setText('Cash: $' + playerCash); // Clean up placement mode cleanupPlacementMode(); } }; } }; // Add mystery button under fire button var mysteryButton2 = game.addChild(LK.getAsset('Mysterybutton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 4 - 250 + 426.67 + 50 + 420 + 50 + 426 + 50 + 50, y: 2732 - 350 + 200 + 20 })); } function spawnEnemy() { // Don't spawn enemies during cutscenes if (!gameplayActive) return; if (enemiesSpawned >= maxEnemiesInWave) return; var enemy; // Check if we're in level 2 (after story1 scene has been shown) var isLevel2 = typeof window.isLevel2 !== 'undefined' ? window.isLevel2 : false; if (isLevel2 && currentWave === 1) { // Level 2 wave 1 - spawn Level2Enemy enemy = game.addChild(new Level2Enemy()); enemy._assetId = 'Level2Enemy'; // Implement gradual spawn speed increase for level 2 wave 1 // Start at 15 seconds, gradually reduce to 3 seconds over the course of the wave var progressRatio = enemiesSpawned / maxEnemiesInWave; var newInterval = 900 - progressRatio * 540; // 900 ticks (15s) down to 360 ticks (6s) enemySpawnInterval = Math.max(180, newInterval); // Minimum 3 seconds } else if (isLevel2 && currentWave === 2) { // Level 2 wave 2 - spawn smaller and harder Level2Enemy instead of drones enemy = game.addChild(new Level2Enemy()); enemy._assetId = 'Level2Enemy'; // Make them smaller by scaling both axes enemy.scaleX = 0.7; enemy.scaleY = 0.7; // Make them much harder to kill - increase health significantly enemy.health = 1500; // Increased from 1000 to 1500 enemy.maxHealth = 1500; } else if (currentWave === 1) { enemy = game.addChild(new Enemy()); } else if (currentWave === 2) { enemy = game.addChild(new Drone()); } else if (currentWave === 3) { enemy = game.addChild(new Robot()); } else if (currentWave === 4) { enemy = game.addChild(new SpaceDrone()); enemy._assetId = 'SpaceDrone'; // Mark for deflection detection } else if (currentWave === 5 && !window.isLevel2) { enemy = game.addChild(new UFO()); enemy._assetId = 'UFO'; // Mark as UFO boss } if (currentWave === 5 && !window.isLevel2) { // Position UFO boss above the center of the screen (hover down from above) enemy.x = 2048 / 2; // Center horizontally enemy.y = -200; // Start above screen enemy.pathLine = 1; // Animate UFO hovering down from above tween(enemy, { y: 400 }, { duration: 3000, easing: tween.easeInOut }); enemy.health = 1200; enemy.maxHealth = 1200; enemy.speed = 0; // UFO doesn't move horizontally initially } else { // Position enemy at the right side of the screen enemy.x = 2048 - 50; // Start from right side // Randomly assign path from all 6 available lines var availableLines = [1, 2, 3, 4, 5, 6]; var randomLineIndex = Math.floor(Math.random() * availableLines.length); var selectedLine = availableLines[randomLineIndex]; // Set enemy position based on randomly selected line if (selectedLine === 1) { enemy.y = pathPositions.line1; enemy.pathLine = 1; } else if (selectedLine === 2) { enemy.y = pathPositions.line2; enemy.pathLine = 2; } else if (selectedLine === 3) { enemy.y = pathPositions.line3; enemy.pathLine = 3; } else if (selectedLine === 4) { enemy.y = pathPositions.line4; enemy.pathLine = 4; } else if (selectedLine === 5) { enemy.y = pathPositions.line5; enemy.pathLine = 5; } else if (selectedLine === 6) { enemy.y = pathPositions.line6; enemy.pathLine = 6; } enemy.health = 100; enemy.maxHealth = 100; enemy.speed = -2; // Negative speed to move left } enemy.pathIndex = 0; enemy.lastPathIndex = -1; enemies.push(enemy); enemiesSpawned++; } function updateEnemies() { for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // Move enemy using its speed (negative for left movement) enemy.x += enemy.speed * gameSpeed; // Check if enemy reached the left side of the screen (goal) if (enemy.lastX >= 0 && enemy.x < 0) { // Enemy reached the goal - player loses a life playerLives -= 1; // Update lives display using direct reference if (livesText) { livesText.setText('Lives: ' + playerLives); } // Check if player has no lives left if (playerLives <= 0) { // Game over - show game over screen LK.showGameOver(); return; } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Enemy reached the left side - remove it enemy.destroy(); enemies.splice(i, 1); continue; } // Optimize collision detection - only check towers within reasonable distance first var nearbyTowers = []; for (var j = 0; j < towers.length; j++) { var tower = towers[j]; var quickDistance = Math.abs(enemy.x - tower.x) + Math.abs(enemy.y - tower.y); // Manhattan distance is faster if (quickDistance < 1200) { // Only consider towers within 1200 pixels nearbyTowers.push(tower); } } // Check each nearby tower type separately (no early exits) for (var j = 0; j < nearbyTowers.length; j++) { var tower = nearbyTowers[j]; // Check if tower is a water tower (Tower1 asset) if (tower._assetId === 'Tower1') { // For UFO boss, check if tower can attack based on UFO's current position and pathLine var canAttackUFO = false; if (enemy._assetId === 'UFO' && enemy.pathLine && tower.pathLine) { // UFO can be attacked by towers on the same line when hovering if (Math.abs(enemy.pathLine - tower.pathLine) === 0) { canAttackUFO = true; } } // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) OR can attack UFO if (Math.abs(enemy.y - tower.y) < 50 || canAttackUFO) { // For UFO, check if within range regardless of direction; for others, check right side and range var inRange = false; if (enemy._assetId === 'UFO') { // UFO can be attacked from any direction within range var distance = Math.sqrt(Math.pow(enemy.x - tower.x, 2) + Math.pow(enemy.y - tower.y, 2)); inRange = distance < 700; } else { // Normal enemies must be to the right and within range inRange = enemy.x > tower.x && enemy.x - tower.x < 700; } if (inRange) { // Initialize fire rate tracking for this tower if (!tower.lastFireTime) { tower.lastFireTime = 0; } // Fire every 30 ticks (0.5 seconds at 60fps) if (LK.ticks - tower.lastFireTime >= 30 / gameSpeed) { tower.lastFireTime = LK.ticks; // Play water sound when tower attacks LK.getSound('Water').play(); // Only create water squirt visual if we have less than 10 active squirts if (waterSquirts.length < 10) { var waterSquirt = game.addChild(LK.getAsset('Water', { anchorX: 0, anchorY: 0.5, x: tower.x, y: tower.y, scaleX: 0.3, scaleY: 0.1, alpha: 0.7, tint: 0x4169E1 })); waterSquirt.createdTime = LK.ticks; waterSquirts.push(waterSquirt); // Simplified water squirt animation tween(waterSquirt, { scaleX: 0.35, alpha: 0.2 }, { duration: 300, easing: tween.linear }); // Auto-destroy after 2 seconds instead of complex tracking LK.setTimeout(function () { if (waterSquirt && waterSquirt.parent) { waterSquirt.destroy(); var index = waterSquirts.indexOf(waterSquirt); if (index !== -1) waterSquirts.splice(index, 1); } }, 2000); } // Deal damage to enemy - reduced damage for UFO boss if (enemy._assetId === 'UFO') { enemy.takeDamage(4); } else { enemy.takeDamage(8); } // Make electric effect visible when hit by water enemy.showElectricEffect(); // Robot fires back when hit by water tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(tower); } // Check if enemy is destroyed if (enemy.health <= 0) { // Create explosion effect when probedroid is destroyed by water var waterExplosion = game.addChild(LK.getAsset('Explosion', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 0.3, scaleY: 0.3 })); // Animate explosion with tween tween(waterExplosion, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { waterExplosion.destroy(); } }); enemy.destroy(); enemies.splice(i, 1); // Give player money for destroying enemy playerCash += 5; cashText.setText('Cash: $' + playerCash); // Give player additional $5 cash for water tower kill playerCash += 5; cashText.setText('Cash: $' + playerCash); // Award 50 points for kill playerScore += 50; LK.setScore(playerScore); if (window.scoreText) { window.scoreText.setText('Score: ' + playerScore); } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Track defeated enemies for win condition enemiesDefeated++; i--; // Adjust index since we removed an enemy return; // Exit function for this enemy } } } } } // Check if tower is a gas tower (Dog asset) if (tower._assetId === 'Dog') { // Check tower health before any firing logic - if dead, skip all processing if (tower.health <= 0) { continue; // Skip to next tower } // For UFO boss, check if tower can attack based on UFO's current position and pathLine var canAttackUFO = false; if (enemy._assetId === 'UFO' && enemy.pathLine && tower.pathLine) { // UFO can be attacked by towers on the same line when hovering if (Math.abs(enemy.pathLine - tower.pathLine) === 0) { canAttackUFO = true; } } // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) OR can attack UFO if (Math.abs(enemy.y - tower.y) < 50 || canAttackUFO) { // For UFO, check if within range regardless of direction; for others, check right side and range var inRange = false; if (enemy._assetId === 'UFO') { // UFO can be attacked from any direction within range var distance = Math.sqrt(Math.pow(enemy.x - tower.x, 2) + Math.pow(enemy.y - tower.y, 2)); inRange = distance < 600; } else { // Normal enemies must be to the right and within range inRange = enemy.x > tower.x && enemy.x - tower.x < 600; } if (inRange) { // Initialize fire rate tracking for this tower if (!tower.lastFireTime) { tower.lastFireTime = 0; } // Fire every 45 ticks (0.75 seconds at 60fps) if (LK.ticks - tower.lastFireTime >= 45 / gameSpeed) { tower.lastFireTime = LK.ticks; // Switch dog asset to dog1 for fart animation tower.destroy(); var fartingDog = game.addChild(LK.getAsset('Dog1', { anchorX: 0.5, anchorY: 0.5, x: tower.x, y: tower.y, alpha: 1 })); // Copy tower properties to the new farting dog fartingDog._assetId = 'Dog'; fartingDog.health = tower.health; fartingDog.maxHealth = tower.maxHealth; fartingDog.isBeingDamaged = tower.isBeingDamaged; fartingDog.lastDamageTime = tower.lastDamageTime; fartingDog.lastFireTime = tower.lastFireTime; fartingDog.healthBarOutline = tower.healthBarOutline; fartingDog.healthBar = tower.healthBar; fartingDog.takeDamage = function (damage) { fartingDog.health -= damage; if (fartingDog.health <= 0) { fartingDog.health = 0; } // Update health bar var healthPercentage = fartingDog.health / fartingDog.maxHealth; fartingDog.healthBar.scaleX = healthPercentage; // Change color based on health if (healthPercentage > 0.6) { fartingDog.healthBar.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { fartingDog.healthBar.tint = 0xffff00; // Yellow } else { fartingDog.healthBar.tint = 0xff0000; // Red } }; // Replace tower in towers array for (var towerIndex = 0; towerIndex < towers.length; towerIndex++) { if (towers[towerIndex] === tower) { towers[towerIndex] = fartingDog; break; } } // Create gas cloud visual var gasCloud = game.addChild(LK.getAsset('GasCloud', { anchorX: 0.5, anchorY: 0.5, x: tower.x + 150, // Position gas cloud 100 pixels further left when dog farts y: tower.y, scaleX: 1.5, scaleY: 1.5, alpha: 0.6, tint: 0x90EE90 })); // Track gas cloud creation time and add to cleanup tracking gasCloud.createdTime = LK.ticks; gasClouds.push(gasCloud); // Animate gas cloud expanding and fading tween(gasCloud, { scaleX: 2.5, scaleY: 2.5, alpha: 0.2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { gasCloud.destroy(); } }); // Switch back to normal dog after 500ms LK.setTimeout(function () { // Check if farting dog is still alive before replacing if (fartingDog.health <= 0) { // Farting dog is dead, don't replace it, just destroy it // Remove health bar components if (fartingDog.healthBarOutline) { fartingDog.healthBarOutline.destroy(); } if (fartingDog.healthBar) { fartingDog.healthBar.destroy(); } fartingDog.destroy(); // Remove from towers array for (var towerIndex = towers.length - 1; towerIndex >= 0; towerIndex--) { if (towers[towerIndex] === fartingDog) { towers.splice(towerIndex, 1); break; } } return; } // Check if fartingDog still exists in towers array before replacing var fartingDogExists = false; for (var towerIndex = 0; towerIndex < towers.length; towerIndex++) { if (towers[towerIndex] === fartingDog) { fartingDogExists = true; break; } } if (!fartingDogExists) { // Farting dog was already destroyed, don't create replacement return; } fartingDog.destroy(); var normalDog = game.addChild(LK.getAsset('Dog', { anchorX: 0.5, anchorY: 0.5, x: fartingDog.x, y: fartingDog.y, alpha: 1 })); // Copy properties back to normal dog normalDog._assetId = 'Dog'; normalDog.health = fartingDog.health; normalDog.maxHealth = fartingDog.maxHealth; normalDog.isBeingDamaged = fartingDog.isBeingDamaged; normalDog.lastDamageTime = fartingDog.lastDamageTime; normalDog.lastFireTime = fartingDog.lastFireTime; normalDog.healthBarOutline = fartingDog.healthBarOutline; normalDog.healthBar = fartingDog.healthBar; normalDog.takeDamage = fartingDog.takeDamage; // Check if normal dog should be destroyed immediately after replacement if (normalDog.health <= 0) { // Remove health bar components if (normalDog.healthBarOutline) { normalDog.healthBarOutline.destroy(); } if (normalDog.healthBar) { normalDog.healthBar.destroy(); } normalDog.destroy(); // Remove from towers array for (var towerIndex = towers.length - 1; towerIndex >= 0; towerIndex--) { if (towers[towerIndex] === fartingDog) { towers.splice(towerIndex, 1); break; } } return; } // Replace in towers array for (var towerIndex = 0; towerIndex < towers.length; towerIndex++) { if (towers[towerIndex] === fartingDog) { towers[towerIndex] = normalDog; break; } } }, 500); // Check if enemy is affected by gas (farts don't affect robots, drones, or probe droids) var isAffectedByGas = true; // Check enemy asset ID to determine if it's a robot/mechanical enemy if (enemy.attachedAssets && enemy.attachedAssets.length > 0) { var enemyAssetId = enemy.attachedAssets[0].assetId; if (enemyAssetId === 'Robot' || enemyAssetId === 'Drone' || enemyAssetId === 'SpaceDrone' || enemyAssetId === 'Probedroid') { isAffectedByGas = false; } } // Only deal damage if enemy is affected by gas if (isAffectedByGas) { // Deal damage to enemy - reduced damage for UFO boss if (enemy._assetId === 'UFO') { enemy.takeDamage(1); } else { enemy.takeDamage(2); } // Move enemy to random adjacent row on guideline var currentLine = enemy.pathLine; var availableLines = []; // Add adjacent lines (above and below current line) if (currentLine > 1) availableLines.push(currentLine - 1); if (currentLine < 6) availableLines.push(currentLine + 1); // If we have adjacent lines available, move to one randomly if (availableLines.length > 0) { var randomIndex = Math.floor(Math.random() * availableLines.length); var newLine = availableLines[randomIndex]; enemy.pathLine = newLine; // Get the new Y position for the new line var newY = pathPositions['line' + newLine]; // Animate the enemy to the new line position tween(enemy, { y: newY }, { duration: 500, easing: tween.easeInOut }); } // Robot fires back when hit by gas tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(fartingDog); } // Check if enemy is destroyed if (enemy.health <= 0) { enemy.destroy(); enemies.splice(i, 1); // Give player money for destroying enemy playerCash += 5; cashText.setText('Cash: $' + playerCash); // Award 50 points for kill playerScore += 50; LK.setScore(playerScore); if (window.scoreText) { window.scoreText.setText('Score: ' + playerScore); } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Track defeated enemies for win condition enemiesDefeated++; i--; // Adjust index since we removed an enemy return; // Exit function for this enemy } } } } } } // Check if tower is a bug zapper (electric tower) if (tower._assetId === 'Bugzapper') { // Initialize zap tracking arrays for this tower if (!tower.activeZaps) { tower.activeZaps = []; tower.zapTargets = []; } // For UFO boss, check if tower can attack based on UFO's current position and pathLine var canAttackUFO = false; if (enemy._assetId === 'UFO' && enemy.pathLine && tower.pathLine) { // UFO can be attacked by towers on the same line when hovering if (Math.abs(enemy.pathLine - tower.pathLine) === 0) { canAttackUFO = true; } } // Check if enemy is on the same horizontal line as the tower (within 100 pixels tolerance) OR can attack UFO if (Math.abs(enemy.y - tower.y) < 100 || canAttackUFO) { // For UFO, check if within range regardless of direction; for others, check right side and range var inRange = false; if (enemy._assetId === 'UFO') { // UFO can be attacked from any direction within range var distance = Math.sqrt(Math.pow(enemy.x - tower.x, 2) + Math.pow(enemy.y - tower.y, 2)); inRange = distance < 800; } else { // Normal enemies must be to the right and within range inRange = enemy.x > tower.x && enemy.x - tower.x < 800; } if (inRange) { // Check if this is a SpaceDrone - SpaceDrones cannot be affected by electric attacks if (enemy._assetId === 'SpaceDrone') { // SpaceDrones are immune to electric attacks - skip all electric tower processing continue; // Skip to next tower } // Check if this enemy already has a zap effect var hasZapEffect = false; for (var zapIndex = 0; zapIndex < tower.zapTargets.length; zapIndex++) { if (tower.zapTargets[zapIndex] === enemy) { hasZapEffect = true; break; } } // Create new zap effect for this enemy if it doesn't have one if (!hasZapEffect) { // Create zap3 visual positioned at enemy's location var newZap = game.addChild(LK.getAsset('Zap3', { anchorX: 0.5, anchorY: 0.5, x: enemy.x - 400, y: enemy.y, scaleX: 1.0, scaleY: 1.0, alpha: 0.9, tint: 0x00FFFF })); // Store zap effect and target tower.activeZaps.push(newZap); tower.zapTargets.push(enemy); // Start zap animation for this specific effect var _animateZap = function animateZap(zapEffect) { if (zapEffect && zapEffect.parent) { tween(zapEffect, { alpha: 0.3 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { if (zapEffect && zapEffect.parent) { tween(zapEffect, { alpha: 0.9 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { _animateZap(zapEffect); } }); } } }); } }; _animateZap(newZap); // Deal initial damage immediately when zap starts - reduced for UFO boss if (enemy._assetId === 'UFO') { enemy.takeDamage(5); } else { enemy.takeDamage(10); } // Make electric effect visible when hit by electricity if (enemy.showElectricEffect) { enemy.showElectricEffect(); } // Robot fires back when hit by electric tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(tower); } } // Deal damage every 20 ticks instead of 10 to reduce processing load if (hasZapEffect && LK.ticks % Math.max(1, Math.floor(20 / gameSpeed)) === 0) { // Deal damage to enemy - reduced damage for UFO boss if (enemy._assetId === 'UFO') { enemy.takeDamage(2); } else { enemy.takeDamage(5); } // Make electric effect visible when hit by electricity if (enemy.showElectricEffect) { enemy.showElectricEffect(); } // Robot fires back when hit by electric tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(tower); } // Check if enemy is destroyed if (enemy.health <= 0) { // Create explosive effect when probedroid is destroyed by bugzapper var bugzapperExplosion = game.addChild(LK.getAsset('Explosion', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 0.3, scaleY: 0.3 })); // Animate explosion with tween tween(bugzapperExplosion, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { bugzapperExplosion.destroy(); } }); // Remove zap effect for this enemy for (var zapIndex = tower.activeZaps.length - 1; zapIndex >= 0; zapIndex--) { if (tower.zapTargets[zapIndex] === enemy) { tower.activeZaps[zapIndex].destroy(); tower.activeZaps.splice(zapIndex, 1); tower.zapTargets.splice(zapIndex, 1); break; } } enemy.destroy(); enemies.splice(i, 1); // Give player money for destroying enemy playerCash += 5; cashText.setText('Cash: $' + playerCash); // Award 50 points for kill playerScore += 50; LK.setScore(playerScore); if (window.scoreText) { window.scoreText.setText('Score: ' + playerScore); } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Track defeated enemies for win condition enemiesDefeated++; i--; // Adjust index since we removed an enemy return; // Exit function for this enemy } } } else { // Enemy is not in range, remove its zap effect for (var zapIndex = tower.activeZaps.length - 1; zapIndex >= 0; zapIndex--) { if (tower.zapTargets[zapIndex] === enemy) { tower.activeZaps[zapIndex].destroy(); tower.activeZaps.splice(zapIndex, 1); tower.zapTargets.splice(zapIndex, 1); break; } } } } else { // Enemy is not on same line, remove its zap effect for (var zapIndex = tower.activeZaps.length - 1; zapIndex >= 0; zapIndex--) { if (tower.zapTargets[zapIndex] === enemy) { tower.activeZaps[zapIndex].destroy(); tower.activeZaps.splice(zapIndex, 1); tower.zapTargets.splice(zapIndex, 1); break; } } } } // Check if tower is a fire tower (Fireworks asset) if (tower._assetId === 'Fireworks') { // For UFO boss, check if tower can attack based on UFO's current position and pathLine var canAttackUFO = false; if (enemy._assetId === 'UFO' && enemy.pathLine && tower.pathLine) { // UFO can be attacked by towers on the same line when hovering if (Math.abs(enemy.pathLine - tower.pathLine) === 0) { canAttackUFO = true; } } // Check if enemy is on the same horizontal line as the tower (within 50 pixels tolerance) OR can attack UFO if (Math.abs(enemy.y - tower.y) < 50 || canAttackUFO) { // For UFO, check if within range regardless of direction; for others, check right side and range var inRange = false; if (enemy._assetId === 'UFO') { // UFO can be attacked from any direction within range var distance = Math.sqrt(Math.pow(enemy.x - tower.x, 2) + Math.pow(enemy.y - tower.y, 2)); inRange = distance < 1500; } else { // Normal enemies must be to the right and within range inRange = enemy.x > tower.x && enemy.x - tower.x < 1500; } if (inRange) { // Initialize fire rate tracking for this tower if (!tower.lastFireTime) { tower.lastFireTime = 0; } // Fire every 60 ticks (1 second at 60fps) if (LK.ticks - tower.lastFireTime >= 60 / gameSpeed) { // Store fireball properties // Add fire animation to fireball var animateFireball = function animateFireball(fireball) { // Pulsing scale animation function startFireballPulse() { tween(fireball, { scaleX: 0.6, scaleY: 0.6, alpha: 0.8 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(fireball, { scaleX: 0.5, scaleY: 0.5, alpha: 1.0 }, { duration: 200, easing: tween.easeInOut, onFinish: startFireballPulse }); } }); } // Flickering alpha animation function startFireballFlicker() { tween(fireball, { alpha: 0.7 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(fireball, { alpha: 1.0 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { LK.setTimeout(function () { if (fireball && fireball.parent) { startFireballFlicker(); } }, Math.random() * 300 + 100); } }); } }); } // Start all fire animations startFireballPulse(); startFireballFlicker(); }; // Start fire animation for this fireball tower.lastFireTime = LK.ticks; // Create fireball projectile var fireball = game.addChild(LK.getAsset('Fireball', { anchorX: 0.5, anchorY: 0.5, x: tower.x + 50, y: tower.y, scaleX: 0.5, scaleY: 0.5, rotation: 35 * Math.PI / 180 // Rotate 35 degrees to the right })); fireball.createdTime = LK.ticks; // Track creation time for cleanup animateFireball(fireball); fireball.targetX = enemy.x; fireball.targetY = enemy.y; fireball.speed = 8; fireball.damage = 10; // Calculate direction to enemy var deltaX = enemy.x - fireball.x; var deltaY = enemy.y - fireball.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); fireball.velocityX = deltaX / distance * fireball.speed; fireball.velocityY = deltaY / distance * fireball.speed; // Animate fireball movement with reduced frequency var fireballInterval = LK.setInterval(function () { fireball.x += fireball.velocityX * 2; fireball.y += fireball.velocityY * 2; // Check if fireball hit any enemy for (var enemyIndex = 0; enemyIndex < enemies.length; enemyIndex++) { var targetEnemy = enemies[enemyIndex]; var fireballDistance = Math.sqrt(Math.pow(fireball.x - targetEnemy.x, 2) + Math.pow(fireball.y - targetEnemy.y, 2)); if (fireballDistance < 80) { // Check if this is a SpaceDrone that is currently rotating if (targetEnemy._assetId === 'SpaceDrone' && targetEnemy.isRotating) { // Deflect fireball back at tower var deflectedFireball = game.addChild(LK.getAsset('Fireball', { anchorX: 0.5, anchorY: 0.5, x: fireball.x, y: fireball.y, scaleX: 0.5, scaleY: 0.5, rotation: fireball.rotation + Math.PI, tint: 0x0088FF // Blue tint to show it's deflected })); // Calculate direction back to tower var deltaX = tower.x - deflectedFireball.x; var deltaY = tower.y - deflectedFireball.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); deflectedFireball.velocityX = deltaX / distance * fireball.speed; deflectedFireball.velocityY = deltaY / distance * fireball.speed; deflectedFireball.damage = fireball.damage; // Animate deflected fireball movement var deflectedInterval = LK.setInterval(function () { deflectedFireball.x += deflectedFireball.velocityX; deflectedFireball.y += deflectedFireball.velocityY; // Check if deflected fireball hit the tower var deflectedDistance = Math.sqrt(Math.pow(deflectedFireball.x - tower.x, 2) + Math.pow(deflectedFireball.y - tower.y, 2)); if (deflectedDistance < 100) { // Hit tower - deal damage tower.takeDamage(deflectedFireball.damage); LK.effects.flashObject(tower, 0xff0000, 300); // Remove deflected fireball LK.clearInterval(deflectedInterval); tween.stop(deflectedFireball); deflectedFireball.destroy(); return; } // Check if deflected fireball went off screen if (deflectedFireball.x > 2048 + 100 || deflectedFireball.x < -100 || deflectedFireball.y > 2732 + 100 || deflectedFireball.y < -100) { LK.clearInterval(deflectedInterval); tween.stop(deflectedFireball); deflectedFireball.destroy(); } }, 16); // Remove original fireball LK.clearInterval(fireballInterval); tween.stop(fireball); fireball.destroy(); return; } // Hit enemy - deal damage, reduced for UFO boss if (targetEnemy._assetId === 'UFO') { targetEnemy.takeDamage(fireball.damage / 2); } else { targetEnemy.takeDamage(fireball.damage); } // Robot fires back when hit by fire tower if (targetEnemy.fireBackAtTower) { targetEnemy.fireBackAtTower(tower); } // Create explosion effect var explosion = game.addChild(LK.getAsset('Explosion', { anchorX: 0.5, anchorY: 0.5, x: fireball.x, y: fireball.y, scaleX: 0.3, scaleY: 0.3 })); // Animate explosion tween(explosion, { scaleX: 0.8, scaleY: 0.8, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { explosion.destroy(); } }); // Check if enemy is destroyed if (targetEnemy.health <= 0) { // Check if this is a mechanical enemy that should have explosion effect var isMechanicalEnemy = false; if (targetEnemy.attachedAssets && targetEnemy.attachedAssets.length > 0) { var enemyAssetId = targetEnemy.attachedAssets[0].assetId; if (enemyAssetId === 'Robot' || enemyAssetId === 'Drone' || enemyAssetId === 'SpaceDrone' || enemyAssetId === 'Probedroid' || enemyAssetId === 'UFO') { isMechanicalEnemy = true; } } // For probe droids (current enemy type), always show explosion if (!isMechanicalEnemy) { // Check if this is a probe droid by checking the asset used var enemyChildren = targetEnemy.children; for (var childIndex = 0; childIndex < enemyChildren.length; childIndex++) { var child = enemyChildren[childIndex]; if (child.assetId === 'Probedroid') { isMechanicalEnemy = true; break; } } } // Create explosion effect for mechanical enemies if (isMechanicalEnemy) { var mechanicalExplosion = game.addChild(LK.getAsset('Explosion', { anchorX: 0.5, anchorY: 0.5, x: targetEnemy.x, y: targetEnemy.y, scaleX: 0.2, scaleY: 0.2 })); // Animate explosion with tween tween(mechanicalExplosion, { scaleX: 1.0, scaleY: 1.0, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { mechanicalExplosion.destroy(); } }); } targetEnemy.destroy(); enemies.splice(enemyIndex, 1); // Give player money for destroying enemy playerCash += 5; cashText.setText('Cash: $' + playerCash); // Give player additional $10 cash for fire tower kill playerCash += 10; cashText.setText('Cash: $' + playerCash); // Award 50 points for kill playerScore += 50; LK.setScore(playerScore); if (window.scoreText) { window.scoreText.setText('Score: ' + playerScore); } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Track defeated enemies for win condition enemiesDefeated++; } // Remove fireball LK.clearInterval(fireballInterval); tween.stop(fireball); fireball.destroy(); return; } } // Check if fireball went off screen if (fireball.x > 2048 + 100 || fireball.x < -100 || fireball.y > 2732 + 100 || fireball.y < -100) { LK.clearInterval(fireballInterval); tween.stop(fireball); fireball.destroy(); } }, 32); // ~30fps for fireballs to reduce processing load break; // Found an enemy in range, no need to check other towers of this type } } } } } // Check collision with fan blades first (before tower collision) for (var k = 0; k < towers.length; k++) { var tower = towers[k]; // Check if this tower has blades (fan tower) and is running (not in cooldown) if (tower.blades && tower.isRunning && !tower.cooldownActive) { // Initialize blade collision tracking for this enemy-tower pair if (!enemy.collidingBlades) { enemy.collidingBlades = {}; } // Check if enemy intersects with fan blades var enemyWidth = 150; // Half width of probedroid var enemyHeight = 183; // Half height of probedroid var bladeWidth = 75; // Half width of blades var bladeHeight = 75; // Half height of blades var isCollidingWithBlades = Math.abs(enemy.x - tower.blades.x) < (enemyWidth + bladeWidth) / 2 && Math.abs(enemy.y - tower.blades.y) < (enemyHeight + bladeHeight) / 2; // Track blade collision state transitions var wasCollidingWithBlades = enemy.collidingBlades[k] || false; // Handle blade collision start if (!wasCollidingWithBlades && isCollidingWithBlades) { // Collision with blades just started enemy.collidingBlades[k] = true; // Push enemy back 400 pixels to the right var pushbackDistance = 400; var targetX = enemy.x + pushbackDistance; // Make sure enemy doesn't go off screen if (targetX > 2048 - 50) { targetX = 2048 - 50; } // Animate pushback using tween tween(enemy, { x: targetX }, { duration: 500, easing: tween.easeOut }); // Inflict damage to enemy enemy.takeDamage(25); // Flash enemy to show damage LK.effects.flashObject(enemy, 0x0088ff, 300); // Robot fires back when hit by fan tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(tower); } // Check if enemy is destroyed if (enemy.health <= 0) { enemy.destroy(); enemies.splice(i, 1); // Give player money for destroying enemy playerCash += 5; cashText.setText('Cash: $' + playerCash); // Award 50 points for kill playerScore += 50; LK.setScore(playerScore); if (window.scoreText) { window.scoreText.setText('Score: ' + playerScore); } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Track defeated enemies for win condition enemiesDefeated++; i--; // Adjust index since we removed an enemy break; // Exit tower loop for this enemy } } else if (!isCollidingWithBlades && wasCollidingWithBlades) { // Collision with blades just ended enemy.collidingBlades[k] = false; } } } // Check if enemy is colliding with any tower for (var k = 0; k < towers.length; k++) { var tower = towers[k]; // Initialize collision tracking for this enemy-tower pair if (!enemy.collidingTowers) { enemy.collidingTowers = {}; } // Check if enemy intersects with tower (use proper collision detection) var enemyWidth = 150; // Half width of probedroid var enemyHeight = 183; // Half height of probedroid var towerWidth = 100; // Half width of tower var towerHeight = 100; // Half height of tower var isColliding = Math.abs(enemy.x - tower.x) < (enemyWidth + towerWidth) / 2 && Math.abs(enemy.y - tower.y) < (enemyHeight + towerHeight) / 2; // Track collision state transitions var wasColliding = enemy.collidingTowers[k] || false; // Handle collision start if (!wasColliding && isColliding) { // Collision just started enemy.collidingTowers[k] = true; // Stop enemy movement enemy.speed = 0; // Mark tower as being damaged tower.isBeingDamaged = true; tower.lastDamageTime = LK.ticks; // Flash both objects to show collision LK.effects.flashObject(enemy, 0xffff00, 200); LK.effects.flashObject(tower, 0xff0000, 200); } // Handle ongoing collision if (isColliding) { // Deal damage every 30 ticks (0.5 seconds at 60fps) if (tower.isBeingDamaged && LK.ticks - tower.lastDamageTime >= 30 / gameSpeed) { // Flash tower red to show damage LK.effects.flashObject(tower, 0xff0000, 300); // Deal damage to tower tower.takeDamage(10); tower.lastDamageTime = LK.ticks; // Check if tower is destroyed - mark for destruction but don't destroy here if (tower.health <= 0) { // Resume enemy movement after destroying tower enemy.speed = -2; // Clear collision tracking for this tower delete enemy.collidingTowers[k]; // Flash enemy green to show it can continue LK.effects.flashObject(enemy, 0x00ff00, 500); } } } else { // No longer colliding if (wasColliding) { // Collision just ended enemy.collidingTowers[k] = false; tower.isBeingDamaged = false; // Resume enemy movement if not colliding with any towers var stillColliding = false; for (var key in enemy.collidingTowers) { if (enemy.collidingTowers[key]) { stillColliding = true; break; } } if (!stillColliding) { enemy.speed = -2; } } } } // Update last position for next frame enemy.lastX = enemy.x; } } // Display the title - They Came For Our WiFi // Import tween plugin var titleImage = game.addChild(LK.getAsset('Title', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 1400 })); // Add skip intro button to top of title screen var skipButton = new Text2('SKIP INTRO', { size: 60, fill: 0xFFFFFF }); skipButton.anchor.set(0.5, 0); skipButton.x = 2048 / 2; skipButton.y = 150; game.addChild(skipButton); // Function to animate plasma pulsation and flicker function animatePlasma(plasma) { if (!plasma) return; // Create pulsation animation - scale up and down function startPulsation() { tween(plasma, { scaleX: 1.3, scaleY: 1.3 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(plasma, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: startPulsation }); } }); } // Create flicker animation - alpha changes function startFlicker() { tween(plasma, { alpha: 0.5 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(plasma, { alpha: 1.0 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { // Random delay before next flicker LK.setTimeout(function () { startFlicker(); }, Math.random() * 1000 + 200); } }); } }); } // Start both animations startPulsation(); startFlicker(); } // Function to handle plasma tower prolonged attack function handlePlasmaAttack(tower) { // Check if this tower has plasma (is a plasma tower) if (!tower.plasma) return; // If tower is destroyed, stop all plasma attacks immediately if (tower.health <= 0) { if (tower.plasmaAttackActive) { tower.plasmaAttackActive = false; // Clean up all plasma rays for this destroyed tower if (tower.plasmaRays) { for (var rayIndex = 0; rayIndex < tower.plasmaRays.length; rayIndex++) { if (tower.plasmaRays[rayIndex] && tower.plasmaRays[rayIndex].parent) { tower.plasmaRays[rayIndex].destroy(); } } tower.plasmaRays = []; } } return; } // Initialize plasma attack tracking if (!tower.plasmaAttackActive) { tower.plasmaAttackActive = false; tower.plasmaRays = []; tower.plasmaAttackStartTime = 0; } // Check for enemies within 800 pixels radius var enemiesInRange = []; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var distance = Math.sqrt(Math.pow(enemy.x - tower.x, 2) + Math.pow(enemy.y - tower.y, 2)); if (distance <= 800) { enemiesInRange.push(enemy); } } // If enemies are in range and attack is not active, start attack if (enemiesInRange.length > 0 && !tower.plasmaAttackActive) { tower.plasmaAttackActive = true; tower.plasmaAttackStartTime = LK.ticks; // Create 6 plasma ray assets instead of 10 for better performance var _loop2 = function _loop2() { angle = j * 60 * Math.PI / 180; // 360 degrees / 6 rays = 60 degrees each rayX = tower.x + Math.cos(angle) * 150; rayY = tower.y + Math.sin(angle) * 150; plasmaRay = game.addChild(LK.getAsset('Plasmaray1', { anchorX: 0.5, anchorY: 0.5, x: rayX, y: rayY, rotation: angle + Math.PI / 2, // Rotate to point outward alpha: 0.8, scaleX: 0.8, scaleY: 0.8 })); plasmaRay.createdTime = LK.ticks; // Track creation time for cleanup tower.plasmaRays.push(plasmaRay); // Animate plasma ray with pulsing effect function animatePlasmaRay(ray) { tween(ray, { alpha: 0.4, scaleX: 1.2, scaleY: 1.2 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(ray, { alpha: 0.9, scaleX: 0.8, scaleY: 0.8 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { if (tower.plasmaAttackActive) { animatePlasmaRay(ray); } } }); } }); } animatePlasmaRay(plasmaRay); }, angle, rayX, rayY, plasmaRay; for (var j = 0; j < 6; j++) { _loop2(); } } // If attack is active, deal damage to enemies in range if (tower.plasmaAttackActive && enemiesInRange.length > 0) { // Deal damage every 20 ticks if ((LK.ticks - tower.plasmaAttackStartTime) % Math.max(1, Math.floor(20 / gameSpeed)) === 0) { for (var i = 0; i < enemiesInRange.length; i++) { var enemy = enemiesInRange[i]; // Reduce damage for UFO boss if (enemy._assetId === 'UFO') { enemy.takeDamage(3); } else { enemy.takeDamage(5); } // Make electric effect visible when hit by plasma enemy.showElectricEffect(); // Robot fires back when hit by plasma tower if (enemy.fireBackAtTower) { enemy.fireBackAtTower(tower); } // Check if enemy is destroyed if (enemy.health <= 0) { // Create explosion effect var plasmaExplosion = game.addChild(LK.getAsset('Explosion', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, scaleX: 0.3, scaleY: 0.3 })); // Animate explosion tween(plasmaExplosion, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { plasmaExplosion.destroy(); } }); enemy.destroy(); for (var k = 0; k < enemies.length; k++) { if (enemies[k] === enemy) { enemies.splice(k, 1); break; } } // Give player money for destroying enemy playerCash += 5; cashText.setText('Cash: $' + playerCash); // Award 50 points for kill playerScore += 50; LK.setScore(playerScore); if (window.scoreText) { window.scoreText.setText('Score: ' + playerScore); } // Track wave completion if (currentWave === 1) { wave1EnemiesCompleted++; } else if (currentWave === 2) { wave2EnemiesCompleted++; } else if (currentWave === 3) { wave3EnemiesCompleted++; } // Track defeated enemies for win condition enemiesDefeated++; } } } } // If no enemies in range and attack is active, or tower is destroyed, stop attack if (enemiesInRange.length === 0 && tower.plasmaAttackActive || tower.health <= 0) { tower.plasmaAttackActive = false; // Remove all plasma rays for (var j = 0; j < tower.plasmaRays.length; j++) { if (tower.plasmaRays[j] && tower.plasmaRays[j].parent) { tower.plasmaRays[j].destroy(); } } tower.plasmaRays = []; } } // Function to skip directly to backdrop scene function skipToBackdrop() { // Stop all tweens tween.stop(titleImage); tween.stop(flybyImage); // Remove title and flyby titleImage.destroy(); flybyImage.destroy(); skipButton.destroy(); // Add backdrop asset to game var backdropAsset = game.addChild(LK.getAsset('backdrop', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1, scaleX: 0.5, scaleY: 0.5 })); // Add flyby asset to backdrop var backdropFlyby = game.addChild(LK.getAsset('Flyby', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 800, alpha: 1, scaleX: 0.1, scaleY: 0.1 })); // Zoom in flyby to final size tween(backdropFlyby, { scaleX: 0.8, scaleY: 0.8 }, { duration: 2000, easing: tween.easeInOut }); // Store the original Y position for backdrop flyby var backdropFlybyOriginalY = backdropFlyby.y; // Make flyby move off the right side of backdrop scene tween(backdropFlyby, { x: 2048 + 375 }, { duration: 6000, easing: tween.linear, onFinish: function onFinish() { // After flyby has left the backdrop scene, fade in wave1 asset var wave1Asset = game.addChild(LK.getAsset('Wave1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave1 asset tween(wave1Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave1 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave1Asset.destroy(); // Start first wave of tower defense game startTowerDefenseWave1(); } }); }, 2000); } }); } }); // Add 6 horizontal guide lines immediately var lineSpacing = 2732 / 7; for (var i = 1; i <= 6; i++) { var yPosition = lineSpacing * i; if (i === 1) { yPosition += 480 + 50; // Move guideline 1 down by 480 pixels } else if (i === 2) { yPosition += 320 + 50; // Move guideline 2 down by 320 pixels } else if (i === 3) { yPosition += 150 + 50; // Move guideline 3 down by 150 pixels } else if (i === 4) { yPosition += 10 + 50; // Move guideline 4 down by 10 pixels } else if (i === 5) { yPosition -= 150 - 50; // Move guideline 5 up by 150 pixels } else if (i === 6) { yPosition -= 310 - 50; // Move guideline 6 up by 310 pixels } var guideLine = game.addChild(LK.getAsset('guideLine', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: yPosition, alpha: 0 })); // Store guideline reference and add click handler guidelines.push(guideLine); guideLine.lineNumber = i; guideLine.down = function (x, y, obj) { if (isPlacingTower && towerPreview) { // Move tower preview to clicked position on guideline // x and y are already in the local coordinate space of the guideline towerPreview.x = x; towerPreview.y = this.y; // Use the guideline's Y position } }; } // Add vertical guidelines for column placement var columnSpacing = 2048 / 9; // Create 8 columns with margins for (var i = 1; i <= 8; i++) { var xPosition = columnSpacing * i; var verticalGuideLine = game.addChild(LK.getAsset('verticalGuideLine', { anchorX: 0.5, anchorY: 0.5, x: xPosition, y: 2732 / 2, alpha: 0 })); verticalGuidelines.push(verticalGuideLine); verticalGuideLine.columnNumber = i; } } // Add touch event to skip button skipButton.down = function (x, y, obj) { skipToBackdrop(); }; // Add flyby asset to title - start at right side of screen var flybyImage = game.addChild(LK.getAsset('Flyby', { anchorX: 0.5, anchorY: 0.5, x: 2048 + 375, y: 2732 / 2 + 1400 - 500 })); // Store the original Y position for flyby var flybyOriginalY = flybyImage.y; // Create hover animation function function startFlybyHover() { // Tween up 50 pixels over 2 seconds tween(flybyImage, { y: flybyOriginalY - 50 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Tween down 50 pixels over 2 seconds tween(flybyImage, { y: flybyOriginalY + 50 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Start the cycle again startFlybyHover(); } }); } }); } // Start the hover animation startFlybyHover(); // Start flyby movement from right to left tween(flybyImage, { x: -375 }, { duration: 8000, easing: tween.linear, onFinish: function onFinish() { // After flyby has left the screen, slowly scroll title up tween(titleImage, { y: titleImage.y - titleImage.height }, { duration: 20000, easing: tween.easeOut }); // Stop the title scrolling after 5 seconds LK.setTimeout(function () { tween.stop(titleImage, { y: true }); // Fade in intro asset in the middle of the screen var introAsset = game.addChild(LK.getAsset('intro', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Add wifi asset to intro var wifiAsset = game.addChild(LK.getAsset('WiFi', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 200, y: 2732 / 2 + 200, alpha: 0 })); // Tween alpha from 0 to 1 for fade-in effect tween(introAsset, { alpha: 1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Also fade in wifi asset tween(wifiAsset, { alpha: 1 }, { duration: 2000, easing: tween.easeInOut }); // After intro has faded in, make flyby move from left to right flybyImage.x = -375; // Reset flyby to left side flybyImage.y = 2732 / 2; // Center vertically tween(flybyImage, { x: 2048 + 375 }, { duration: 6000, easing: tween.linear, onFinish: function onFinish() { // After flyby has left the screen, fade out intro and fade in backdrop tween(introAsset, { alpha: 0 }, { duration: 2000, easing: tween.easeInOut }); // Also fade out wifi asset tween(wifiAsset, { alpha: 0 }, { duration: 2000, easing: tween.easeInOut }); // Add backdrop asset to game var backdropAsset = game.addChild(LK.getAsset('backdrop', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0, scaleX: 0.5, scaleY: 0.5 })); // Add flyby asset to backdrop var backdropFlyby = game.addChild(LK.getAsset('Flyby', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 800, alpha: 1, scaleX: 0.1, scaleY: 0.1 })); // Store the original Y position for backdrop flyby var backdropFlybyOriginalY = backdropFlyby.y; // Make flyby move off the right side of backdrop scene function startBackdropFlybyHover() { tween(backdropFlyby, { x: 2048 + 375 }, { duration: 6000, easing: tween.linear, onFinish: function onFinish() { // After flyby has left the backdrop scene, fade in wave1 asset var wave1Asset = game.addChild(LK.getAsset('Wave1', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in wave1 asset tween(wave1Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave1 visible for 2 seconds then fade out LK.setTimeout(function () { tween(wave1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { wave1Asset.destroy(); // Start first wave of tower defense game startTowerDefenseWave1(); } }); }, 2000); } }); } }); } // Fade in backdrop tween(backdropAsset, { alpha: 1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Zoom in backdrop flyby tween(backdropFlyby, { scaleX: 0.8, scaleY: 0.8 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Start flyby movement to right side after zoom in startBackdropFlybyHover(); } }); // Add 6 horizontal guide lines after backdrop fades in var lineSpacing = 2732 / 7; // Divide screen height by 7 to get 6 lines with margins for (var i = 1; i <= 6; i++) { var yPosition = lineSpacing * i; if (i === 1) { yPosition += 480 + 50; // Move guideline 1 down by 480 pixels } else if (i === 2) { yPosition += 320 + 50; // Move guideline 2 down by 320 pixels } else if (i === 3) { yPosition += 150 + 50; // Move guideline 3 down by 150 pixels } else if (i === 4) { yPosition += 10 + 50; // Move guideline 4 down by 10 pixels } else if (i === 5) { yPosition -= 150 - 50; // Move guideline 5 up by 150 pixels } else if (i === 6) { yPosition -= 310 - 50; // Move guideline 6 up by 310 pixels } var guideLine = game.addChild(LK.getAsset('guideLine', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: yPosition, alpha: 0 })); // Store guideline reference and add click handler guidelines.push(guideLine); guideLine.lineNumber = i; guideLine.down = function (x, y, obj) { if (isPlacingTower && towerPreview) { // Move tower preview to clicked position on guideline // x and y are already in the local coordinate space of the guideline towerPreview.x = x; towerPreview.y = this.y; // Use the guideline's Y position } }; } // Add vertical guidelines for column placement var columnSpacing = 2048 / 9; // Create 8 columns with margins for (var i = 1; i <= 8; i++) { var xPosition = columnSpacing * i; var verticalGuideLine = game.addChild(LK.getAsset('verticalGuideLine', { anchorX: 0.5, anchorY: 0.5, x: xPosition, y: 2732 / 2, alpha: 0 })); verticalGuidelines.push(verticalGuideLine); verticalGuideLine.columnNumber = i; } } }); } }); } }); }, 5000); } }); ;
===================================================================
--- original.js
+++ change.js
@@ -4564,13 +4564,14 @@
var progressRatio = enemiesSpawned / maxEnemiesInWave;
var newInterval = 900 - progressRatio * 540; // 900 ticks (15s) down to 360 ticks (6s)
enemySpawnInterval = Math.max(180, newInterval); // Minimum 3 seconds
} else if (isLevel2 && currentWave === 2) {
- // Level 2 wave 2 - spawn taller and harder Level2Enemy instead of drones
+ // Level 2 wave 2 - spawn smaller and harder Level2Enemy instead of drones
enemy = game.addChild(new Level2Enemy());
enemy._assetId = 'Level2Enemy';
- // Make them taller by scaling Y axis
- enemy.scaleY = 1.3;
+ // Make them smaller by scaling both axes
+ enemy.scaleX = 0.7;
+ enemy.scaleY = 0.7;
// Make them much harder to kill - increase health significantly
enemy.health = 1500; // Increased from 1000 to 1500
enemy.maxHealth = 1500;
} else if (currentWave === 1) {
White circle with two eyes, seen from above.. In-Game asset. 2d. High contrast. No shadows
White simple circular enemy seen from above, black outline. Black eyes, with a single shield in-font of it. Black and white only. Blue background.
White circle with black outline. Blue background.. In-Game asset. 2d. High contrast. No shadows
Fire hydrant. In-Game asset. 2d. High contrast. No shadows
Water spraying forward In-Game asset. 2d. High contrast. No shadows
Fart cloud. In-Game asset. 2d. High contrast. No shadows
Remove fan blades
Fan blades symmetrical. In-Game asset. 2d. High contrast. No shadows
Fireball. In-Game asset. 2d. High contrast. No shadows
Symmetrical explosion. In-Game asset. 2d. High contrast. No shadows
Make picture transparent
Bug zapper on a pole. In-Game asset. 2d. High contrast. No shadows
Probe droid. In-Game asset. 2d. High contrast. No shadows
Space drone. In-Game asset. 2d. High contrast. No shadows
UFO with grey aliens in it. In-Game asset. 2d. High contrast. No shadows
Remove propellers and make them symmetrical
Add more rows to gris
Make this picture with more night sky above the city skyline
Change text to say wave 1
Make button green
Make button blue and say water $10
Make button gold and say electric $50
Make button purple and say Plasma $60
Make button say fire $40
Make button light blue and say air $30
Make button grey and say ??????