User prompt
Move cash digit display to line up under lives digit display
User prompt
Move cash digit display right 300
User prompt
Move cash digit display down 50
User prompt
Move lives digit display right 100
User prompt
Move lives digit display right 200
User prompt
Move speed button down 300
User prompt
Add lives asset to the left of lives digit display
User prompt
Replace lives display text with lives asset
User prompt
Move score asset right 250
User prompt
Move score display to the middle at top of screen
User prompt
Move score digits down 50
User prompt
Move score digits down 20
User prompt
Adjust all digits so they are 60 pixels apart
User prompt
Adjust all digits so they are 10 pixels apart
User prompt
Move score digits under score asset
User prompt
Move skip wave button down 300
User prompt
Move score display left 100
User prompt
Use one,two,three,four,five,six,seven,right,nine assets for displaying scores, cash and lives
User prompt
If skip intro is pressed cancel title screen animations and intro
User prompt
cancel or prevent the automatic story transition timeouts when skip intro is pressed
User prompt
When skip intro is pressed in intro2 or titlescene destroy the story scenes 1-4 so they don't appear later on
User prompt
Cancel all intro story scene if game has started when skip intro button is pressed ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
If skip intro is pressed cancel transition to story scenes ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Replace score display text with score asset
User prompt
Transition through each story scene after 3 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Cutscene class var Cutscene = Container.expand(function () { var self = Container.call(this); // Properties self.scenes = []; self.currentSceneIndex = 0; self.isPlaying = false; // Add a scene to the cutscene self.addScene = function (sceneData) { self.scenes.push(sceneData); }; // Start playing the cutscene self.play = function () { if (self.scenes.length === 0) return; self.isPlaying = true; self.currentSceneIndex = 0; self.playCurrentScene(); }; // Play the current scene self.playCurrentScene = function () { if (self.currentSceneIndex >= self.scenes.length) { self.finish(); return; } var scene = self.scenes[self.currentSceneIndex]; if (scene.onStart) { scene.onStart(); } // Auto-advance to next scene after duration if (scene.duration) { LK.setTimeout(function () { self.nextScene(); }, scene.duration); } }; // Move to next scene self.nextScene = function () { var scene = self.scenes[self.currentSceneIndex]; if (scene.onEnd) { scene.onEnd(); } self.currentSceneIndex++; self.playCurrentScene(); }; // Skip to specific scene self.skipToScene = function (index) { if (index >= 0 && index < self.scenes.length) { self.currentSceneIndex = index; self.playCurrentScene(); } }; // Finish cutscene self.finish = function () { self.isPlaying = false; if (self.onFinish) { self.onFinish(); } }; // Stop cutscene self.stop = function () { self.isPlaying = false; // Stop any running tweens or timeouts tween.stopAll(); }; return self; }); // Defense class var Defense = Container.expand(function () { var self = Container.call(this); var defenseGraphics = self.attachAsset('defense', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Defense logic goes here }; }); // Enemy class for tower defense var Enemy = Container.expand(function () { var self = Container.call(this); // Default to Probedroid, but can be changed self.assetType = 'Probedroid'; var enemyGraphics = self.attachAsset(self.assetType, { anchorX: 0.5, anchorY: 0.5 }); // Enemy properties self.health = 100; self.maxHealth = 100; self.speed = 1.5; self.targetX = -100; // Move to left side of screen self.lastX = self.x; self.collidingWithTower = false; // Track if enemy is colliding with a tower // Health bar graphics - positioned above enemy regardless of asset type self.healthBarBackground = self.attachAsset('healthBarBackground', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -80 }); self.healthBarForeground = self.attachAsset('healthBarForeground', { anchorX: 0, anchorY: 0.5, x: -30, y: -80 }); self.update = function () { // Store last position for collision detection self.lastX = self.x; // Reset collision flag at start of each frame self.collidingWithTower = false; // Check collision with towers on same guideline that are directly to the left for (var i = 0; i < towers.length; i++) { var tower = towers[i]; // Only check towers on the same guideline that are positioned to the left of the enemy if (tower.guidelineIndex === self.guidelineIndex && tower.x < self.x && self.intersects(tower)) { self.collidingWithTower = true; break; } } // Move towards target (right to left) only if not colliding with tower if (self.x > self.targetX && !self.collidingWithTower) { self.x -= self.speed * (typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1); } // Move towards the center of the assigned guideline if (self.baseY !== undefined) { var targetY = self.baseY; var yDifference = targetY - self.y; // Move towards guideline center with a fraction of movement speed if (Math.abs(yDifference) > 2) { self.y += yDifference * 0.1; // Adjust 0.1 to control how quickly enemies center on guidelines } } // Update health bar var healthPercentage = self.health / self.maxHealth; self.healthBarForeground.scaleX = healthPercentage; // Change health bar color based on health percentage if (healthPercentage > 0.6) { self.healthBarForeground.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { self.healthBarForeground.tint = 0xffff00; // Yellow } else { self.healthBarForeground.tint = 0xff0000; // Red } // SpaceDrone shooting logic if (self.assetType === 'SpaceDrone') { // Initialize shooting properties if not set if (self.lastShotFrame === undefined) { self.lastShotFrame = 0; self.shootingRange = 400; self.fireRate = 45; // Shoot every 0.75 seconds at 60fps } // Find nearest tower to shoot at var nearestTower = null; var nearestDistance = Infinity; for (var t = 0; t < towers.length; t++) { var tower = towers[t]; // Only target towers on the same guideline if (tower.guidelineIndex === self.guidelineIndex) { var dx = tower.x - self.x; var dy = tower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Check if tower is within shooting range and closer than current target if (distance <= self.shootingRange && distance < nearestDistance) { nearestDistance = distance; nearestTower = tower; } } } // Shoot at nearest tower if in range and fire rate allows var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate; if (nearestTower && LK.ticks - self.lastShotFrame >= adjustedFireRate) { // Create red bullet var bullet = game.addChild(LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, tint: 0xFF0000 // Red color for SpaceDrone bullets })); // Calculate direction to target tower var dx = nearestTower.x - self.x; var dy = nearestTower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Animate bullet to target tower tween(bullet, { x: nearestTower.x, y: nearestTower.y }, { duration: distance / 3, // Bullet speed easing: tween.linear, onFinish: function onFinish() { bullet.destroy(); // Deal damage to target tower if (nearestTower && nearestTower.parent) { nearestTower.health -= 25; // SpaceDrone bullets deal 25 damage // Flash tower red to show damage tween(nearestTower, { tint: 0xFF0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(nearestTower, { tint: 0xFFFFFF }, { duration: 300, easing: tween.easeOut }); } }); // Check if tower is destroyed if (nearestTower.health <= 0) { // Create explosion effect at tower position var explosionEffect = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: nearestTower.x, y: nearestTower.y, alpha: 1, scaleX: 0.5, scaleY: 0.5 })); // Play explosion sound LK.getSound('Explosion').play(); // Animate explosion with scale and fade out tween(explosionEffect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosionEffect.destroy(); } }); // Remove tower from towers array for (var k = towers.length - 1; k >= 0; k--) { if (towers[k] === nearestTower) { towers.splice(k, 1); break; } } // Destroy tower nearestTower.destroy(); } } } }); self.lastShotFrame = LK.ticks; } } // Robot shooting logic if (self.assetType === 'Robot') { // Rotate wheels when moving - slower anti-clockwise rotation if (self.leftWheel && self.rightWheel && !self.collidingWithTower) { self.leftWheel.rotation -= 0.15; self.rightWheel.rotation -= 0.15; } // Initialize shooting properties if not set if (self.lastShotFrame === undefined) { self.lastShotFrame = 0; self.shootingRange = 450; self.fireRate = 60; // Shoot every 1 second at 60fps } // Find nearest tower to shoot at var nearestTower = null; var nearestDistance = Infinity; for (var t = 0; t < towers.length; t++) { var tower = towers[t]; // Only target towers on the same guideline if (tower.guidelineIndex === self.guidelineIndex) { var dx = tower.x - self.x; var dy = tower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Check if tower is within shooting range and closer than current target if (distance <= self.shootingRange && distance < nearestDistance) { nearestDistance = distance; nearestTower = tower; } } } // Shoot at nearest tower if in range and fire rate allows var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate; if (nearestTower && LK.ticks - self.lastShotFrame >= adjustedFireRate) { // Create laserfire bullet using Laserfire asset var laser = game.addChild(LK.getAsset('Laserfire', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); // Calculate direction to target tower var dx = nearestTower.x - self.x; var dy = nearestTower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Animate laser to target tower tween(laser, { x: nearestTower.x, y: nearestTower.y }, { duration: distance / 4, // Laser speed (faster than regular bullets) easing: tween.linear, onFinish: function onFinish() { laser.destroy(); // Deal damage to target tower if (nearestTower && nearestTower.parent) { nearestTower.health -= 35; // Robot laserfire deals 35 damage // Flash tower blue to show laser damage tween(nearestTower, { tint: 0x00FFFF }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(nearestTower, { tint: 0xFFFFFF }, { duration: 300, easing: tween.easeOut }); } }); // Check if tower is destroyed if (nearestTower.health <= 0) { // Create explosion effect at tower position var explosionEffect = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: nearestTower.x, y: nearestTower.y, alpha: 1, scaleX: 0.5, scaleY: 0.5 })); // Play explosion sound LK.getSound('Explosion').play(); // Animate explosion with scale and fade out tween(explosionEffect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosionEffect.destroy(); } }); // Remove tower from towers array for (var k = towers.length - 1; k >= 0; k--) { if (towers[k] === nearestTower) { towers.splice(k, 1); break; } } // Destroy tower nearestTower.destroy(); } } } }); self.lastShotFrame = LK.ticks; } } // UFO boss special behavior - simplified sequential abduction if (self.isUFOBoss) { // Initialize UFO properties if not set if (!self.movementInitialized) { self.movementInitialized = true; self.currentTargetTower = null; self.movingToTower = false; self.abductionPhase = 'seeking'; // seeking, hovering, abducting, moving_to_next } // Phase 1: Hover down from top if not already done if (!self.hasHoveredDown) { if (self.y < 2732 / 2) { self.y += self.speed * (typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1); } else { self.hasHoveredDown = true; } return; } // Phase 2: Sequential tower abduction if (towers.length > 0) { // Find next tower to abduct if we don't have a target if (!self.currentTargetTower && self.abductionPhase === 'seeking') { // Find closest tower var closestTower = null; var closestDistance = Infinity; for (var t = 0; t < towers.length; t++) { var tower = towers[t]; var dx = tower.x - self.x; var dy = tower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closestDistance = distance; closestTower = tower; } } if (closestTower) { self.currentTargetTower = closestTower; self.abductionPhase = 'moving_to_tower'; // Move to hover higher above the tower tween(self, { x: closestTower.x, y: closestTower.y - 350 // Hover much higher above tower }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { self.abductionPhase = 'hovering'; // Start abduction after brief hover LK.setTimeout(function () { if (self.currentTargetTower && self.currentTargetTower.parent) { self.abductionPhase = 'abducting'; // Create tractor beam that starts small and expands to engulf tower self.tractorBeam = game.addChild(LK.getAsset('Blowing', { anchorX: 0.5, anchorY: 0.0, x: self.currentTargetTower.x, y: self.y + 50, alpha: 0, scaleX: 0.1, scaleY: 0.5, tint: 0x00FF00 // Green tractor beam })); // Animate tractor beam expanding to engulf tower tween(self.tractorBeam, { alpha: 0.7, scaleX: 1.5, scaleY: 1.8 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Add pulsing effect to tractor beam function pulseTractorBeam() { if (self.tractorBeam && self.tractorBeam.parent) { tween(self.tractorBeam, { scaleX: 1.7, scaleY: 2.0, alpha: 0.9 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { if (self.tractorBeam && self.tractorBeam.parent) { tween(self.tractorBeam, { scaleX: 1.5, scaleY: 1.8, alpha: 0.7 }, { duration: 400, easing: tween.easeInOut }); } } }); } } self.tractorBeamPulse = LK.setInterval(pulseTractorBeam, 800); } }); } }, 1000); } }); } } // Keep UFO positioned over tractor beam and tower during abduction if (self.abductionPhase === 'abducting' && self.currentTargetTower && self.currentTargetTower.parent) { // Keep UFO directly above the tower being abducted self.x = self.currentTargetTower.x; // Update tractor beam position to stay connected between UFO and tower if (self.tractorBeam && self.tractorBeam.parent) { self.tractorBeam.x = self.currentTargetTower.x; self.tractorBeam.y = self.y + 50; // Position at bottom of UFO // Dynamically scale tractor beam height to reach tower var beamHeight = Math.abs(self.currentTargetTower.y - (self.y + 50)); self.tractorBeam.scaleY = Math.max(0.5, beamHeight / 200); } } // Phase 3: Abduction process with enhanced sucking effect if (self.abductionPhase === 'abducting' && self.currentTargetTower && self.currentTargetTower.parent) { var tower = self.currentTargetTower; var targetY = self.y + 100; // Target position inside UFO var dy = targetY - tower.y; var distance = Math.abs(dy); if (distance > 20) { // Create sucking motion with acceleration var liftSpeed = Math.min(8, 1.5 + (1 - distance / 400) * 6); // Accelerate as tower gets closer tower.y += dy / distance * liftSpeed * (typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1); // No spinning during lift - removed per request // Gradually shrink tower with more dramatic effect var scaleEffect = Math.max(0.1, distance / 300); tower.scaleX = scaleEffect; tower.scaleY = scaleEffect; // Make tower slightly transparent as it gets sucked up tower.alpha = Math.max(0.3, distance / 400); // Keep tower centered in tractor beam tower.x = self.currentTargetTower.x + Math.sin(LK.ticks * 0.1) * 10; // Add slight wobble } else { // Tower fully abducted - clean up and move to next if (self.tractorBeamPulse) { LK.clearInterval(self.tractorBeamPulse); self.tractorBeamPulse = null; } if (self.tractorBeam && self.tractorBeam.parent) { // Fade out tractor beam tween(self.tractorBeam, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { if (self.tractorBeam && self.tractorBeam.parent) { self.tractorBeam.destroy(); self.tractorBeam = null; } } }); } // Remove tower from game for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === tower) { towers.splice(i, 1); break; } } tower.destroy(); self.currentTargetTower = null; self.abductionPhase = 'seeking'; } } } // Phase 4: Victory condition - head to goal when all towers abducted if (towers.length === 0 && !self.headingToGoal) { // Clean up any remaining tractor beam and intervals if (self.tractorBeamPulse) { LK.clearInterval(self.tractorBeamPulse); self.tractorBeamPulse = null; } if (self.tractorBeam && self.tractorBeam.parent) { self.tractorBeam.destroy(); self.tractorBeam = null; } self.headingToGoal = true; tween(self, { x: -200, y: 2732 / 2 }, { duration: 3000, easing: tween.easeInOut }); } // Check if UFO reached goal if (self.headingToGoal && self.lastX > 0 && self.x <= 0) { playerLives = 0; if (livesDisplay) { livesDisplay.setText('Lives: ' + playerLives); } if (scoreDisplay && scoreDisplay.parent) { scoreDisplay.destroy(); scoreDisplay = null; } if (livesDisplay && livesDisplay.parent) { livesDisplay.destroy(); livesDisplay = null; } if (cashDisplay && cashDisplay.parent) { cashDisplay.destroy(); cashDisplay = null; } LK.showGameOver(); return; } return; // Skip normal enemy logic for UFO boss } // Check if enemy reached the end if (self.lastX > 0 && self.x <= 0) { // Enemy reached the end - remove from game and reduce lives if (self.parent) { // Reduce player lives playerLives--; // Update lives display if it exists if (livesDisplay) { livesDisplay.setText('Lives: ' + playerLives); } // Check for game over if (playerLives <= 0) { // Remove user interface displays before showing game over if (scoreDisplay && scoreDisplay.parent) { scoreDisplay.destroy(); scoreDisplay = null; } if (livesDisplay && livesDisplay.parent) { livesDisplay.destroy(); livesDisplay = null; } if (cashDisplay && cashDisplay.parent) { cashDisplay.destroy(); cashDisplay = null; } LK.showGameOver(); } self.destroy(); // Remove from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } } }; // Method to defeat this enemy self.defeat = function () { if (self.parent) { // Award player 50 points and $10 cash playerScore += 50; playerCash += 10; // Update displays if they exist if (scoreDisplay) { scoreDisplay.setText('Score: ' + playerScore); } if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create explosion effect at enemy position var explosionEffect = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 1, scaleX: 0.5, scaleY: 0.5 })); // Play explosion sound LK.getSound('Explosion').play(); // Animate explosion with scale and fade out tween(explosionEffect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosionEffect.destroy(); } }); enemiesDefeated++; // Special handling for UFO boss defeat if (self.isUFOBoss) { // Clean up tractor beam if it exists if (self.tractorBeam && self.tractorBeam.parent) { self.tractorBeam.destroy(); self.tractorBeam = null; } // UFO boss defeated - transition to story5 cutscene and then level2 var story5Asset = game.addChild(LK.getAsset('Story5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in story5 asset tween(story5Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Add click handler to story5 to transition to level2 story5Asset.down = function (x, y, obj) { // Fade out story5 asset tween(story5Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story5Asset.destroy(); // Start level2 with backdrop2 startLevel2(); } }); }; } }); } self.destroy(); // Remove from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } } }; self.setAssetType = function (assetType) { if (enemyGraphics && enemyGraphics.parent) { enemyGraphics.destroy(); } // Clean up existing wheel graphics if they exist if (self.leftWheel && self.leftWheel.parent) { self.leftWheel.destroy(); self.leftWheel = null; } if (self.rightWheel && self.rightWheel.parent) { self.rightWheel.destroy(); self.rightWheel = null; } // Clean up existing roboarm graphics if they exist if (self.roboarm && self.roboarm.parent) { self.roboarm.destroy(); self.roboarm = null; } self.assetType = assetType; enemyGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); // Add wheels and roboarm for Robot enemies if (assetType === 'Robot') { // Add left wheel self.leftWheel = self.attachAsset('Wheel', { anchorX: 0.5, anchorY: 0.5, x: -80, y: 95, scaleX: 0.6, scaleY: 0.6 }); // Move wheel1 (leftWheel) left 25 pixels from its current position tween(self.leftWheel, { x: -5 // Move from -80 to -5 (75 pixels to the right total, 25 pixels left from original 20) }, { duration: 1000, easing: tween.easeInOut }); // Add right wheel self.rightWheel = self.attachAsset('Wheel', { anchorX: 0.5, anchorY: 0.5, x: 55, // Move from 80 to 55 (25 pixels left) y: 95, scaleX: 0.6, scaleY: 0.6 }); // Add roboarm attachment self.roboarm = self.attachAsset('Roboarm', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -5, scaleX: 0.5, scaleY: 0.5 }); } // UFO boss special properties if (assetType === 'UFO') { self.isUFOBoss = true; self.abductionTarget = null; self.isAbducting = false; self.hasHoveredDown = false; self.tractorBeam = null; self.speed = 1; // Slower movement for boss self.targetX = 0; // Move to goal at x=0 } // Ensure health bars are brought to front after asset change if (self.healthBarBackground && self.healthBarBackground.parent) { self.removeChild(self.healthBarBackground); self.addChild(self.healthBarBackground); } if (self.healthBarForeground && self.healthBarForeground.parent) { self.removeChild(self.healthBarForeground); self.addChild(self.healthBarForeground); } }; return self; }); // Tower class for tower defense var Tower = Container.expand(function () { var self = Container.call(this); // Tower properties self.towerType = 'basic'; self.level = 1; self.damage = 25; self.range = 300; self.fireRate = 60; // Frames between shots (60 = 1 second at 60fps) self.cost = 100; self.upgradeCost = 150; self.lastShotFrame = 0; self.target = null; self.rangeCircle = null; self.showingRange = false; // Tower1 specific properties self.tower1Range = 500; // Tower1 has 500 range straight ahead self.isTower1 = false; // Flag to identify Tower1 instances // Tower health properties self.health = 500; self.maxHealth = 500; self.lastCollidingEnemies = []; // Track which enemies were colliding last frame // Guideline tracking for same-line targeting self.guidelineIndex = -1; // Which guideline this tower is on // Dog tower graphics (for gas towers) self.dogGraphics = null; self.dog1Graphics = null; // Create tower graphics based on tower type var towerGraphics; if (self.towerType === 'fire') { towerGraphics = self.attachAsset('Fireworks', { anchorX: 0.5, anchorY: 0.5 }); } else if (self.towerType === 'air') { towerGraphics = self.attachAsset('Fan', { anchorX: 0.5, anchorY: 0.5 }); } else { towerGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); } // Create level indicator var levelIndicator = self.attachAsset('towerLevelIndicator', { anchorX: 0.5, anchorY: 0.5, x: 30, y: -30 }); // Tower health bar graphics self.towerHealthBarBackground = self.attachAsset('towerHealthBarBackground', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -30 }); self.towerHealthBarForeground = self.attachAsset('towerHealthBarForeground', { anchorX: 0, anchorY: 0.5, x: -40, y: -30 }); // Method to show/hide range circle self.showRange = function () { if (!self.rangeCircle) { self.rangeCircle = self.addChild(LK.getAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: self.range / 500, scaleY: self.range / 500, alpha: 0.3 })); } self.rangeCircle.alpha = 0.3; self.showingRange = true; }; self.hideRange = function () { if (self.rangeCircle) { self.rangeCircle.alpha = 0; } self.showingRange = false; }; // Method to find nearest enemy in range self.findTarget = function () { var nearestEnemy = null; var nearestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Only target enemies on the same guideline, except UFO boss (guidelineIndex -1) which can be targeted by all towers if (enemy.guidelineIndex !== self.guidelineIndex && enemy.guidelineIndex !== -1) { continue; // Skip enemies not on same line } var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // For Tower1 (water tower), target enemies within 500 pixel circular range if (self.isTower1) { // Check if enemy is within 650 range in all directions (extended by 150 pixels) if (distance <= 650) { if (distance < nearestDistance) { nearestDistance = distance; nearestEnemy = enemy; } } } else if (self.towerType === 'fire') { // Fire tower targets enemies 800 pixels both left and right if (Math.abs(dx) <= 800 && Math.abs(dy) <= 50) { var straightDistance = Math.abs(dx); // Distance horizontally if (straightDistance < nearestDistance) { nearestDistance = straightDistance; nearestEnemy = enemy; } } } else if (self.towerType === 'dog') { // Gas tower (dog) targets enemies 600 pixels straight ahead to the right if (dx >= 0 && dx <= 600 && Math.abs(dy) <= 50) { var straightDistance = dx; // Distance to the right if (straightDistance < nearestDistance) { nearestDistance = straightDistance; nearestEnemy = enemy; } } } else if (self.towerType === 'electric') { // Electric tower targets enemies 500 pixels straight ahead to the right if (dx >= 0 && dx <= 500 && Math.abs(dy) <= 50) { var straightDistance = dx; // Distance to the right if (straightDistance < nearestDistance) { nearestDistance = straightDistance; nearestEnemy = enemy; } } } else { // Regular circular range for other towers if (distance <= self.range && distance < nearestDistance) { nearestDistance = distance; nearestEnemy = enemy; } } } return nearestEnemy; }; // Method to shoot at target self.shoot = function (target) { if (!target || !target.parent) return; // For slingshot towers, add pullback effect before firing if (self.towerType === 'slingshot') { // Pull slingshot back 20 pixels tween(self, { scaleX: 0.8, scaleY: 1.1, y: self.y + 20 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Snap forward to firing position tween(self, { scaleX: 1.0, scaleY: 1.0, y: self.y - 20 }, { duration: 100, easing: tween.easeIn }); } }); return; // Slingshot firing is handled elsewhere } // For gas towers (dog), swap visibility when firing if (self.towerType === 'dog' && self.dogGraphics && self.dog1Graphics) { // Show Dog1 (firing state) self.dogGraphics.visible = false; self.dog1Graphics.visible = true; // Play fart sound for dog tower LK.getSound('Fart').play(); // Switch back to Dog after a short delay tween(self, {}, { duration: 200, onFinish: function onFinish() { if (self.dogGraphics && self.dog1Graphics) { self.dogGraphics.visible = true; self.dog1Graphics.visible = false; } } }); } // Play water sound for water tower (Tower1) if (self.isTower1) { LK.getSound('Water').play(); } // Play fireball sound for fire tower if (self.towerType === 'fire') { LK.getSound('Fireball').play(); } // Play electric sound for electric tower if (self.towerType === 'electric') { LK.getSound('Electric').play(); } // For water towers, create prolonged spray attack with 500 pixel range if (self.isTower1) { // Calculate direction to target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only attack if target is within 650 pixel range (extended by 150 pixels) if (distance <= 650) { // Create pulsing spray effect var pulseSpray = function pulseSpray() { if (stream && stream.parent) { tween(stream, { scaleY: (streamWidth + 10) / 4, alpha: 1.0 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { if (stream && stream.parent) { tween(stream, { scaleY: streamWidth / 4, alpha: 0.8 }, { duration: 200, easing: tween.easeInOut }); } } }); } }; // Start pulsing animation // Create blue water spray stream var streamWidth = 25; var streamLength = Math.min(distance, 650); // Use actual distance but cap at 650 (extended by 150 pixels) var stream = game.addChild(LK.getAsset('guideLine', { anchorX: 0, anchorY: 0.5, x: self.x, y: self.y, scaleX: streamLength / 2200, // Scale to target distance scaleY: streamWidth / 4, // Scale to desired width tint: 0x66CCFF, // Lighter blue water color alpha: 0.8 })); // Calculate angle to target for proper rotation var angle = Math.atan2(dy, dx); stream.rotation = angle; // Create prolonged spray effect - animate the stream for 1.5 seconds var sprayDuration = 1500; var damageInterval = 100; // Deal damage every 100ms during spray var damageDealt = 0; var maxDamage = self.damage; pulseSpray(); var pulseInterval = LK.setInterval(pulseSpray, 400); // Deal damage over time during spray var damageTimer = LK.setInterval(function () { if (target && target.parent && damageDealt < maxDamage) { var damagePerTick = Math.ceil(maxDamage / (sprayDuration / damageInterval)); target.health -= damagePerTick; damageDealt += damagePerTick; // Visual feedback - flash target blue // Also flash the target tween(target, { tint: 0x66CCFF }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(target, { tint: 0xFFFFFF }, { duration: 150, easing: tween.easeOut }); } }); if (target.health <= 0) { target.defeat(); LK.clearInterval(damageTimer); LK.clearInterval(pulseInterval); } } }, damageInterval); // Clean up spray effect after duration LK.setTimeout(function () { LK.clearInterval(damageTimer); LK.clearInterval(pulseInterval); if (stream && stream.parent) { tween(stream, { alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { stream.destroy(); } }); } }, sprayDuration); } } else { // Create bullet/projectile effect for non-water towers var bullet = game.addChild(LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); // Make fire tower projectiles use Fireball asset if (self.towerType === 'fire') { // Destroy the default bullet and create fireball projectile bullet.destroy(); bullet = game.addChild(LK.getAsset('Fireball', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); } // Make gas tower (dog) projectiles use Gas asset if (self.towerType === 'dog') { // Destroy the default bullet and create gas projectile bullet.destroy(); bullet = game.addChild(LK.getAsset('Gas', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); } // Make electric tower projectiles cyan and add electric effect if (self.towerType === 'electric') { bullet.tint = 0x00FFFF; // Cyan color for electric tower projectiles // Create electric effect at tower position var electricEffect = game.addChild(LK.getAsset('Electriceffect', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 1, scaleX: 0.8, scaleY: 0.8 })); // Animate electric effect with fade out tween(electricEffect, { alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { electricEffect.destroy(); } }); } // Calculate direction to target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Animate bullet to target tween(bullet, { x: target.x, y: target.y }, { duration: distance / 2, // Speed based on distance easing: tween.linear, onFinish: function onFinish() { bullet.destroy(); // Deal damage to target if (target && target.parent) { target.health -= self.damage; if (target.health <= 0) { target.defeat(); } } } }); } }; // Method to upgrade tower self.upgrade = function () { if (playerCash >= self.upgradeCost && self.level < 3) { playerCash -= self.upgradeCost; self.level++; self.damage += 15; self.range += 50; self.fireRate = Math.max(30, self.fireRate - 10); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); // Update level indicator levelIndicator.destroy(); levelIndicator = self.attachAsset('towerLevelIndicator', { anchorX: 0.5, anchorY: 0.5, x: 30, y: -30, scaleX: self.level, scaleY: self.level }); // Update range circle if showing if (self.showingRange && self.rangeCircle) { self.rangeCircle.scaleX = self.range / 500; self.rangeCircle.scaleY = self.range / 500; } // Play upgrade sound LK.getSound('Towerselect').play(); return true; } return false; }; // Click handler for tower interaction self.down = function (x, y, obj) { if (self.towerType === 'slingshot') { // Start aiming for slingshot self.isAiming = true; self.aimStartX = x; self.aimStartY = y; // Convert to global coordinates var globalPos = self.toGlobal({ x: x, y: y }); self.aimStartX = globalPos.x; self.aimStartY = globalPos.y; } else { if (self.showingRange) { self.hideRange(); } else { self.showRange(); } } }; self.update = function () { // Only operate when wave is active if (!waveActive) return; // Rotate blades for air towers if (self.towerType === 'air' && self.bladeGraphics) { self.bladeGraphics.rotation += 0.2; // Continuous blade rotation // Check for enemies within 300 pixel range straight ahead (to the right) for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; // Only target enemies on the same guideline if (enemy.guidelineIndex === self.guidelineIndex) { var dx = enemy.x - self.x; var dy = enemy.y - self.y; // Check if enemy is within 300 range straight ahead (to the right) if (dx >= 0 && dx <= 300 && Math.abs(dy) <= 50) { // Initialize last blown back tracking if not set if (enemy.lastBlownBack === undefined) { enemy.lastBlownBack = false; } // Only blow back if enemy wasn't blown back last frame (prevent continuous blowing) if (!enemy.lastBlownBack) { // Blow enemy back 400 pixels var newX = enemy.x + 400; // Use tween to animate the blow-back effect tween(enemy, { x: newX }, { duration: 300, easing: tween.easeOut }); // Inflict small amount of damage (15 damage) enemy.health -= 15; // Reduce air tower health by 5 when blowing back enemy self.health -= 5; if (enemy.health <= 0) { enemy.defeat(); } // Visual effect - flash enemy to show they were hit tween(enemy, { tint: 0x00FFFF }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(enemy, { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeOut }); } }); // Mark enemy as blown back this frame enemy.lastBlownBack = true; } } else { // Enemy is not in range, reset blown back tracking enemy.lastBlownBack = false; } } } } // Check for enemy collisions and reduce tower health for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; // Only check collision with enemies on the same guideline if (enemy.guidelineIndex === self.guidelineIndex && self.intersects(enemy)) { // Initialize collision tracking properties if not set if (enemy.lastCollidingWithTower === undefined) { enemy.lastCollidingWithTower = false; } // Check if this enemy just started colliding (wasn't colliding last frame) if (!enemy.lastCollidingWithTower) { // Enemy just started colliding - apply damage self.health -= 5; // Reduce health by 5 per collision // Flash tower red to show damage tween(towerGraphics, { tint: 0xFF0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(towerGraphics, { tint: 0xFFFFFF }, { duration: 300, easing: tween.easeOut }); } }); // Check if tower is destroyed if (self.health <= 0) { // Create explosion effect at tower position var explosionEffect = game.addChild(LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 1, scaleX: 0.5, scaleY: 0.5 })); // Play explosion sound LK.getSound('Explosion').play(); // Animate explosion with scale and fade out tween(explosionEffect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { explosionEffect.destroy(); } }); // Remove tower from towers array for (var k = towers.length - 1; k >= 0; k--) { if (towers[k] === self) { towers.splice(k, 1); break; } } // Destroy tower self.destroy(); return; } } // Mark enemy as currently colliding and pause movement enemy.lastCollidingWithTower = true; enemy.collidingWithTower = true; } else { // Enemy is not colliding - reset collision tracking if (enemy.lastCollidingWithTower === undefined) { enemy.lastCollidingWithTower = false; } else { enemy.lastCollidingWithTower = false; } } } // Update tower health bar var healthPercentage = self.health / self.maxHealth; self.towerHealthBarForeground.scaleX = healthPercentage; // Change health bar color based on health percentage if (healthPercentage > 0.6) { self.towerHealthBarForeground.tint = 0x00ff00; // Green } else if (healthPercentage > 0.3) { self.towerHealthBarForeground.tint = 0xffff00; // Yellow } else { self.towerHealthBarForeground.tint = 0xff0000; // Red } // Find target if we don't have one or current target is invalid if (!self.target || !self.target.parent) { self.target = self.findTarget(); } // Plasma tower pulsation and damage logic if (self.towerType === 'plasma') { // Initialize pulsation tracking if not set if (self.lastPulseFrame === undefined) { self.lastPulseFrame = 0; self.pulseRate = 90; // Pulsate every 1.5 seconds } // Pulsate plasma tower var adjustedPulseRate = typeof gameSpeedMultiplier !== 'undefined' ? self.pulseRate / gameSpeedMultiplier : self.pulseRate; if (LK.ticks - self.lastPulseFrame >= adjustedPulseRate) { // Create visual pulsation effect tween(self, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeOut }); } }); // Find all enemies within 400 pixels radius on same line and lines above/below for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Check if enemy is on same line or adjacent lines (above/below) var guidelineIndexDiff = Math.abs(enemy.guidelineIndex - self.guidelineIndex); if (distance <= 400 && guidelineIndexDiff <= 1) { // Deal damage to enemy enemy.health -= self.damage; // Flash enemy purple to show plasma damage tween(enemy, { tint: 0xFF00FF }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(enemy, { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeOut }); } }); if (enemy.health <= 0) { enemy.defeat(); } } } self.lastPulseFrame = LK.ticks; } } // WiFi tower shooting logic if (self.towerType === 'wifi') { // Initialize shooting properties if not set if (self.lastShotFrame === undefined) { self.lastShotFrame = 0; self.fireRate = 30; // Fast shooting rate self.range = 600; // 600 pixel range } // Find all enemies within 600 range and shoot at them for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Check if enemy is within 600 range if (distance <= 600) { // Check if enough time has passed since last shot var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate; if (LK.ticks - self.lastShotFrame >= adjustedFireRate) { // Create WiFi projectile using WiFi asset var bullet = game.addChild(LK.getAsset('WiFi', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, scaleX: 0.5, // Scale down WiFi asset scaleY: 0.5 // Scale down WiFi asset })); // Animate bullet to target enemy tween(bullet, { x: enemy.x, y: enemy.y }, { duration: distance / 4, // Fast bullet speed easing: tween.linear, onFinish: function onFinish() { bullet.destroy(); // Deal high damage to target enemy if (enemy && enemy.parent) { enemy.health -= 100; // High damage // Flash enemy yellow to show hit tween(enemy, { tint: 0xFFFF00 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(enemy, { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeOut }); } }); if (enemy.health <= 0) { enemy.defeat(); } } } }); self.lastShotFrame = LK.ticks; break; // Only shoot at one enemy per frame } } } return; // Skip regular shooting logic for WiFi towers } // Slingshot tower aiming mechanics - manual player aiming required if (self.towerType === 'slingshot') { // Slingshot towers don't auto-shoot, they require player interaction // The shooting is handled by mouse events return; // Skip auto-shooting logic for slingshot towers } // Shoot at target if in range and fire rate allows (but not for air towers, plasma towers, and slingshot towers) if (self.target && self.target.parent && self.towerType !== 'air' && self.towerType !== 'plasma' && self.towerType !== 'slingshot') { var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var targetInRange = false; // Check if target is still in range based on tower type if (self.isTower1) { // Tower1 (water tower) has 650 pixel circular range (extended by 150 pixels) targetInRange = distance <= 650; } else if (self.towerType === 'fire') { // Fire tower has 800 range both left and right targetInRange = Math.abs(dx) <= 800 && Math.abs(dy) <= 50; } else if (self.towerType === 'dog') { // Gas tower has 600 range straight ahead to the right targetInRange = dx >= 0 && dx <= 600 && Math.abs(dy) <= 50; } else if (self.towerType === 'electric') { // Electric tower has 500 range straight ahead to the right targetInRange = dx >= 0 && dx <= 500 && Math.abs(dy) <= 50; } else { // Regular circular range for other towers targetInRange = distance <= self.range; } if (targetInRange) { // Check if enough time has passed since last shot var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate; if (LK.ticks - self.lastShotFrame >= adjustedFireRate) { self.shoot(self.target); self.lastShotFrame = LK.ticks; } } else { // Target moved out of range self.target = null; } } }; self.setAssetType = function (assetType) { if (towerGraphics && towerGraphics.parent) { towerGraphics.destroy(); } // Clean up existing dog graphics if they exist if (self.dogGraphics && self.dogGraphics.parent) { self.dogGraphics.destroy(); self.dogGraphics = null; } if (self.dog1Graphics && self.dog1Graphics.parent) { self.dog1Graphics.destroy(); self.dog1Graphics = null; } // Clean up existing air tower graphics if they exist if (self.bladeGraphics && self.bladeGraphics.parent) { self.bladeGraphics.destroy(); self.bladeGraphics = null; } self.towerType = assetType.toLowerCase(); // For dog towers, create both Dog and Dog1 assets if (assetType === 'Dog') { self.dogGraphics = self.attachAsset('Dog', { anchorX: 0.5, anchorY: 0.5 }); self.dog1Graphics = self.attachAsset('Dog1', { anchorX: 0.5, anchorY: 0.5 }); // Initially show Dog, hide Dog1 self.dogGraphics.visible = true; self.dog1Graphics.visible = false; towerGraphics = self.dogGraphics; // Set main reference to dogGraphics } else if (assetType === 'Fan') { // For air towers, use Fan asset as actual tower towerGraphics = self.attachAsset('Fan', { anchorX: 0.5, anchorY: 0.5 }); self.bladeGraphics = self.attachAsset('Blades', { anchorX: 0.5, anchorY: 0.5, y: -70 }); self.towerType = 'air'; } else { towerGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); } // Ensure health bars are brought to front after asset change if (self.towerHealthBarBackground && self.towerHealthBarBackground.parent) { self.removeChild(self.towerHealthBarBackground); self.addChild(self.towerHealthBarBackground); } if (self.towerHealthBarForeground && self.towerHealthBarForeground.parent) { self.removeChild(self.towerHealthBarForeground); self.addChild(self.towerHealthBarForeground); } }; // Up handler for slingshot aiming self.up = function (x, y, obj) { if (self.towerType === 'slingshot' && self.isAiming) { // Convert to global coordinates var globalPos = self.toGlobal({ x: x, y: y }); self.aimEndX = globalPos.x; self.aimEndY = globalPos.y; // Calculate aim direction (reverse drag direction for slingshot physics) var aimDx = self.aimStartX - self.aimEndX; var aimDy = self.aimStartY - self.aimEndY; var aimDistance = Math.sqrt(aimDx * aimDx + aimDy * aimDy); // Only shoot if player dragged far enough (minimum 50 pixels) if (aimDistance > 50) { // Find target enemy in aim direction var targetEnemy = null; var bestScore = -1; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var enemyDx = enemy.x - self.x; var enemyDy = enemy.y - self.y; var enemyDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy); // Check if enemy is within range if (enemyDistance <= self.range) { // Calculate angle between aim direction and enemy direction var aimAngle = Math.atan2(aimDy, aimDx); var enemyAngle = Math.atan2(enemyDy, enemyDx); var angleDiff = Math.abs(aimAngle - enemyAngle); // Normalize angle difference to 0-π range if (angleDiff > Math.PI) { angleDiff = 2 * Math.PI - angleDiff; } // Score based on angle accuracy and distance (closer and more accurate = higher score) var angleScore = Math.PI - angleDiff; // Higher score for smaller angle diff var distanceScore = self.range - enemyDistance; // Higher score for closer enemies var totalScore = angleScore * 2 + distanceScore * 0.5; // Accept targets within 45 degrees of aim direction if (angleDiff < Math.PI / 4 && totalScore > bestScore) { bestScore = totalScore; targetEnemy = enemy; } } } // Shoot at target enemy if found if (targetEnemy) { // Add pullback effect before firing tween(self, { scaleX: 0.8, scaleY: 1.1, y: self.y + 20 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Snap forward and create bullet tween(self, { scaleX: 1.0, scaleY: 1.0, y: self.y - 20 }, { duration: 100, easing: tween.easeIn }); // Create bullet projectile with larger size and distinctive appearance var bullet = game.addChild(LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y - 100, scaleX: 2.0, // Make projectile larger scaleY: 2.0, // Make projectile larger tint: 0xFF6B00 // Bright orange color for better visibility })); // Add pulsing effect to make projectile more visible tween(bullet, { scaleX: 2.5, scaleY: 2.5 }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(bullet, { scaleX: 2.0, scaleY: 2.0 }, { duration: 150, easing: tween.easeInOut }); } }); // Calculate trajectory to target var dx = targetEnemy.x - self.x; var dy = targetEnemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Animate bullet to target with arc trajectory tween(bullet, { x: targetEnemy.x, y: targetEnemy.y }, { duration: distance / 3, // Bullet speed easing: tween.easeOut, // Arc-like trajectory onFinish: function onFinish() { bullet.destroy(); // Deal heavy damage to target if (targetEnemy && targetEnemy.parent) { targetEnemy.health -= self.damage; // Flash target to show hit tween(targetEnemy, { tint: 0xFFFF00 // Yellow flash }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(targetEnemy, { tint: 0xFFFFFF }, { duration: 200, easing: tween.easeOut }); } }); if (targetEnemy.health <= 0) { targetEnemy.defeat(); } } } }); } }); } } // No aiming guide to clean up // Reset aiming state self.isAiming = false; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 //Init game with black background }); /**** * Game Code ****/ // Fire tower already uses 'Fireworks' asset - goal already achieved var enemies = []; var towers = []; var waveActive = false; var enemySpawnTimer = 0; var enemySpawnInterval = 420; // Spawn every 7 seconds at 60fps var enemiesSpawned = 0; var maxEnemiesInWave = 10; var guidelineYPositions = []; var firstEnemySpawned = false; var currentWave = 1; var enemiesDefeated = 0; var playerScore = 0; var playerLives = 10; var playerCash = 50; var scoreDisplay; var livesDisplay; var cashDisplay; var placingTower = false; var towerPreview = null; var confirmButton = null; var cancelButton = null; var pendingTowerCost = 10; var verticalGuidelines = []; var snapPositions = []; var introPlayed = false; var gameStarted = false; // Flag to track if game has started var gameSpeedMultiplier = 1; // Initialize game speed multiplier // Create intro cutscene using intro2 asset var introCutscene = new Cutscene(); // Add intro2 scene introCutscene.addScene({ onStart: function onStart() { // Create intro2 asset var intro2Asset = game.addChild(LK.getAsset('Intro2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Create cell asset in middle of intro2 var cellAsset = game.addChild(LK.getAsset('cell', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Store references for cleanup introCutscene.intro2Asset = intro2Asset; introCutscene.cellAsset = cellAsset; // Fade in intro2 tween(intro2Asset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut }); // Fade in cell asset tween(cellAsset, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // After cell asset fades in, wait 5 seconds then transition to story1 LK.setTimeout(function () { // Fade out intro2 and cell assets tween(intro2Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut }); tween(cellAsset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Create and fade in story1 scene 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: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Store story1 reference for cleanup introCutscene.story1Asset = story1Asset; // Auto-transition to story2 after 3 seconds LK.setTimeout(function () { tween(story1Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story1Asset.destroy(); // Create and fade in story2 scene var story2Asset = game.addChild(LK.getAsset('Story2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); tween(story2Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { introCutscene.story2Asset = story2Asset; // Auto-transition to story3 after 3 seconds LK.setTimeout(function () { tween(story2Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story2Asset.destroy(); // Create and fade in story3 scene var story3Asset = game.addChild(LK.getAsset('Story3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); tween(story3Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { introCutscene.story3Asset = story3Asset; // Auto-transition to story4 after 3 seconds LK.setTimeout(function () { tween(story3Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story3Asset.destroy(); // Create and fade in story4 scene var story4Asset = game.addChild(LK.getAsset('Story4', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); tween(story4Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { introCutscene.story4Asset = story4Asset; // After story4, wait 3 seconds then finish cutscene LK.setTimeout(function () { tween(story4Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story4Asset.destroy(); // Cutscene complete - continue to title screen showTitleScreen(); } }); }, 3000); } }); } }); }, 3000); } }); } }); }, 3000); } }); } }); }, 3000); } }); } }); }, 5000); } }); // Add skip intro button var skipIntroButton = new Text2('SKIP INTRO', { size: 60, fill: 0xFFFFFF }); skipIntroButton.anchor.set(0.5, 0); skipIntroButton.x = 2048 / 2; skipIntroButton.y = 200; skipIntroButton.alpha = 0; game.addChild(skipIntroButton); // Store reference for cleanup introCutscene.skipIntroButton = skipIntroButton; // Fade in skip button tween(skipIntroButton, { alpha: 1 }, { duration: 1000, easing: tween.easeInOut }); // Add click event handler to skip button skipIntroButton.down = function (x, y, obj) { // Don't allow skip if game has already started if (gameStarted) return; // Play intro music LK.playMusic('Intro', { loop: false }); // Clean up intro2 assets tween.stop(intro2Asset); tween.stop(cellAsset); tween.stop(skipIntroButton); intro2Asset.destroy(); cellAsset.destroy(); skipIntroButton.destroy(); // Skip directly to title screen showTitleScreen(); }; // Cell asset now has no click handler to prevent story replay } }); // Set what happens when cutscene finishes introCutscene.onFinish = function () { // Cutscene finished - no automatic transition }; // Function to skip directly to backdrop scene function skipToBackdrop() { // 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.2, scaleY: 0.2 })); // Zoom flyby to final size tween(backdropFlyby, { scaleX: 0.8, scaleY: 0.8 }, { duration: 1000, 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 })); // Ensure wave1 appears in front of all game objects wave1Asset.zIndex = 1000; // 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 tower defense wave startWave1(); } }); }, 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 += 525; // Move guideline 1 down by 525 pixels (was 505) } else if (i === 2) { yPosition += 395; // Move guideline 2 down by 395 pixels (was 420) } else if (i === 3) { yPosition += 225; // Move guideline 3 down by 225 pixels (was 250) } else if (i === 4) { yPosition += 95; // Move guideline 4 down by 95 pixels (was 120) } else if (i === 5) { yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75) } else if (i === 6) { yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250) } var guideLine = game.addChild(LK.getAsset('guideLine', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: yPosition, alpha: 0.5 })); } } // Function to show title screen function showTitleScreen() { // Don't show title screen if game has already started if (gameStarted) return; // Display the title // 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); // 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 })); // Tween alpha from 0 to 1 for fade-in effect tween(introAsset, { alpha: 1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // 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 }); // 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: 0, scaleX: 0.2, scaleY: 0.2 })); // 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 })); // Ensure wave1 appears in front of all game objects wave1Asset.zIndex = 1000; // 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 tower defense wave startWave1(); } }); }, 2000); } }); } }); } // Fade in backdrop tween(backdropAsset, { alpha: 1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Fade in backdrop flyby and zoom to position tween(backdropFlyby, { alpha: 1, scaleX: 0.8, scaleY: 0.8 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Start flyby movement to right side after fade 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 += 525; // Move guideline 1 down by 525 pixels (was 505) } else if (i === 2) { yPosition += 395; // Move guideline 2 down by 395 pixels (was 420) } else if (i === 3) { yPosition += 225; // Move guideline 3 down by 225 pixels (was 250) } else if (i === 4) { yPosition += 95; // Move guideline 4 down by 95 pixels (was 120) } else if (i === 5) { yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75) } else if (i === 6) { yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250) } var guideLine = game.addChild(LK.getAsset('guideLine', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: yPosition, alpha: 0.5 })); } } }); } }); } }); }, 5000); } }); } // Calculate guideline Y positions for enemy spawning function calculateGuidelinePositions() { var lineSpacing = 2732 / 7; guidelineYPositions = []; for (var i = 1; i <= 6; i++) { var yPosition = lineSpacing * i; if (i === 1) { yPosition += 525; // Move guideline 1 down by 525 pixels (was 505) } else if (i === 2) { yPosition += 395; // Move guideline 2 down by 395 pixels (was 420) } else if (i === 3) { yPosition += 225; // Move guideline 3 down by 225 pixels (was 250) } else if (i === 4) { yPosition += 95; // Move guideline 4 down by 95 pixels (was 120) } else if (i === 5) { yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75) } else if (i === 6) { yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250) } guidelineYPositions.push(yPosition); } } // Calculate snap positions from guideline intersections function calculateSnapPositions() { snapPositions = []; // Create vertical guideline positions (8 columns across screen width) var verticalSpacing = 2048 / 9; var verticalXPositions = []; for (var i = 1; i <= 8; i++) { var xPosition = verticalSpacing * i; verticalXPositions.push(xPosition); } // Create snap positions at intersections of horizontal and vertical guidelines for (var h = 0; h < guidelineYPositions.length; h++) { for (var v = 0; v < verticalXPositions.length; v++) { snapPositions.push({ x: verticalXPositions[v], y: guidelineYPositions[h] }); } } } // Find nearest snap position to given coordinates function findNearestSnapPosition(x, y) { var nearestPosition = { x: x, y: y }; var nearestDistance = Infinity; for (var i = 0; i < snapPositions.length; i++) { var snap = snapPositions[i]; var dx = snap.x - x; var dy = snap.y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < nearestDistance) { nearestDistance = distance; nearestPosition = snap; } } return nearestPosition; } // Show vertical guidelines during tower placement function showVerticalGuidelines() { hideVerticalGuidelines(); // Clear any existing guidelines var verticalSpacing = 2048 / 9; for (var i = 1; i <= 8; i++) { var xPosition = verticalSpacing * i; var yPosition = 2732 / 2; if (i === 1) { yPosition += 50; // Move vertical guideline 1 down by 50 pixels } var guideLine = game.addChild(LK.getAsset('verticalGuideLine', { anchorX: 0.5, anchorY: 0.5, x: xPosition, y: yPosition, alpha: 0.3 })); verticalGuidelines.push(guideLine); } } // Hide vertical guidelines function hideVerticalGuidelines() { for (var i = 0; i < verticalGuidelines.length; i++) { verticalGuidelines[i].destroy(); } verticalGuidelines = []; } // Start first wave of tower defense function startWave1() { calculateGuidelinePositions(); waveActive = true; gameStarted = true; // Mark that the game has officially started enemySpawnTimer = 0; enemiesSpawned = 0; currentWave = 1; enemiesDefeated = 0; // Add score display at top left scoreDisplay = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreDisplay.anchor.set(0, 0); scoreDisplay.x = 400; scoreDisplay.y = 50; LK.gui.addChild(scoreDisplay); // Add lives display at top center livesDisplay = new Text2('Lives: 10', { size: 60, fill: 0xFFFFFF }); livesDisplay.anchor.set(0.5, 0); livesDisplay.x = 1024; livesDisplay.y = 50; LK.gui.addChild(livesDisplay); // Add cash display under lives display cashDisplay = new Text2('Cash: $50', { size: 60, fill: 0xFFFFFF }); cashDisplay.anchor.set(0.5, 0); cashDisplay.x = 1024; cashDisplay.y = 120; LK.gui.addChild(cashDisplay); // Add speed up X2 button under cash display var speedUpButton = new Text2('SPEED X1', { size: 50, fill: 0x00FF00 }); speedUpButton.anchor.set(0.5, 0); speedUpButton.x = 1024; speedUpButton.y = 190; LK.gui.addChild(speedUpButton); // Add click handler for speed up button speedUpButton.down = function (x, y, obj) { if (gameSpeedMultiplier === 1) { gameSpeedMultiplier = 2; speedUpButton.setText('SPEED X2'); speedUpButton.tint = 0xFFFF00; // Yellow when active } else { gameSpeedMultiplier = 1; speedUpButton.setText('SPEED X1'); speedUpButton.tint = 0x00FF00; // Green when normal } }; // Add skip wave button under score display var skipWaveButton = new Text2('SKIP WAVE', { size: 40, fill: 0xFFFF00 }); skipWaveButton.anchor.set(0, 0); skipWaveButton.x = 400; skipWaveButton.y = 120; LK.gui.addChild(skipWaveButton); // Add global click handler to cancel tower placement game.down = function (x, y, obj) { // Cancel tower placement if clicking on empty space if (placingTower && obj === game) { if (towerPreview) { // Clean up blade preview if it exists if (towerPreview.bladePreview) { towerPreview.bladePreview.destroy(); } towerPreview.destroy(); towerPreview = null; } if (confirmButton) { confirmButton.destroy(); confirmButton = null; } hideVerticalGuidelines(); placingTower = false; } }; // Add tower purchase buttons at bottom of screen var towerButtonY = 2400; // Position near bottom of screen var buttonSpacing = 50; // 50 pixel spacing between buttons var startX = 430; // Water Tower Button var waterTowerButton = game.addChild(LK.getAsset('Waterbutton', { anchorX: 0.5, anchorY: 0.5, x: startX, y: towerButtonY, scaleX: 0.8, scaleY: 0.8 })); // Gas Tower Button var gasTowerButton = game.addChild(LK.getAsset('Gasbutton', { anchorX: 0.5, anchorY: 0.5, x: startX + waterTowerButton.width * 0.8 + buttonSpacing, y: towerButtonY, scaleX: 0.8, scaleY: 0.8 })); // Air Tower Button var basicTowerButton = game.addChild(LK.getAsset('Airbutton', { anchorX: 0.5, anchorY: 0.5, x: startX + waterTowerButton.width * 0.8 + buttonSpacing + gasTowerButton.width * 0.8 + buttonSpacing, y: towerButtonY, scaleX: 0.8, scaleY: 0.8 })); // Electric Tower Button var electricTowerButton = game.addChild(LK.getAsset('Electricbutton', { anchorX: 0.5, anchorY: 0.5, x: startX, y: towerButtonY + 150 + buttonSpacing, scaleX: 0.8, scaleY: 0.8 })); // Advanced Tower Button var advancedTowerButton = game.addChild(LK.getAsset('Plasmabutton', { anchorX: 0.5, anchorY: 0.5, x: startX + electricTowerButton.width * 0.8 + buttonSpacing, y: towerButtonY + 150 + buttonSpacing, scaleX: 0.8, scaleY: 0.8 })); // Slingshot Tower Button var slingshotTowerButton = game.addChild(LK.getAsset('Slingshotbutton', { anchorX: 0.5, anchorY: 0.5, x: startX + basicTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing, y: towerButtonY + 150 + buttonSpacing, scaleX: 0.8, scaleY: 0.8 })); // Fire Tower Button (above WiFi button) var fireButton = game.addChild(LK.getAsset('Firebutton', { anchorX: 0.5, anchorY: 0.5, x: startX + basicTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing + slingshotTowerButton.width * 0.8 + buttonSpacing, y: towerButtonY + 150 + buttonSpacing - 150 - buttonSpacing, scaleX: 0.8, scaleY: 0.8 })); // WiFi Tower Button var wifiTowerButton = game.addChild(LK.getAsset('Wifibutton', { anchorX: 0.5, anchorY: 0.5, x: startX + basicTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing + slingshotTowerButton.width * 0.8 + buttonSpacing, y: towerButtonY + 150 + buttonSpacing, scaleX: 0.8, scaleY: 0.8 })); // Add click handlers for tower buttons fireButton.down = function (x, y, obj) { if (playerCash >= 40 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for fire tower placingTower = true; pendingTowerCost = 40; // Fire tower costs $40 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Fireworks asset towerPreview = game.addChild(LK.getAsset('Fireworks', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Fireworks asset var newTower = new Tower(); newTower.setAssetType('Fireworks'); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'fire'; // Set fire tower properties newTower.damage = 40; newTower.range = 400; newTower.fireRate = 50; // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; advancedTowerButton.down = function (x, y, obj) { if (playerCash >= 60 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for plasma tower placingTower = true; pendingTowerCost = 60; // Plasma tower costs $60 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Plasmaball asset towerPreview = game.addChild(LK.getAsset('Plasmaball', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Plasmaball asset var newTower = new Tower(); newTower.setAssetType('Plasmaball'); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'plasma'; // Set plasma tower properties newTower.damage = 80; newTower.range = 400; newTower.fireRate = 60; // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; electricTowerButton.down = function (x, y, obj) { if (playerCash >= 50 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for electric tower placingTower = true; pendingTowerCost = 50; // Electric tower costs $50 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Bugzapper asset towerPreview = game.addChild(LK.getAsset('Bugzapper', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Bugzapper asset var newTower = new Tower(); newTower.setAssetType('Bugzapper'); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'electric'; // Set electric tower properties newTower.damage = 60; newTower.range = 350; newTower.fireRate = 40; // Faster shooting // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; waterTowerButton.down = function (x, y, obj) { if (playerCash >= pendingTowerCost && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode placingTower = true; // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Tower1 asset towerPreview = game.addChild(LK.getAsset('Tower1', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Tower1 asset var newTower = new Tower(); var towerGraphics = newTower.attachAsset('Tower1', { anchorX: 0.5, anchorY: 0.5 }); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.isTower1 = true; // Mark as Tower1 newTower.range = 650; // Extended water tower range from 500 to 650 pixels newTower.damage = 35; // Increase water tower damage from default 25 to 35 // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; gasTowerButton.down = function (x, y, obj) { if (playerCash >= 20 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for dog tower placingTower = true; pendingTowerCost = 20; // Dog tower costs $20 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Dog asset towerPreview = game.addChild(LK.getAsset('Dog', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Dog asset var newTower = new Tower(); newTower.setAssetType('Dog'); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'dog'; // Set dog tower properties newTower.damage = 30; newTower.range = 600; // Gas tower has 600 range straight ahead newTower.fireRate = 45; // Faster than basic towers // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; wifiTowerButton.down = function (x, y, obj) { if (playerCash >= 150 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for wifi tower placingTower = true; pendingTowerCost = 150; // WiFi tower costs $150 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Wifitower asset towerPreview = game.addChild(LK.getAsset('Wifitower', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Wifitower asset var newTower = new Tower(); newTower.setAssetType('Wifitower'); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'wifi'; // Set wifi tower properties newTower.damage = 100; // High damage bullets newTower.range = 600; // 600 pixel range newTower.fireRate = 30; // Fast shooting rate // Initialize wifi tower specific properties newTower.lastShotFrame = 0; // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; slingshotTowerButton.down = function (x, y, obj) { if (playerCash >= 100 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for slingshot tower placingTower = true; pendingTowerCost = 100; // Slingshot tower costs $100 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Slingshot asset towerPreview = game.addChild(LK.getAsset('Slingshot', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Slingshot asset var newTower = new Tower(); newTower.setAssetType('Slingshot'); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'slingshot'; // Set slingshot tower properties newTower.damage = 150; // Heavy damage newTower.range = 1200; // Increased range to 1200 pixels newTower.fireRate = 120; // Slower shooting rate // Initialize slingshot specific properties newTower.isAiming = false; newTower.aimStartX = 0; newTower.aimStartY = 0; newTower.aimEndX = 0; newTower.aimEndY = 0; // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; basicTowerButton.down = function (x, y, obj) { if (playerCash >= 30 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for air tower placingTower = true; pendingTowerCost = 30; // Air tower costs $30 // Calculate snap positions and show guidelines calculateSnapPositions(); showVerticalGuidelines(); // Find initial snap position var snapPos = findNearestSnapPosition(x, y); // Create tower preview using Fan asset towerPreview = game.addChild(LK.getAsset('Fan', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); // Create blade preview on top of fan var bladePreview = game.addChild(LK.getAsset('Blades', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y - 70, alpha: 0.7 })); // Store blade preview reference on tower preview towerPreview.bladePreview = bladePreview; // Create green tick confirmation button confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 })); // Create red cancel button under green confirm button cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 80 + 25 + 30 // Position under confirm button })); // Add click handler to cancel button cancelButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Clean up placement mode towerPreview.destroy(); if (towerPreview.bladePreview) { towerPreview.bladePreview.destroy(); } confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; } }; // Add click handler to confirm button confirmButton.down = function (cx, cy, cobj) { if (placingTower && towerPreview) { // Deduct cash playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Create actual tower at preview position using Fan asset var newTower = new Tower(); var fanGraphics = newTower.attachAsset('Fan', { anchorX: 0.5, anchorY: 0.5 }); var bladeGraphics = newTower.attachAsset('Blades', { anchorX: 0.5, anchorY: 0.5, y: -70 }); // Store blade reference for rotation newTower.bladeGraphics = bladeGraphics; newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; newTower.towerType = 'air'; // Set air tower properties newTower.damage = 35; newTower.range = 350; newTower.fireRate = 30; // Faster shooting // Assign tower to nearest guideline var nearestGuidelineIndex = 0; var nearestDistance = Infinity; for (var g = 0; g < guidelineYPositions.length; g++) { var distance = Math.abs(newTower.y - guidelineYPositions[g]); if (distance < nearestDistance) { nearestDistance = distance; nearestGuidelineIndex = g; } } newTower.guidelineIndex = nearestGuidelineIndex; game.addChild(newTower); towers.push(newTower); // Clean up placement mode towerPreview.destroy(); if (towerPreview.bladePreview) { towerPreview.bladePreview.destroy(); } confirmButton.destroy(); cancelButton.destroy(); towerPreview = null; confirmButton = null; hideVerticalGuidelines(); placingTower = false; // Reset pending tower cost pendingTowerCost = 10; // Play confirmation sound LK.getSound('Confirm').play(); } }; } }; // Add click handler to skip wave button skipWaveButton.down = function (x, y, obj) { // Force wave completion by clearing all enemies and setting appropriate flags for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); enemies.splice(i, 1); } enemiesSpawned = maxEnemiesInWave; enemiesDefeated = maxEnemiesInWave; // Check which wave we're skipping and trigger appropriate transition if (currentWave === 1) { // Show wave2 asset with fade in/out var wave2Asset = game.addChild(LK.getAsset('Wave2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave2 appears in front of all game objects wave2Asset.zIndex = 1000; // Fade in wave2 asset tween(wave2Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out wave2 asset tween(wave2Asset, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { wave2Asset.destroy(); // Start wave2 startWave2(); } }); } }); } else if (currentWave === 2) { // Show wave3 asset with fade in/out var wave3Asset = game.addChild(LK.getAsset('Wave3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave3 appears in front of all game objects wave3Asset.zIndex = 1000; // Fade in wave3 asset tween(wave3Asset, { alpha: 1 }, { duration: 750, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave3 visible for 1.5 seconds then fade out LK.setTimeout(function () { tween(wave3Asset, { alpha: 0 }, { duration: 750, easing: tween.easeInOut, onFinish: function onFinish() { wave3Asset.destroy(); // Start wave3 startWave3(); } }); }, 1500); } }); } else if (currentWave === 3) { // Show wave4 asset with fade in/out var wave4Asset = game.addChild(LK.getAsset('Wave4', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave4 appears in front of all game objects wave4Asset.zIndex = 1000; // Fade in wave4 asset tween(wave4Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out wave4 asset tween(wave4Asset, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { wave4Asset.destroy(); // Start wave4 startWave4(); } }); } }); } else if (currentWave === 4) { // Show wave5 asset with fade in/out var wave5Asset = game.addChild(LK.getAsset('Wave5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave5 appears in front of all game objects wave5Asset.zIndex = 1000; // Fade in wave5 asset tween(wave5Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out wave5 asset tween(wave5Asset, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { wave5Asset.destroy(); // Start wave5 startWave5(); } }); } }); } else if (currentWave === 5) { // UFO boss defeated - transition to story5 cutscene var story5Asset = game.addChild(LK.getAsset('Story5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in story5 asset tween(story5Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Add click handler to story5 to transition to level2 story5Asset.down = function (x, y, obj) { // Fade out story5 asset tween(story5Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story5Asset.destroy(); // Start level2 with backdrop2 startLevel2(); } }); }; } }); } else { waveActive = false; } }; } // Start second wave of tower defense function startWave2() { calculateGuidelinePositions(); waveActive = true; enemySpawnTimer = 0; enemiesSpawned = 0; maxEnemiesInWave = 20; enemySpawnInterval = 300; // Spawn every 5 seconds at 60fps currentWave = 2; } // Start third wave of tower defense function startWave3() { calculateGuidelinePositions(); waveActive = true; enemySpawnTimer = 0; enemiesSpawned = 0; maxEnemiesInWave = 30; enemySpawnInterval = 300; // Spawn every 5 seconds at 60fps (300 frames = 5 seconds at 60fps) currentWave = 3; } // Start fourth wave of tower defense function startWave4() { calculateGuidelinePositions(); waveActive = true; enemySpawnTimer = 0; enemiesSpawned = 0; maxEnemiesInWave = 40; enemySpawnInterval = 180; // Spawn every 3 seconds at 60fps (180 frames = 3 seconds at 60fps) currentWave = 4; } // Start fifth wave of tower defense (boss stage) function startWave5() { calculateGuidelinePositions(); waveActive = true; enemySpawnTimer = 0; enemiesSpawned = 0; maxEnemiesInWave = 1; // Only 1 boss enemy enemySpawnInterval = 60; // Spawn immediately currentWave = 5; } // Spawn enemy on random guideline function spawnEnemy() { if (guidelineYPositions.length === 0) return; // Pick random guideline with better distribution var randomIndex = Math.floor(Math.random() * guidelineYPositions.length); var spawnY = guidelineYPositions[randomIndex]; // Add some vertical variation to make movement less predictable var verticalVariation = (Math.random() - 0.5) * 60; // +/- 30 pixels variation spawnY += verticalVariation; // Create new enemy var enemy = new Enemy(); // Set asset type and health based on current wave if (currentWave === 1) { // Probedroids in wave 1 - make them slightly stronger enemy.health = 150; enemy.maxHealth = 150; } else if (currentWave === 2) { enemy.setAssetType('Drone'); enemy.health = 200; enemy.maxHealth = 200; enemy.speed = 2; // Reduced drone speed } else if (currentWave === 3) { enemy.setAssetType('SpaceDrone'); enemy.health = 250; enemy.maxHealth = 250; enemy.speed = 2.5; // Reduced SpaceDrone speed } else if (currentWave === 4) { enemy.setAssetType('Robot'); enemy.health = 350; enemy.maxHealth = 350; enemy.speed = 4; // Make robots slightly faster } else if (currentWave === 5) { enemy.setAssetType('UFO'); enemy.health = 5000; enemy.maxHealth = 5000; // UFO boss starts at top center of screen enemy.x = 2048 / 2; enemy.y = -200; // Start above screen enemy.baseY = 2732 / 2; // Target center of screen // Assign UFO boss to special guideline index -1 to indicate it can be targeted by all towers enemy.guidelineIndex = -1; // Add UFO to game before early return enemies.push(enemy); game.addChild(enemy); return; // Skip normal positioning for UFO boss } enemy.x = 2048 + 100; // Start off right side of screen enemy.y = spawnY; // Store which guideline this enemy is following for reference enemy.guidelineIndex = randomIndex; enemy.baseY = guidelineYPositions[randomIndex]; enemies.push(enemy); game.addChild(enemy); // Play probedroid sound when first enemy appears if (!firstEnemySpawned) { LK.getSound('Probedroid').play(); firstEnemySpawned = true; } // Play probedroid2 sound when first robot appears in wave4 if (currentWave === 4 && enemiesSpawned === 0) { LK.getSound('Probedroid2').play(); } } // Add tower placement move handler game.move = function (x, y, obj) { if (placingTower && towerPreview) { // Find nearest snap position var snapPos = findNearestSnapPosition(x, y); towerPreview.x = snapPos.x; towerPreview.y = snapPos.y; // Update blade preview position if it exists if (towerPreview.bladePreview) { towerPreview.bladePreview.x = snapPos.x; towerPreview.bladePreview.y = snapPos.y - 70; } if (confirmButton) { confirmButton.x = snapPos.x + 60; confirmButton.y = snapPos.y - 60; } // Update cancel button position if it exists if (typeof cancelButton !== 'undefined' && cancelButton) { cancelButton.x = snapPos.x + 60; cancelButton.y = snapPos.y - 60 + 80 + 25 + 30; } } // Handle slingshot aiming updates for (var t = 0; t < towers.length; t++) { var tower = towers[t]; if (tower.towerType === 'slingshot' && tower.isAiming) { // Convert to global coordinates for consistent aiming var globalPos = tower.toGlobal({ x: 0, y: 0 }); var gamePos = game.toLocal(globalPos); tower.aimEndX = x; tower.aimEndY = y; } } }; // Function to sort game objects by Y position (objects with higher Y appear in front) function sortGameObjectsByY() { // Collect all towers and enemies with their Y positions var allObjects = []; // Add towers to sorting array for (var i = 0; i < towers.length; i++) { if (towers[i].parent) { allObjects.push({ object: towers[i], y: towers[i].y, type: 'tower' }); } } // Add enemies to sorting array for (var i = 0; i < enemies.length; i++) { if (enemies[i].parent) { allObjects.push({ object: enemies[i], y: enemies[i].y, type: 'enemy' }); } } // Sort by Y position (lower Y values first, higher Y values last) // Objects with higher Y values will be rendered in front allObjects.sort(function (a, b) { return a.y - b.y; }); // Set zIndex based on sorted order to ensure proper depth for (var i = 0; i < allObjects.length; i++) { var obj = allObjects[i].object; if (obj.parent) { // Set zIndex based on position in sorted array // Higher index = rendered in front obj.zIndex = i; } } } // Update tower defense game game.update = function () { // Sort game objects by Y position for proper depth ordering // This ensures enemies and towers below others are rendered in front sortGameObjectsByY(); // Only run tower defense logic when wave is active if (!waveActive) return; // Spawn enemies if (enemiesSpawned < maxEnemiesInWave) { enemySpawnTimer += gameSpeedMultiplier; if (enemySpawnTimer >= enemySpawnInterval) { spawnEnemy(); enemiesSpawned++; enemySpawnTimer = 0; } } // Check if wave is complete if (enemiesSpawned >= maxEnemiesInWave && enemies.length === 0) { waveActive = false; // Award player $50 for completing wave playerCash += 50; if (cashDisplay) { cashDisplay.setText('Cash: $' + playerCash); } // Wave complete - transition to next wave if (currentWave === 1) { // Show wave2 asset with fade in/out var wave2Asset = game.addChild(LK.getAsset('Wave2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave2 appears in front of all game objects wave2Asset.zIndex = 1000; // Fade in wave2 asset tween(wave2Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out wave2 asset tween(wave2Asset, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { wave2Asset.destroy(); // Start wave2 startWave2(); } }); } }); } else if (currentWave === 2) { // Show wave3 asset with fade in/out var wave3Asset = game.addChild(LK.getAsset('Wave3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave3 appears in front of all game objects wave3Asset.zIndex = 1000; // Fade in wave3 asset tween(wave3Asset, { alpha: 1 }, { duration: 750, easing: tween.easeInOut, onFinish: function onFinish() { // Keep wave3 visible for 1.5 seconds then fade out LK.setTimeout(function () { tween(wave3Asset, { alpha: 0 }, { duration: 750, easing: tween.easeInOut, onFinish: function onFinish() { wave3Asset.destroy(); // Start wave3 startWave3(); } }); }, 1500); } }); } else if (currentWave === 3) { // Show wave4 asset with fade in/out var wave4Asset = game.addChild(LK.getAsset('Wave4', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave4 appears in front of all game objects wave4Asset.zIndex = 1000; // Fade in wave4 asset tween(wave4Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out wave4 asset tween(wave4Asset, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { wave4Asset.destroy(); // Start wave4 startWave4(); } }); } }); } else if (currentWave === 4) { // Show wave5 asset with fade in/out var wave5Asset = game.addChild(LK.getAsset('Wave5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Ensure wave5 appears in front of all game objects wave5Asset.zIndex = 1000; // Fade in wave5 asset tween(wave5Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Fade out wave5 asset tween(wave5Asset, { alpha: 0 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { wave5Asset.destroy(); // Start wave5 startWave5(); } }); } }); } else if (currentWave === 5) { // UFO boss defeated - transition to story5 cutscene var story5Asset = game.addChild(LK.getAsset('Story5', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 })); // Fade in story5 asset tween(story5Asset, { alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Add click handler to story5 to transition to level2 story5Asset.down = function (x, y, obj) { // Fade out story5 asset tween(story5Asset, { alpha: 0 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { story5Asset.destroy(); // Start level2 with backdrop2 startLevel2(); } }); }; } }); } } }; // Start the intro cutscene only if it hasn't been played yet and wave hasn't started if (!introPlayed && !waveActive && currentWave === 1 && enemiesSpawned === 0) { introPlayed = true; introCutscene.play(); } else if (!waveActive && currentWave === 1 && enemiesSpawned === 0 && introPlayed) { // If intro has been played, skip directly to backdrop scene skipToBackdrop(); } ; ; // Start level2 with backdrop2 function startLevel2() { // Clear any existing game elements for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].destroy(); enemies.splice(i, 1); } // Reset game variables for level2 waveActive = false; enemySpawnTimer = 0; enemiesSpawned = 0; maxEnemiesInWave = 10; enemySpawnInterval = 420; firstEnemySpawned = false; currentWave = 1; enemiesDefeated = 0; // Add backdrop2 asset as the new background var backdrop2Asset = game.addChild(LK.getAsset('Backdrop2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0, scaleX: 1.0, scaleY: 1.0 })); // Fade in backdrop2 tween(backdrop2Asset, { alpha: 1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Level2 is now ready - could start first wave or wait for player input // For now, level1 is officially over and level2 has begun with backdrop2 } }); // Recalculate guideline positions for level2 calculateGuidelinePositions(); // Add 6 horizontal guide lines for level2 var lineSpacing = 2732 / 7; for (var i = 1; i <= 6; i++) { var yPosition = lineSpacing * i; if (i === 1) { yPosition += 525; // Move guideline 1 down by 525 pixels (was 505) } else if (i === 2) { yPosition += 395; // Move guideline 2 down by 395 pixels (was 420) } else if (i === 3) { yPosition += 225; // Move guideline 3 down by 225 pixels (was 250) } else if (i === 4) { yPosition += 95; // Move guideline 4 down by 95 pixels (was 120) } else if (i === 5) { yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75) } else if (i === 6) { yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250) } var guideLine = game.addChild(LK.getAsset('guideLine', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: yPosition, alpha: 0.5 })); } }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Cutscene class
var Cutscene = Container.expand(function () {
var self = Container.call(this);
// Properties
self.scenes = [];
self.currentSceneIndex = 0;
self.isPlaying = false;
// Add a scene to the cutscene
self.addScene = function (sceneData) {
self.scenes.push(sceneData);
};
// Start playing the cutscene
self.play = function () {
if (self.scenes.length === 0) return;
self.isPlaying = true;
self.currentSceneIndex = 0;
self.playCurrentScene();
};
// Play the current scene
self.playCurrentScene = function () {
if (self.currentSceneIndex >= self.scenes.length) {
self.finish();
return;
}
var scene = self.scenes[self.currentSceneIndex];
if (scene.onStart) {
scene.onStart();
}
// Auto-advance to next scene after duration
if (scene.duration) {
LK.setTimeout(function () {
self.nextScene();
}, scene.duration);
}
};
// Move to next scene
self.nextScene = function () {
var scene = self.scenes[self.currentSceneIndex];
if (scene.onEnd) {
scene.onEnd();
}
self.currentSceneIndex++;
self.playCurrentScene();
};
// Skip to specific scene
self.skipToScene = function (index) {
if (index >= 0 && index < self.scenes.length) {
self.currentSceneIndex = index;
self.playCurrentScene();
}
};
// Finish cutscene
self.finish = function () {
self.isPlaying = false;
if (self.onFinish) {
self.onFinish();
}
};
// Stop cutscene
self.stop = function () {
self.isPlaying = false;
// Stop any running tweens or timeouts
tween.stopAll();
};
return self;
});
// Defense class
var Defense = Container.expand(function () {
var self = Container.call(this);
var defenseGraphics = self.attachAsset('defense', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
// Defense logic goes here
};
});
// Enemy class for tower defense
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Default to Probedroid, but can be changed
self.assetType = 'Probedroid';
var enemyGraphics = self.attachAsset(self.assetType, {
anchorX: 0.5,
anchorY: 0.5
});
// Enemy properties
self.health = 100;
self.maxHealth = 100;
self.speed = 1.5;
self.targetX = -100; // Move to left side of screen
self.lastX = self.x;
self.collidingWithTower = false; // Track if enemy is colliding with a tower
// Health bar graphics - positioned above enemy regardless of asset type
self.healthBarBackground = self.attachAsset('healthBarBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -80
});
self.healthBarForeground = self.attachAsset('healthBarForeground', {
anchorX: 0,
anchorY: 0.5,
x: -30,
y: -80
});
self.update = function () {
// Store last position for collision detection
self.lastX = self.x;
// Reset collision flag at start of each frame
self.collidingWithTower = false;
// Check collision with towers on same guideline that are directly to the left
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
// Only check towers on the same guideline that are positioned to the left of the enemy
if (tower.guidelineIndex === self.guidelineIndex && tower.x < self.x && self.intersects(tower)) {
self.collidingWithTower = true;
break;
}
}
// Move towards target (right to left) only if not colliding with tower
if (self.x > self.targetX && !self.collidingWithTower) {
self.x -= self.speed * (typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1);
}
// Move towards the center of the assigned guideline
if (self.baseY !== undefined) {
var targetY = self.baseY;
var yDifference = targetY - self.y;
// Move towards guideline center with a fraction of movement speed
if (Math.abs(yDifference) > 2) {
self.y += yDifference * 0.1; // Adjust 0.1 to control how quickly enemies center on guidelines
}
}
// Update health bar
var healthPercentage = self.health / self.maxHealth;
self.healthBarForeground.scaleX = healthPercentage;
// Change health bar color based on health percentage
if (healthPercentage > 0.6) {
self.healthBarForeground.tint = 0x00ff00; // Green
} else if (healthPercentage > 0.3) {
self.healthBarForeground.tint = 0xffff00; // Yellow
} else {
self.healthBarForeground.tint = 0xff0000; // Red
}
// SpaceDrone shooting logic
if (self.assetType === 'SpaceDrone') {
// Initialize shooting properties if not set
if (self.lastShotFrame === undefined) {
self.lastShotFrame = 0;
self.shootingRange = 400;
self.fireRate = 45; // Shoot every 0.75 seconds at 60fps
}
// Find nearest tower to shoot at
var nearestTower = null;
var nearestDistance = Infinity;
for (var t = 0; t < towers.length; t++) {
var tower = towers[t];
// Only target towers on the same guideline
if (tower.guidelineIndex === self.guidelineIndex) {
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if tower is within shooting range and closer than current target
if (distance <= self.shootingRange && distance < nearestDistance) {
nearestDistance = distance;
nearestTower = tower;
}
}
}
// Shoot at nearest tower if in range and fire rate allows
var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate;
if (nearestTower && LK.ticks - self.lastShotFrame >= adjustedFireRate) {
// Create red bullet
var bullet = game.addChild(LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
tint: 0xFF0000 // Red color for SpaceDrone bullets
}));
// Calculate direction to target tower
var dx = nearestTower.x - self.x;
var dy = nearestTower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Animate bullet to target tower
tween(bullet, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: distance / 3,
// Bullet speed
easing: tween.linear,
onFinish: function onFinish() {
bullet.destroy();
// Deal damage to target tower
if (nearestTower && nearestTower.parent) {
nearestTower.health -= 25; // SpaceDrone bullets deal 25 damage
// Flash tower red to show damage
tween(nearestTower, {
tint: 0xFF0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(nearestTower, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Check if tower is destroyed
if (nearestTower.health <= 0) {
// Create explosion effect at tower position
var explosionEffect = game.addChild(LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: nearestTower.x,
y: nearestTower.y,
alpha: 1,
scaleX: 0.5,
scaleY: 0.5
}));
// Play explosion sound
LK.getSound('Explosion').play();
// Animate explosion with scale and fade out
tween(explosionEffect, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Remove tower from towers array
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === nearestTower) {
towers.splice(k, 1);
break;
}
}
// Destroy tower
nearestTower.destroy();
}
}
}
});
self.lastShotFrame = LK.ticks;
}
}
// Robot shooting logic
if (self.assetType === 'Robot') {
// Rotate wheels when moving - slower anti-clockwise rotation
if (self.leftWheel && self.rightWheel && !self.collidingWithTower) {
self.leftWheel.rotation -= 0.15;
self.rightWheel.rotation -= 0.15;
}
// Initialize shooting properties if not set
if (self.lastShotFrame === undefined) {
self.lastShotFrame = 0;
self.shootingRange = 450;
self.fireRate = 60; // Shoot every 1 second at 60fps
}
// Find nearest tower to shoot at
var nearestTower = null;
var nearestDistance = Infinity;
for (var t = 0; t < towers.length; t++) {
var tower = towers[t];
// Only target towers on the same guideline
if (tower.guidelineIndex === self.guidelineIndex) {
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if tower is within shooting range and closer than current target
if (distance <= self.shootingRange && distance < nearestDistance) {
nearestDistance = distance;
nearestTower = tower;
}
}
}
// Shoot at nearest tower if in range and fire rate allows
var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate;
if (nearestTower && LK.ticks - self.lastShotFrame >= adjustedFireRate) {
// Create laserfire bullet using Laserfire asset
var laser = game.addChild(LK.getAsset('Laserfire', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
// Calculate direction to target tower
var dx = nearestTower.x - self.x;
var dy = nearestTower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Animate laser to target tower
tween(laser, {
x: nearestTower.x,
y: nearestTower.y
}, {
duration: distance / 4,
// Laser speed (faster than regular bullets)
easing: tween.linear,
onFinish: function onFinish() {
laser.destroy();
// Deal damage to target tower
if (nearestTower && nearestTower.parent) {
nearestTower.health -= 35; // Robot laserfire deals 35 damage
// Flash tower blue to show laser damage
tween(nearestTower, {
tint: 0x00FFFF
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(nearestTower, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Check if tower is destroyed
if (nearestTower.health <= 0) {
// Create explosion effect at tower position
var explosionEffect = game.addChild(LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: nearestTower.x,
y: nearestTower.y,
alpha: 1,
scaleX: 0.5,
scaleY: 0.5
}));
// Play explosion sound
LK.getSound('Explosion').play();
// Animate explosion with scale and fade out
tween(explosionEffect, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Remove tower from towers array
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === nearestTower) {
towers.splice(k, 1);
break;
}
}
// Destroy tower
nearestTower.destroy();
}
}
}
});
self.lastShotFrame = LK.ticks;
}
}
// UFO boss special behavior - simplified sequential abduction
if (self.isUFOBoss) {
// Initialize UFO properties if not set
if (!self.movementInitialized) {
self.movementInitialized = true;
self.currentTargetTower = null;
self.movingToTower = false;
self.abductionPhase = 'seeking'; // seeking, hovering, abducting, moving_to_next
}
// Phase 1: Hover down from top if not already done
if (!self.hasHoveredDown) {
if (self.y < 2732 / 2) {
self.y += self.speed * (typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1);
} else {
self.hasHoveredDown = true;
}
return;
}
// Phase 2: Sequential tower abduction
if (towers.length > 0) {
// Find next tower to abduct if we don't have a target
if (!self.currentTargetTower && self.abductionPhase === 'seeking') {
// Find closest tower
var closestTower = null;
var closestDistance = Infinity;
for (var t = 0; t < towers.length; t++) {
var tower = towers[t];
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestDistance = distance;
closestTower = tower;
}
}
if (closestTower) {
self.currentTargetTower = closestTower;
self.abductionPhase = 'moving_to_tower';
// Move to hover higher above the tower
tween(self, {
x: closestTower.x,
y: closestTower.y - 350 // Hover much higher above tower
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.abductionPhase = 'hovering';
// Start abduction after brief hover
LK.setTimeout(function () {
if (self.currentTargetTower && self.currentTargetTower.parent) {
self.abductionPhase = 'abducting';
// Create tractor beam that starts small and expands to engulf tower
self.tractorBeam = game.addChild(LK.getAsset('Blowing', {
anchorX: 0.5,
anchorY: 0.0,
x: self.currentTargetTower.x,
y: self.y + 50,
alpha: 0,
scaleX: 0.1,
scaleY: 0.5,
tint: 0x00FF00 // Green tractor beam
}));
// Animate tractor beam expanding to engulf tower
tween(self.tractorBeam, {
alpha: 0.7,
scaleX: 1.5,
scaleY: 1.8
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Add pulsing effect to tractor beam
function pulseTractorBeam() {
if (self.tractorBeam && self.tractorBeam.parent) {
tween(self.tractorBeam, {
scaleX: 1.7,
scaleY: 2.0,
alpha: 0.9
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.tractorBeam && self.tractorBeam.parent) {
tween(self.tractorBeam, {
scaleX: 1.5,
scaleY: 1.8,
alpha: 0.7
}, {
duration: 400,
easing: tween.easeInOut
});
}
}
});
}
}
self.tractorBeamPulse = LK.setInterval(pulseTractorBeam, 800);
}
});
}
}, 1000);
}
});
}
}
// Keep UFO positioned over tractor beam and tower during abduction
if (self.abductionPhase === 'abducting' && self.currentTargetTower && self.currentTargetTower.parent) {
// Keep UFO directly above the tower being abducted
self.x = self.currentTargetTower.x;
// Update tractor beam position to stay connected between UFO and tower
if (self.tractorBeam && self.tractorBeam.parent) {
self.tractorBeam.x = self.currentTargetTower.x;
self.tractorBeam.y = self.y + 50; // Position at bottom of UFO
// Dynamically scale tractor beam height to reach tower
var beamHeight = Math.abs(self.currentTargetTower.y - (self.y + 50));
self.tractorBeam.scaleY = Math.max(0.5, beamHeight / 200);
}
}
// Phase 3: Abduction process with enhanced sucking effect
if (self.abductionPhase === 'abducting' && self.currentTargetTower && self.currentTargetTower.parent) {
var tower = self.currentTargetTower;
var targetY = self.y + 100; // Target position inside UFO
var dy = targetY - tower.y;
var distance = Math.abs(dy);
if (distance > 20) {
// Create sucking motion with acceleration
var liftSpeed = Math.min(8, 1.5 + (1 - distance / 400) * 6); // Accelerate as tower gets closer
tower.y += dy / distance * liftSpeed * (typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1);
// No spinning during lift - removed per request
// Gradually shrink tower with more dramatic effect
var scaleEffect = Math.max(0.1, distance / 300);
tower.scaleX = scaleEffect;
tower.scaleY = scaleEffect;
// Make tower slightly transparent as it gets sucked up
tower.alpha = Math.max(0.3, distance / 400);
// Keep tower centered in tractor beam
tower.x = self.currentTargetTower.x + Math.sin(LK.ticks * 0.1) * 10; // Add slight wobble
} else {
// Tower fully abducted - clean up and move to next
if (self.tractorBeamPulse) {
LK.clearInterval(self.tractorBeamPulse);
self.tractorBeamPulse = null;
}
if (self.tractorBeam && self.tractorBeam.parent) {
// Fade out tractor beam
tween(self.tractorBeam, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
if (self.tractorBeam && self.tractorBeam.parent) {
self.tractorBeam.destroy();
self.tractorBeam = null;
}
}
});
}
// Remove tower from game
for (var i = towers.length - 1; i >= 0; i--) {
if (towers[i] === tower) {
towers.splice(i, 1);
break;
}
}
tower.destroy();
self.currentTargetTower = null;
self.abductionPhase = 'seeking';
}
}
}
// Phase 4: Victory condition - head to goal when all towers abducted
if (towers.length === 0 && !self.headingToGoal) {
// Clean up any remaining tractor beam and intervals
if (self.tractorBeamPulse) {
LK.clearInterval(self.tractorBeamPulse);
self.tractorBeamPulse = null;
}
if (self.tractorBeam && self.tractorBeam.parent) {
self.tractorBeam.destroy();
self.tractorBeam = null;
}
self.headingToGoal = true;
tween(self, {
x: -200,
y: 2732 / 2
}, {
duration: 3000,
easing: tween.easeInOut
});
}
// Check if UFO reached goal
if (self.headingToGoal && self.lastX > 0 && self.x <= 0) {
playerLives = 0;
if (livesDisplay) {
livesDisplay.setText('Lives: ' + playerLives);
}
if (scoreDisplay && scoreDisplay.parent) {
scoreDisplay.destroy();
scoreDisplay = null;
}
if (livesDisplay && livesDisplay.parent) {
livesDisplay.destroy();
livesDisplay = null;
}
if (cashDisplay && cashDisplay.parent) {
cashDisplay.destroy();
cashDisplay = null;
}
LK.showGameOver();
return;
}
return; // Skip normal enemy logic for UFO boss
}
// Check if enemy reached the end
if (self.lastX > 0 && self.x <= 0) {
// Enemy reached the end - remove from game and reduce lives
if (self.parent) {
// Reduce player lives
playerLives--;
// Update lives display if it exists
if (livesDisplay) {
livesDisplay.setText('Lives: ' + playerLives);
}
// Check for game over
if (playerLives <= 0) {
// Remove user interface displays before showing game over
if (scoreDisplay && scoreDisplay.parent) {
scoreDisplay.destroy();
scoreDisplay = null;
}
if (livesDisplay && livesDisplay.parent) {
livesDisplay.destroy();
livesDisplay = null;
}
if (cashDisplay && cashDisplay.parent) {
cashDisplay.destroy();
cashDisplay = null;
}
LK.showGameOver();
}
self.destroy();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
}
};
// Method to defeat this enemy
self.defeat = function () {
if (self.parent) {
// Award player 50 points and $10 cash
playerScore += 50;
playerCash += 10;
// Update displays if they exist
if (scoreDisplay) {
scoreDisplay.setText('Score: ' + playerScore);
}
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create explosion effect at enemy position
var explosionEffect = game.addChild(LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 1,
scaleX: 0.5,
scaleY: 0.5
}));
// Play explosion sound
LK.getSound('Explosion').play();
// Animate explosion with scale and fade out
tween(explosionEffect, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
enemiesDefeated++;
// Special handling for UFO boss defeat
if (self.isUFOBoss) {
// Clean up tractor beam if it exists
if (self.tractorBeam && self.tractorBeam.parent) {
self.tractorBeam.destroy();
self.tractorBeam = null;
}
// UFO boss defeated - transition to story5 cutscene and then level2
var story5Asset = game.addChild(LK.getAsset('Story5', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Fade in story5 asset
tween(story5Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Add click handler to story5 to transition to level2
story5Asset.down = function (x, y, obj) {
// Fade out story5 asset
tween(story5Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story5Asset.destroy();
// Start level2 with backdrop2
startLevel2();
}
});
};
}
});
}
self.destroy();
// Remove from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
}
};
self.setAssetType = function (assetType) {
if (enemyGraphics && enemyGraphics.parent) {
enemyGraphics.destroy();
}
// Clean up existing wheel graphics if they exist
if (self.leftWheel && self.leftWheel.parent) {
self.leftWheel.destroy();
self.leftWheel = null;
}
if (self.rightWheel && self.rightWheel.parent) {
self.rightWheel.destroy();
self.rightWheel = null;
}
// Clean up existing roboarm graphics if they exist
if (self.roboarm && self.roboarm.parent) {
self.roboarm.destroy();
self.roboarm = null;
}
self.assetType = assetType;
enemyGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
// Add wheels and roboarm for Robot enemies
if (assetType === 'Robot') {
// Add left wheel
self.leftWheel = self.attachAsset('Wheel', {
anchorX: 0.5,
anchorY: 0.5,
x: -80,
y: 95,
scaleX: 0.6,
scaleY: 0.6
});
// Move wheel1 (leftWheel) left 25 pixels from its current position
tween(self.leftWheel, {
x: -5 // Move from -80 to -5 (75 pixels to the right total, 25 pixels left from original 20)
}, {
duration: 1000,
easing: tween.easeInOut
});
// Add right wheel
self.rightWheel = self.attachAsset('Wheel', {
anchorX: 0.5,
anchorY: 0.5,
x: 55,
// Move from 80 to 55 (25 pixels left)
y: 95,
scaleX: 0.6,
scaleY: 0.6
});
// Add roboarm attachment
self.roboarm = self.attachAsset('Roboarm', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -5,
scaleX: 0.5,
scaleY: 0.5
});
}
// UFO boss special properties
if (assetType === 'UFO') {
self.isUFOBoss = true;
self.abductionTarget = null;
self.isAbducting = false;
self.hasHoveredDown = false;
self.tractorBeam = null;
self.speed = 1; // Slower movement for boss
self.targetX = 0; // Move to goal at x=0
}
// Ensure health bars are brought to front after asset change
if (self.healthBarBackground && self.healthBarBackground.parent) {
self.removeChild(self.healthBarBackground);
self.addChild(self.healthBarBackground);
}
if (self.healthBarForeground && self.healthBarForeground.parent) {
self.removeChild(self.healthBarForeground);
self.addChild(self.healthBarForeground);
}
};
return self;
});
// Tower class for tower defense
var Tower = Container.expand(function () {
var self = Container.call(this);
// Tower properties
self.towerType = 'basic';
self.level = 1;
self.damage = 25;
self.range = 300;
self.fireRate = 60; // Frames between shots (60 = 1 second at 60fps)
self.cost = 100;
self.upgradeCost = 150;
self.lastShotFrame = 0;
self.target = null;
self.rangeCircle = null;
self.showingRange = false;
// Tower1 specific properties
self.tower1Range = 500; // Tower1 has 500 range straight ahead
self.isTower1 = false; // Flag to identify Tower1 instances
// Tower health properties
self.health = 500;
self.maxHealth = 500;
self.lastCollidingEnemies = []; // Track which enemies were colliding last frame
// Guideline tracking for same-line targeting
self.guidelineIndex = -1; // Which guideline this tower is on
// Dog tower graphics (for gas towers)
self.dogGraphics = null;
self.dog1Graphics = null;
// Create tower graphics based on tower type
var towerGraphics;
if (self.towerType === 'fire') {
towerGraphics = self.attachAsset('Fireworks', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (self.towerType === 'air') {
towerGraphics = self.attachAsset('Fan', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
towerGraphics = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Create level indicator
var levelIndicator = self.attachAsset('towerLevelIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: 30,
y: -30
});
// Tower health bar graphics
self.towerHealthBarBackground = self.attachAsset('towerHealthBarBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -30
});
self.towerHealthBarForeground = self.attachAsset('towerHealthBarForeground', {
anchorX: 0,
anchorY: 0.5,
x: -40,
y: -30
});
// Method to show/hide range circle
self.showRange = function () {
if (!self.rangeCircle) {
self.rangeCircle = self.addChild(LK.getAsset('rangeCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self.range / 500,
scaleY: self.range / 500,
alpha: 0.3
}));
}
self.rangeCircle.alpha = 0.3;
self.showingRange = true;
};
self.hideRange = function () {
if (self.rangeCircle) {
self.rangeCircle.alpha = 0;
}
self.showingRange = false;
};
// Method to find nearest enemy in range
self.findTarget = function () {
var nearestEnemy = null;
var nearestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Only target enemies on the same guideline, except UFO boss (guidelineIndex -1) which can be targeted by all towers
if (enemy.guidelineIndex !== self.guidelineIndex && enemy.guidelineIndex !== -1) {
continue; // Skip enemies not on same line
}
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// For Tower1 (water tower), target enemies within 500 pixel circular range
if (self.isTower1) {
// Check if enemy is within 650 range in all directions (extended by 150 pixels)
if (distance <= 650) {
if (distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
} else if (self.towerType === 'fire') {
// Fire tower targets enemies 800 pixels both left and right
if (Math.abs(dx) <= 800 && Math.abs(dy) <= 50) {
var straightDistance = Math.abs(dx); // Distance horizontally
if (straightDistance < nearestDistance) {
nearestDistance = straightDistance;
nearestEnemy = enemy;
}
}
} else if (self.towerType === 'dog') {
// Gas tower (dog) targets enemies 600 pixels straight ahead to the right
if (dx >= 0 && dx <= 600 && Math.abs(dy) <= 50) {
var straightDistance = dx; // Distance to the right
if (straightDistance < nearestDistance) {
nearestDistance = straightDistance;
nearestEnemy = enemy;
}
}
} else if (self.towerType === 'electric') {
// Electric tower targets enemies 500 pixels straight ahead to the right
if (dx >= 0 && dx <= 500 && Math.abs(dy) <= 50) {
var straightDistance = dx; // Distance to the right
if (straightDistance < nearestDistance) {
nearestDistance = straightDistance;
nearestEnemy = enemy;
}
}
} else {
// Regular circular range for other towers
if (distance <= self.range && distance < nearestDistance) {
nearestDistance = distance;
nearestEnemy = enemy;
}
}
}
return nearestEnemy;
};
// Method to shoot at target
self.shoot = function (target) {
if (!target || !target.parent) return;
// For slingshot towers, add pullback effect before firing
if (self.towerType === 'slingshot') {
// Pull slingshot back 20 pixels
tween(self, {
scaleX: 0.8,
scaleY: 1.1,
y: self.y + 20
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Snap forward to firing position
tween(self, {
scaleX: 1.0,
scaleY: 1.0,
y: self.y - 20
}, {
duration: 100,
easing: tween.easeIn
});
}
});
return; // Slingshot firing is handled elsewhere
}
// For gas towers (dog), swap visibility when firing
if (self.towerType === 'dog' && self.dogGraphics && self.dog1Graphics) {
// Show Dog1 (firing state)
self.dogGraphics.visible = false;
self.dog1Graphics.visible = true;
// Play fart sound for dog tower
LK.getSound('Fart').play();
// Switch back to Dog after a short delay
tween(self, {}, {
duration: 200,
onFinish: function onFinish() {
if (self.dogGraphics && self.dog1Graphics) {
self.dogGraphics.visible = true;
self.dog1Graphics.visible = false;
}
}
});
}
// Play water sound for water tower (Tower1)
if (self.isTower1) {
LK.getSound('Water').play();
}
// Play fireball sound for fire tower
if (self.towerType === 'fire') {
LK.getSound('Fireball').play();
}
// Play electric sound for electric tower
if (self.towerType === 'electric') {
LK.getSound('Electric').play();
}
// For water towers, create prolonged spray attack with 500 pixel range
if (self.isTower1) {
// Calculate direction to target
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only attack if target is within 650 pixel range (extended by 150 pixels)
if (distance <= 650) {
// Create pulsing spray effect
var pulseSpray = function pulseSpray() {
if (stream && stream.parent) {
tween(stream, {
scaleY: (streamWidth + 10) / 4,
alpha: 1.0
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (stream && stream.parent) {
tween(stream, {
scaleY: streamWidth / 4,
alpha: 0.8
}, {
duration: 200,
easing: tween.easeInOut
});
}
}
});
}
}; // Start pulsing animation
// Create blue water spray stream
var streamWidth = 25;
var streamLength = Math.min(distance, 650); // Use actual distance but cap at 650 (extended by 150 pixels)
var stream = game.addChild(LK.getAsset('guideLine', {
anchorX: 0,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: streamLength / 2200,
// Scale to target distance
scaleY: streamWidth / 4,
// Scale to desired width
tint: 0x66CCFF,
// Lighter blue water color
alpha: 0.8
}));
// Calculate angle to target for proper rotation
var angle = Math.atan2(dy, dx);
stream.rotation = angle;
// Create prolonged spray effect - animate the stream for 1.5 seconds
var sprayDuration = 1500;
var damageInterval = 100; // Deal damage every 100ms during spray
var damageDealt = 0;
var maxDamage = self.damage;
pulseSpray();
var pulseInterval = LK.setInterval(pulseSpray, 400);
// Deal damage over time during spray
var damageTimer = LK.setInterval(function () {
if (target && target.parent && damageDealt < maxDamage) {
var damagePerTick = Math.ceil(maxDamage / (sprayDuration / damageInterval));
target.health -= damagePerTick;
damageDealt += damagePerTick;
// Visual feedback - flash target blue
// Also flash the target
tween(target, {
tint: 0x66CCFF
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(target, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeOut
});
}
});
if (target.health <= 0) {
target.defeat();
LK.clearInterval(damageTimer);
LK.clearInterval(pulseInterval);
}
}
}, damageInterval);
// Clean up spray effect after duration
LK.setTimeout(function () {
LK.clearInterval(damageTimer);
LK.clearInterval(pulseInterval);
if (stream && stream.parent) {
tween(stream, {
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
stream.destroy();
}
});
}
}, sprayDuration);
}
} else {
// Create bullet/projectile effect for non-water towers
var bullet = game.addChild(LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
// Make fire tower projectiles use Fireball asset
if (self.towerType === 'fire') {
// Destroy the default bullet and create fireball projectile
bullet.destroy();
bullet = game.addChild(LK.getAsset('Fireball', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
}
// Make gas tower (dog) projectiles use Gas asset
if (self.towerType === 'dog') {
// Destroy the default bullet and create gas projectile
bullet.destroy();
bullet = game.addChild(LK.getAsset('Gas', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
}
// Make electric tower projectiles cyan and add electric effect
if (self.towerType === 'electric') {
bullet.tint = 0x00FFFF; // Cyan color for electric tower projectiles
// Create electric effect at tower position
var electricEffect = game.addChild(LK.getAsset('Electriceffect', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
}));
// Animate electric effect with fade out
tween(electricEffect, {
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
electricEffect.destroy();
}
});
}
// Calculate direction to target
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Animate bullet to target
tween(bullet, {
x: target.x,
y: target.y
}, {
duration: distance / 2,
// Speed based on distance
easing: tween.linear,
onFinish: function onFinish() {
bullet.destroy();
// Deal damage to target
if (target && target.parent) {
target.health -= self.damage;
if (target.health <= 0) {
target.defeat();
}
}
}
});
}
};
// Method to upgrade tower
self.upgrade = function () {
if (playerCash >= self.upgradeCost && self.level < 3) {
playerCash -= self.upgradeCost;
self.level++;
self.damage += 15;
self.range += 50;
self.fireRate = Math.max(30, self.fireRate - 10);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
// Update level indicator
levelIndicator.destroy();
levelIndicator = self.attachAsset('towerLevelIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: 30,
y: -30,
scaleX: self.level,
scaleY: self.level
});
// Update range circle if showing
if (self.showingRange && self.rangeCircle) {
self.rangeCircle.scaleX = self.range / 500;
self.rangeCircle.scaleY = self.range / 500;
}
// Play upgrade sound
LK.getSound('Towerselect').play();
return true;
}
return false;
};
// Click handler for tower interaction
self.down = function (x, y, obj) {
if (self.towerType === 'slingshot') {
// Start aiming for slingshot
self.isAiming = true;
self.aimStartX = x;
self.aimStartY = y;
// Convert to global coordinates
var globalPos = self.toGlobal({
x: x,
y: y
});
self.aimStartX = globalPos.x;
self.aimStartY = globalPos.y;
} else {
if (self.showingRange) {
self.hideRange();
} else {
self.showRange();
}
}
};
self.update = function () {
// Only operate when wave is active
if (!waveActive) return;
// Rotate blades for air towers
if (self.towerType === 'air' && self.bladeGraphics) {
self.bladeGraphics.rotation += 0.2; // Continuous blade rotation
// Check for enemies within 300 pixel range straight ahead (to the right)
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
// Only target enemies on the same guideline
if (enemy.guidelineIndex === self.guidelineIndex) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
// Check if enemy is within 300 range straight ahead (to the right)
if (dx >= 0 && dx <= 300 && Math.abs(dy) <= 50) {
// Initialize last blown back tracking if not set
if (enemy.lastBlownBack === undefined) {
enemy.lastBlownBack = false;
}
// Only blow back if enemy wasn't blown back last frame (prevent continuous blowing)
if (!enemy.lastBlownBack) {
// Blow enemy back 400 pixels
var newX = enemy.x + 400;
// Use tween to animate the blow-back effect
tween(enemy, {
x: newX
}, {
duration: 300,
easing: tween.easeOut
});
// Inflict small amount of damage (15 damage)
enemy.health -= 15;
// Reduce air tower health by 5 when blowing back enemy
self.health -= 5;
if (enemy.health <= 0) {
enemy.defeat();
}
// Visual effect - flash enemy to show they were hit
tween(enemy, {
tint: 0x00FFFF
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemy, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Mark enemy as blown back this frame
enemy.lastBlownBack = true;
}
} else {
// Enemy is not in range, reset blown back tracking
enemy.lastBlownBack = false;
}
}
}
}
// Check for enemy collisions and reduce tower health
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Only check collision with enemies on the same guideline
if (enemy.guidelineIndex === self.guidelineIndex && self.intersects(enemy)) {
// Initialize collision tracking properties if not set
if (enemy.lastCollidingWithTower === undefined) {
enemy.lastCollidingWithTower = false;
}
// Check if this enemy just started colliding (wasn't colliding last frame)
if (!enemy.lastCollidingWithTower) {
// Enemy just started colliding - apply damage
self.health -= 5; // Reduce health by 5 per collision
// Flash tower red to show damage
tween(towerGraphics, {
tint: 0xFF0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Check if tower is destroyed
if (self.health <= 0) {
// Create explosion effect at tower position
var explosionEffect = game.addChild(LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 1,
scaleX: 0.5,
scaleY: 0.5
}));
// Play explosion sound
LK.getSound('Explosion').play();
// Animate explosion with scale and fade out
tween(explosionEffect, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
explosionEffect.destroy();
}
});
// Remove tower from towers array
for (var k = towers.length - 1; k >= 0; k--) {
if (towers[k] === self) {
towers.splice(k, 1);
break;
}
}
// Destroy tower
self.destroy();
return;
}
}
// Mark enemy as currently colliding and pause movement
enemy.lastCollidingWithTower = true;
enemy.collidingWithTower = true;
} else {
// Enemy is not colliding - reset collision tracking
if (enemy.lastCollidingWithTower === undefined) {
enemy.lastCollidingWithTower = false;
} else {
enemy.lastCollidingWithTower = false;
}
}
}
// Update tower health bar
var healthPercentage = self.health / self.maxHealth;
self.towerHealthBarForeground.scaleX = healthPercentage;
// Change health bar color based on health percentage
if (healthPercentage > 0.6) {
self.towerHealthBarForeground.tint = 0x00ff00; // Green
} else if (healthPercentage > 0.3) {
self.towerHealthBarForeground.tint = 0xffff00; // Yellow
} else {
self.towerHealthBarForeground.tint = 0xff0000; // Red
}
// Find target if we don't have one or current target is invalid
if (!self.target || !self.target.parent) {
self.target = self.findTarget();
}
// Plasma tower pulsation and damage logic
if (self.towerType === 'plasma') {
// Initialize pulsation tracking if not set
if (self.lastPulseFrame === undefined) {
self.lastPulseFrame = 0;
self.pulseRate = 90; // Pulsate every 1.5 seconds
}
// Pulsate plasma tower
var adjustedPulseRate = typeof gameSpeedMultiplier !== 'undefined' ? self.pulseRate / gameSpeedMultiplier : self.pulseRate;
if (LK.ticks - self.lastPulseFrame >= adjustedPulseRate) {
// Create visual pulsation effect
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Find all enemies within 400 pixels radius on same line and lines above/below
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if enemy is on same line or adjacent lines (above/below)
var guidelineIndexDiff = Math.abs(enemy.guidelineIndex - self.guidelineIndex);
if (distance <= 400 && guidelineIndexDiff <= 1) {
// Deal damage to enemy
enemy.health -= self.damage;
// Flash enemy purple to show plasma damage
tween(enemy, {
tint: 0xFF00FF
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemy, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
});
if (enemy.health <= 0) {
enemy.defeat();
}
}
}
self.lastPulseFrame = LK.ticks;
}
}
// WiFi tower shooting logic
if (self.towerType === 'wifi') {
// Initialize shooting properties if not set
if (self.lastShotFrame === undefined) {
self.lastShotFrame = 0;
self.fireRate = 30; // Fast shooting rate
self.range = 600; // 600 pixel range
}
// Find all enemies within 600 range and shoot at them
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if enemy is within 600 range
if (distance <= 600) {
// Check if enough time has passed since last shot
var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate;
if (LK.ticks - self.lastShotFrame >= adjustedFireRate) {
// Create WiFi projectile using WiFi asset
var bullet = game.addChild(LK.getAsset('WiFi', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.5,
// Scale down WiFi asset
scaleY: 0.5
// Scale down WiFi asset
}));
// Animate bullet to target enemy
tween(bullet, {
x: enemy.x,
y: enemy.y
}, {
duration: distance / 4,
// Fast bullet speed
easing: tween.linear,
onFinish: function onFinish() {
bullet.destroy();
// Deal high damage to target enemy
if (enemy && enemy.parent) {
enemy.health -= 100; // High damage
// Flash enemy yellow to show hit
tween(enemy, {
tint: 0xFFFF00
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemy, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
});
if (enemy.health <= 0) {
enemy.defeat();
}
}
}
});
self.lastShotFrame = LK.ticks;
break; // Only shoot at one enemy per frame
}
}
}
return; // Skip regular shooting logic for WiFi towers
}
// Slingshot tower aiming mechanics - manual player aiming required
if (self.towerType === 'slingshot') {
// Slingshot towers don't auto-shoot, they require player interaction
// The shooting is handled by mouse events
return; // Skip auto-shooting logic for slingshot towers
}
// Shoot at target if in range and fire rate allows (but not for air towers, plasma towers, and slingshot towers)
if (self.target && self.target.parent && self.towerType !== 'air' && self.towerType !== 'plasma' && self.towerType !== 'slingshot') {
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var targetInRange = false;
// Check if target is still in range based on tower type
if (self.isTower1) {
// Tower1 (water tower) has 650 pixel circular range (extended by 150 pixels)
targetInRange = distance <= 650;
} else if (self.towerType === 'fire') {
// Fire tower has 800 range both left and right
targetInRange = Math.abs(dx) <= 800 && Math.abs(dy) <= 50;
} else if (self.towerType === 'dog') {
// Gas tower has 600 range straight ahead to the right
targetInRange = dx >= 0 && dx <= 600 && Math.abs(dy) <= 50;
} else if (self.towerType === 'electric') {
// Electric tower has 500 range straight ahead to the right
targetInRange = dx >= 0 && dx <= 500 && Math.abs(dy) <= 50;
} else {
// Regular circular range for other towers
targetInRange = distance <= self.range;
}
if (targetInRange) {
// Check if enough time has passed since last shot
var adjustedFireRate = typeof gameSpeedMultiplier !== 'undefined' ? self.fireRate / gameSpeedMultiplier : self.fireRate;
if (LK.ticks - self.lastShotFrame >= adjustedFireRate) {
self.shoot(self.target);
self.lastShotFrame = LK.ticks;
}
} else {
// Target moved out of range
self.target = null;
}
}
};
self.setAssetType = function (assetType) {
if (towerGraphics && towerGraphics.parent) {
towerGraphics.destroy();
}
// Clean up existing dog graphics if they exist
if (self.dogGraphics && self.dogGraphics.parent) {
self.dogGraphics.destroy();
self.dogGraphics = null;
}
if (self.dog1Graphics && self.dog1Graphics.parent) {
self.dog1Graphics.destroy();
self.dog1Graphics = null;
}
// Clean up existing air tower graphics if they exist
if (self.bladeGraphics && self.bladeGraphics.parent) {
self.bladeGraphics.destroy();
self.bladeGraphics = null;
}
self.towerType = assetType.toLowerCase();
// For dog towers, create both Dog and Dog1 assets
if (assetType === 'Dog') {
self.dogGraphics = self.attachAsset('Dog', {
anchorX: 0.5,
anchorY: 0.5
});
self.dog1Graphics = self.attachAsset('Dog1', {
anchorX: 0.5,
anchorY: 0.5
});
// Initially show Dog, hide Dog1
self.dogGraphics.visible = true;
self.dog1Graphics.visible = false;
towerGraphics = self.dogGraphics; // Set main reference to dogGraphics
} else if (assetType === 'Fan') {
// For air towers, use Fan asset as actual tower
towerGraphics = self.attachAsset('Fan', {
anchorX: 0.5,
anchorY: 0.5
});
self.bladeGraphics = self.attachAsset('Blades', {
anchorX: 0.5,
anchorY: 0.5,
y: -70
});
self.towerType = 'air';
} else {
towerGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
}
// Ensure health bars are brought to front after asset change
if (self.towerHealthBarBackground && self.towerHealthBarBackground.parent) {
self.removeChild(self.towerHealthBarBackground);
self.addChild(self.towerHealthBarBackground);
}
if (self.towerHealthBarForeground && self.towerHealthBarForeground.parent) {
self.removeChild(self.towerHealthBarForeground);
self.addChild(self.towerHealthBarForeground);
}
};
// Up handler for slingshot aiming
self.up = function (x, y, obj) {
if (self.towerType === 'slingshot' && self.isAiming) {
// Convert to global coordinates
var globalPos = self.toGlobal({
x: x,
y: y
});
self.aimEndX = globalPos.x;
self.aimEndY = globalPos.y;
// Calculate aim direction (reverse drag direction for slingshot physics)
var aimDx = self.aimStartX - self.aimEndX;
var aimDy = self.aimStartY - self.aimEndY;
var aimDistance = Math.sqrt(aimDx * aimDx + aimDy * aimDy);
// Only shoot if player dragged far enough (minimum 50 pixels)
if (aimDistance > 50) {
// Find target enemy in aim direction
var targetEnemy = null;
var bestScore = -1;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var enemyDx = enemy.x - self.x;
var enemyDy = enemy.y - self.y;
var enemyDistance = Math.sqrt(enemyDx * enemyDx + enemyDy * enemyDy);
// Check if enemy is within range
if (enemyDistance <= self.range) {
// Calculate angle between aim direction and enemy direction
var aimAngle = Math.atan2(aimDy, aimDx);
var enemyAngle = Math.atan2(enemyDy, enemyDx);
var angleDiff = Math.abs(aimAngle - enemyAngle);
// Normalize angle difference to 0-π range
if (angleDiff > Math.PI) {
angleDiff = 2 * Math.PI - angleDiff;
}
// Score based on angle accuracy and distance (closer and more accurate = higher score)
var angleScore = Math.PI - angleDiff; // Higher score for smaller angle diff
var distanceScore = self.range - enemyDistance; // Higher score for closer enemies
var totalScore = angleScore * 2 + distanceScore * 0.5;
// Accept targets within 45 degrees of aim direction
if (angleDiff < Math.PI / 4 && totalScore > bestScore) {
bestScore = totalScore;
targetEnemy = enemy;
}
}
}
// Shoot at target enemy if found
if (targetEnemy) {
// Add pullback effect before firing
tween(self, {
scaleX: 0.8,
scaleY: 1.1,
y: self.y + 20
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Snap forward and create bullet
tween(self, {
scaleX: 1.0,
scaleY: 1.0,
y: self.y - 20
}, {
duration: 100,
easing: tween.easeIn
});
// Create bullet projectile with larger size and distinctive appearance
var bullet = game.addChild(LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y - 100,
scaleX: 2.0,
// Make projectile larger
scaleY: 2.0,
// Make projectile larger
tint: 0xFF6B00 // Bright orange color for better visibility
}));
// Add pulsing effect to make projectile more visible
tween(bullet, {
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bullet, {
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
// Calculate trajectory to target
var dx = targetEnemy.x - self.x;
var dy = targetEnemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Animate bullet to target with arc trajectory
tween(bullet, {
x: targetEnemy.x,
y: targetEnemy.y
}, {
duration: distance / 3,
// Bullet speed
easing: tween.easeOut,
// Arc-like trajectory
onFinish: function onFinish() {
bullet.destroy();
// Deal heavy damage to target
if (targetEnemy && targetEnemy.parent) {
targetEnemy.health -= self.damage;
// Flash target to show hit
tween(targetEnemy, {
tint: 0xFFFF00 // Yellow flash
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(targetEnemy, {
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeOut
});
}
});
if (targetEnemy.health <= 0) {
targetEnemy.defeat();
}
}
}
});
}
});
}
}
// No aiming guide to clean up
// Reset aiming state
self.isAiming = false;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 //Init game with black background
});
/****
* Game Code
****/
// Fire tower already uses 'Fireworks' asset - goal already achieved
var enemies = [];
var towers = [];
var waveActive = false;
var enemySpawnTimer = 0;
var enemySpawnInterval = 420; // Spawn every 7 seconds at 60fps
var enemiesSpawned = 0;
var maxEnemiesInWave = 10;
var guidelineYPositions = [];
var firstEnemySpawned = false;
var currentWave = 1;
var enemiesDefeated = 0;
var playerScore = 0;
var playerLives = 10;
var playerCash = 50;
var scoreDisplay;
var livesDisplay;
var cashDisplay;
var placingTower = false;
var towerPreview = null;
var confirmButton = null;
var cancelButton = null;
var pendingTowerCost = 10;
var verticalGuidelines = [];
var snapPositions = [];
var introPlayed = false;
var gameStarted = false; // Flag to track if game has started
var gameSpeedMultiplier = 1; // Initialize game speed multiplier
// Create intro cutscene using intro2 asset
var introCutscene = new Cutscene();
// Add intro2 scene
introCutscene.addScene({
onStart: function onStart() {
// Create intro2 asset
var intro2Asset = game.addChild(LK.getAsset('Intro2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Create cell asset in middle of intro2
var cellAsset = game.addChild(LK.getAsset('cell', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Store references for cleanup
introCutscene.intro2Asset = intro2Asset;
introCutscene.cellAsset = cellAsset;
// Fade in intro2
tween(intro2Asset, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeInOut
});
// Fade in cell asset
tween(cellAsset, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// After cell asset fades in, wait 5 seconds then transition to story1
LK.setTimeout(function () {
// Fade out intro2 and cell assets
tween(intro2Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut
});
tween(cellAsset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create and fade in story1 scene
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: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Store story1 reference for cleanup
introCutscene.story1Asset = story1Asset;
// Auto-transition to story2 after 3 seconds
LK.setTimeout(function () {
tween(story1Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story1Asset.destroy();
// Create and fade in story2 scene
var story2Asset = game.addChild(LK.getAsset('Story2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
tween(story2Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
introCutscene.story2Asset = story2Asset;
// Auto-transition to story3 after 3 seconds
LK.setTimeout(function () {
tween(story2Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story2Asset.destroy();
// Create and fade in story3 scene
var story3Asset = game.addChild(LK.getAsset('Story3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
tween(story3Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
introCutscene.story3Asset = story3Asset;
// Auto-transition to story4 after 3 seconds
LK.setTimeout(function () {
tween(story3Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story3Asset.destroy();
// Create and fade in story4 scene
var story4Asset = game.addChild(LK.getAsset('Story4', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
tween(story4Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
introCutscene.story4Asset = story4Asset;
// After story4, wait 3 seconds then finish cutscene
LK.setTimeout(function () {
tween(story4Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story4Asset.destroy();
// Cutscene complete - continue to title screen
showTitleScreen();
}
});
}, 3000);
}
});
}
});
}, 3000);
}
});
}
});
}, 3000);
}
});
}
});
}, 3000);
}
});
}
});
}, 5000);
}
});
// Add skip intro button
var skipIntroButton = new Text2('SKIP INTRO', {
size: 60,
fill: 0xFFFFFF
});
skipIntroButton.anchor.set(0.5, 0);
skipIntroButton.x = 2048 / 2;
skipIntroButton.y = 200;
skipIntroButton.alpha = 0;
game.addChild(skipIntroButton);
// Store reference for cleanup
introCutscene.skipIntroButton = skipIntroButton;
// Fade in skip button
tween(skipIntroButton, {
alpha: 1
}, {
duration: 1000,
easing: tween.easeInOut
});
// Add click event handler to skip button
skipIntroButton.down = function (x, y, obj) {
// Don't allow skip if game has already started
if (gameStarted) return;
// Play intro music
LK.playMusic('Intro', {
loop: false
});
// Clean up intro2 assets
tween.stop(intro2Asset);
tween.stop(cellAsset);
tween.stop(skipIntroButton);
intro2Asset.destroy();
cellAsset.destroy();
skipIntroButton.destroy();
// Skip directly to title screen
showTitleScreen();
};
// Cell asset now has no click handler to prevent story replay
}
});
// Set what happens when cutscene finishes
introCutscene.onFinish = function () {
// Cutscene finished - no automatic transition
};
// Function to skip directly to backdrop scene
function skipToBackdrop() {
// 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.2,
scaleY: 0.2
}));
// Zoom flyby to final size
tween(backdropFlyby, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 1000,
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
}));
// Ensure wave1 appears in front of all game objects
wave1Asset.zIndex = 1000;
// 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 tower defense wave
startWave1();
}
});
}, 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 += 525; // Move guideline 1 down by 525 pixels (was 505)
} else if (i === 2) {
yPosition += 395; // Move guideline 2 down by 395 pixels (was 420)
} else if (i === 3) {
yPosition += 225; // Move guideline 3 down by 225 pixels (was 250)
} else if (i === 4) {
yPosition += 95; // Move guideline 4 down by 95 pixels (was 120)
} else if (i === 5) {
yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75)
} else if (i === 6) {
yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250)
}
var guideLine = game.addChild(LK.getAsset('guideLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: yPosition,
alpha: 0.5
}));
}
}
// Function to show title screen
function showTitleScreen() {
// Don't show title screen if game has already started
if (gameStarted) return;
// Display the title
// 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);
// 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
}));
// Tween alpha from 0 to 1 for fade-in effect
tween(introAsset, {
alpha: 1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// 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
});
// 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: 0,
scaleX: 0.2,
scaleY: 0.2
}));
// 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
}));
// Ensure wave1 appears in front of all game objects
wave1Asset.zIndex = 1000;
// 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 tower defense wave
startWave1();
}
});
}, 2000);
}
});
}
});
}
// Fade in backdrop
tween(backdropAsset, {
alpha: 1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade in backdrop flyby and zoom to position
tween(backdropFlyby, {
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Start flyby movement to right side after fade 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 += 525; // Move guideline 1 down by 525 pixels (was 505)
} else if (i === 2) {
yPosition += 395; // Move guideline 2 down by 395 pixels (was 420)
} else if (i === 3) {
yPosition += 225; // Move guideline 3 down by 225 pixels (was 250)
} else if (i === 4) {
yPosition += 95; // Move guideline 4 down by 95 pixels (was 120)
} else if (i === 5) {
yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75)
} else if (i === 6) {
yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250)
}
var guideLine = game.addChild(LK.getAsset('guideLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: yPosition,
alpha: 0.5
}));
}
}
});
}
});
}
});
}, 5000);
}
});
}
// Calculate guideline Y positions for enemy spawning
function calculateGuidelinePositions() {
var lineSpacing = 2732 / 7;
guidelineYPositions = [];
for (var i = 1; i <= 6; i++) {
var yPosition = lineSpacing * i;
if (i === 1) {
yPosition += 525; // Move guideline 1 down by 525 pixels (was 505)
} else if (i === 2) {
yPosition += 395; // Move guideline 2 down by 395 pixels (was 420)
} else if (i === 3) {
yPosition += 225; // Move guideline 3 down by 225 pixels (was 250)
} else if (i === 4) {
yPosition += 95; // Move guideline 4 down by 95 pixels (was 120)
} else if (i === 5) {
yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75)
} else if (i === 6) {
yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250)
}
guidelineYPositions.push(yPosition);
}
}
// Calculate snap positions from guideline intersections
function calculateSnapPositions() {
snapPositions = [];
// Create vertical guideline positions (8 columns across screen width)
var verticalSpacing = 2048 / 9;
var verticalXPositions = [];
for (var i = 1; i <= 8; i++) {
var xPosition = verticalSpacing * i;
verticalXPositions.push(xPosition);
}
// Create snap positions at intersections of horizontal and vertical guidelines
for (var h = 0; h < guidelineYPositions.length; h++) {
for (var v = 0; v < verticalXPositions.length; v++) {
snapPositions.push({
x: verticalXPositions[v],
y: guidelineYPositions[h]
});
}
}
}
// Find nearest snap position to given coordinates
function findNearestSnapPosition(x, y) {
var nearestPosition = {
x: x,
y: y
};
var nearestDistance = Infinity;
for (var i = 0; i < snapPositions.length; i++) {
var snap = snapPositions[i];
var dx = snap.x - x;
var dy = snap.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPosition = snap;
}
}
return nearestPosition;
}
// Show vertical guidelines during tower placement
function showVerticalGuidelines() {
hideVerticalGuidelines(); // Clear any existing guidelines
var verticalSpacing = 2048 / 9;
for (var i = 1; i <= 8; i++) {
var xPosition = verticalSpacing * i;
var yPosition = 2732 / 2;
if (i === 1) {
yPosition += 50; // Move vertical guideline 1 down by 50 pixels
}
var guideLine = game.addChild(LK.getAsset('verticalGuideLine', {
anchorX: 0.5,
anchorY: 0.5,
x: xPosition,
y: yPosition,
alpha: 0.3
}));
verticalGuidelines.push(guideLine);
}
}
// Hide vertical guidelines
function hideVerticalGuidelines() {
for (var i = 0; i < verticalGuidelines.length; i++) {
verticalGuidelines[i].destroy();
}
verticalGuidelines = [];
}
// Start first wave of tower defense
function startWave1() {
calculateGuidelinePositions();
waveActive = true;
gameStarted = true; // Mark that the game has officially started
enemySpawnTimer = 0;
enemiesSpawned = 0;
currentWave = 1;
enemiesDefeated = 0;
// Add score display at top left
scoreDisplay = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreDisplay.anchor.set(0, 0);
scoreDisplay.x = 400;
scoreDisplay.y = 50;
LK.gui.addChild(scoreDisplay);
// Add lives display at top center
livesDisplay = new Text2('Lives: 10', {
size: 60,
fill: 0xFFFFFF
});
livesDisplay.anchor.set(0.5, 0);
livesDisplay.x = 1024;
livesDisplay.y = 50;
LK.gui.addChild(livesDisplay);
// Add cash display under lives display
cashDisplay = new Text2('Cash: $50', {
size: 60,
fill: 0xFFFFFF
});
cashDisplay.anchor.set(0.5, 0);
cashDisplay.x = 1024;
cashDisplay.y = 120;
LK.gui.addChild(cashDisplay);
// Add speed up X2 button under cash display
var speedUpButton = new Text2('SPEED X1', {
size: 50,
fill: 0x00FF00
});
speedUpButton.anchor.set(0.5, 0);
speedUpButton.x = 1024;
speedUpButton.y = 190;
LK.gui.addChild(speedUpButton);
// Add click handler for speed up button
speedUpButton.down = function (x, y, obj) {
if (gameSpeedMultiplier === 1) {
gameSpeedMultiplier = 2;
speedUpButton.setText('SPEED X2');
speedUpButton.tint = 0xFFFF00; // Yellow when active
} else {
gameSpeedMultiplier = 1;
speedUpButton.setText('SPEED X1');
speedUpButton.tint = 0x00FF00; // Green when normal
}
};
// Add skip wave button under score display
var skipWaveButton = new Text2('SKIP WAVE', {
size: 40,
fill: 0xFFFF00
});
skipWaveButton.anchor.set(0, 0);
skipWaveButton.x = 400;
skipWaveButton.y = 120;
LK.gui.addChild(skipWaveButton);
// Add global click handler to cancel tower placement
game.down = function (x, y, obj) {
// Cancel tower placement if clicking on empty space
if (placingTower && obj === game) {
if (towerPreview) {
// Clean up blade preview if it exists
if (towerPreview.bladePreview) {
towerPreview.bladePreview.destroy();
}
towerPreview.destroy();
towerPreview = null;
}
if (confirmButton) {
confirmButton.destroy();
confirmButton = null;
}
hideVerticalGuidelines();
placingTower = false;
}
};
// Add tower purchase buttons at bottom of screen
var towerButtonY = 2400; // Position near bottom of screen
var buttonSpacing = 50; // 50 pixel spacing between buttons
var startX = 430;
// Water Tower Button
var waterTowerButton = game.addChild(LK.getAsset('Waterbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX,
y: towerButtonY,
scaleX: 0.8,
scaleY: 0.8
}));
// Gas Tower Button
var gasTowerButton = game.addChild(LK.getAsset('Gasbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + waterTowerButton.width * 0.8 + buttonSpacing,
y: towerButtonY,
scaleX: 0.8,
scaleY: 0.8
}));
// Air Tower Button
var basicTowerButton = game.addChild(LK.getAsset('Airbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + waterTowerButton.width * 0.8 + buttonSpacing + gasTowerButton.width * 0.8 + buttonSpacing,
y: towerButtonY,
scaleX: 0.8,
scaleY: 0.8
}));
// Electric Tower Button
var electricTowerButton = game.addChild(LK.getAsset('Electricbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX,
y: towerButtonY + 150 + buttonSpacing,
scaleX: 0.8,
scaleY: 0.8
}));
// Advanced Tower Button
var advancedTowerButton = game.addChild(LK.getAsset('Plasmabutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + electricTowerButton.width * 0.8 + buttonSpacing,
y: towerButtonY + 150 + buttonSpacing,
scaleX: 0.8,
scaleY: 0.8
}));
// Slingshot Tower Button
var slingshotTowerButton = game.addChild(LK.getAsset('Slingshotbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + basicTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing,
y: towerButtonY + 150 + buttonSpacing,
scaleX: 0.8,
scaleY: 0.8
}));
// Fire Tower Button (above WiFi button)
var fireButton = game.addChild(LK.getAsset('Firebutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + basicTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing + slingshotTowerButton.width * 0.8 + buttonSpacing,
y: towerButtonY + 150 + buttonSpacing - 150 - buttonSpacing,
scaleX: 0.8,
scaleY: 0.8
}));
// WiFi Tower Button
var wifiTowerButton = game.addChild(LK.getAsset('Wifibutton', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + basicTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing + slingshotTowerButton.width * 0.8 + buttonSpacing,
y: towerButtonY + 150 + buttonSpacing,
scaleX: 0.8,
scaleY: 0.8
}));
// Add click handlers for tower buttons
fireButton.down = function (x, y, obj) {
if (playerCash >= 40 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for fire tower
placingTower = true;
pendingTowerCost = 40; // Fire tower costs $40
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Fireworks asset
towerPreview = game.addChild(LK.getAsset('Fireworks', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Fireworks asset
var newTower = new Tower();
newTower.setAssetType('Fireworks');
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'fire';
// Set fire tower properties
newTower.damage = 40;
newTower.range = 400;
newTower.fireRate = 50;
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
advancedTowerButton.down = function (x, y, obj) {
if (playerCash >= 60 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for plasma tower
placingTower = true;
pendingTowerCost = 60; // Plasma tower costs $60
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Plasmaball asset
towerPreview = game.addChild(LK.getAsset('Plasmaball', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Plasmaball asset
var newTower = new Tower();
newTower.setAssetType('Plasmaball');
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'plasma';
// Set plasma tower properties
newTower.damage = 80;
newTower.range = 400;
newTower.fireRate = 60;
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
electricTowerButton.down = function (x, y, obj) {
if (playerCash >= 50 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for electric tower
placingTower = true;
pendingTowerCost = 50; // Electric tower costs $50
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Bugzapper asset
towerPreview = game.addChild(LK.getAsset('Bugzapper', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Bugzapper asset
var newTower = new Tower();
newTower.setAssetType('Bugzapper');
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'electric';
// Set electric tower properties
newTower.damage = 60;
newTower.range = 350;
newTower.fireRate = 40; // Faster shooting
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
waterTowerButton.down = function (x, y, obj) {
if (playerCash >= pendingTowerCost && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode
placingTower = true;
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Tower1 asset
towerPreview = game.addChild(LK.getAsset('Tower1', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Tower1 asset
var newTower = new Tower();
var towerGraphics = newTower.attachAsset('Tower1', {
anchorX: 0.5,
anchorY: 0.5
});
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.isTower1 = true; // Mark as Tower1
newTower.range = 650; // Extended water tower range from 500 to 650 pixels
newTower.damage = 35; // Increase water tower damage from default 25 to 35
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
gasTowerButton.down = function (x, y, obj) {
if (playerCash >= 20 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for dog tower
placingTower = true;
pendingTowerCost = 20; // Dog tower costs $20
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Dog asset
towerPreview = game.addChild(LK.getAsset('Dog', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Dog asset
var newTower = new Tower();
newTower.setAssetType('Dog');
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'dog';
// Set dog tower properties
newTower.damage = 30;
newTower.range = 600; // Gas tower has 600 range straight ahead
newTower.fireRate = 45; // Faster than basic towers
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
wifiTowerButton.down = function (x, y, obj) {
if (playerCash >= 150 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for wifi tower
placingTower = true;
pendingTowerCost = 150; // WiFi tower costs $150
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Wifitower asset
towerPreview = game.addChild(LK.getAsset('Wifitower', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Wifitower asset
var newTower = new Tower();
newTower.setAssetType('Wifitower');
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'wifi';
// Set wifi tower properties
newTower.damage = 100; // High damage bullets
newTower.range = 600; // 600 pixel range
newTower.fireRate = 30; // Fast shooting rate
// Initialize wifi tower specific properties
newTower.lastShotFrame = 0;
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
slingshotTowerButton.down = function (x, y, obj) {
if (playerCash >= 100 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for slingshot tower
placingTower = true;
pendingTowerCost = 100; // Slingshot tower costs $100
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Slingshot asset
towerPreview = game.addChild(LK.getAsset('Slingshot', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Slingshot asset
var newTower = new Tower();
newTower.setAssetType('Slingshot');
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'slingshot';
// Set slingshot tower properties
newTower.damage = 150; // Heavy damage
newTower.range = 1200; // Increased range to 1200 pixels
newTower.fireRate = 120; // Slower shooting rate
// Initialize slingshot specific properties
newTower.isAiming = false;
newTower.aimStartX = 0;
newTower.aimStartY = 0;
newTower.aimEndX = 0;
newTower.aimEndY = 0;
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
basicTowerButton.down = function (x, y, obj) {
if (playerCash >= 30 && !placingTower) {
LK.getSound('Towerselect').play();
// Start tower placement mode for air tower
placingTower = true;
pendingTowerCost = 30; // Air tower costs $30
// Calculate snap positions and show guidelines
calculateSnapPositions();
showVerticalGuidelines();
// Find initial snap position
var snapPos = findNearestSnapPosition(x, y);
// Create tower preview using Fan asset
towerPreview = game.addChild(LK.getAsset('Fan', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
// Create blade preview on top of fan
var bladePreview = game.addChild(LK.getAsset('Blades', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y - 70,
alpha: 0.7
}));
// Store blade preview reference on tower preview
towerPreview.bladePreview = bladePreview;
// Create green tick confirmation button
confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60
}));
// Create red cancel button under green confirm button
cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x + 60,
y: snapPos.y - 60 + 80 + 25 + 30
// Position under confirm button
}));
// Add click handler to cancel button
cancelButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Clean up placement mode
towerPreview.destroy();
if (towerPreview.bladePreview) {
towerPreview.bladePreview.destroy();
}
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
}
};
// Add click handler to confirm button
confirmButton.down = function (cx, cy, cobj) {
if (placingTower && towerPreview) {
// Deduct cash
playerCash -= pendingTowerCost;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Create actual tower at preview position using Fan asset
var newTower = new Tower();
var fanGraphics = newTower.attachAsset('Fan', {
anchorX: 0.5,
anchorY: 0.5
});
var bladeGraphics = newTower.attachAsset('Blades', {
anchorX: 0.5,
anchorY: 0.5,
y: -70
});
// Store blade reference for rotation
newTower.bladeGraphics = bladeGraphics;
newTower.x = towerPreview.x;
newTower.y = towerPreview.y;
newTower.cost = pendingTowerCost;
newTower.towerType = 'air';
// Set air tower properties
newTower.damage = 35;
newTower.range = 350;
newTower.fireRate = 30; // Faster shooting
// Assign tower to nearest guideline
var nearestGuidelineIndex = 0;
var nearestDistance = Infinity;
for (var g = 0; g < guidelineYPositions.length; g++) {
var distance = Math.abs(newTower.y - guidelineYPositions[g]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestGuidelineIndex = g;
}
}
newTower.guidelineIndex = nearestGuidelineIndex;
game.addChild(newTower);
towers.push(newTower);
// Clean up placement mode
towerPreview.destroy();
if (towerPreview.bladePreview) {
towerPreview.bladePreview.destroy();
}
confirmButton.destroy();
cancelButton.destroy();
towerPreview = null;
confirmButton = null;
hideVerticalGuidelines();
placingTower = false;
// Reset pending tower cost
pendingTowerCost = 10;
// Play confirmation sound
LK.getSound('Confirm').play();
}
};
}
};
// Add click handler to skip wave button
skipWaveButton.down = function (x, y, obj) {
// Force wave completion by clearing all enemies and setting appropriate flags
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
enemiesSpawned = maxEnemiesInWave;
enemiesDefeated = maxEnemiesInWave;
// Check which wave we're skipping and trigger appropriate transition
if (currentWave === 1) {
// Show wave2 asset with fade in/out
var wave2Asset = game.addChild(LK.getAsset('Wave2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave2 appears in front of all game objects
wave2Asset.zIndex = 1000;
// Fade in wave2 asset
tween(wave2Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out wave2 asset
tween(wave2Asset, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave2Asset.destroy();
// Start wave2
startWave2();
}
});
}
});
} else if (currentWave === 2) {
// Show wave3 asset with fade in/out
var wave3Asset = game.addChild(LK.getAsset('Wave3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave3 appears in front of all game objects
wave3Asset.zIndex = 1000;
// Fade in wave3 asset
tween(wave3Asset, {
alpha: 1
}, {
duration: 750,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Keep wave3 visible for 1.5 seconds then fade out
LK.setTimeout(function () {
tween(wave3Asset, {
alpha: 0
}, {
duration: 750,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave3Asset.destroy();
// Start wave3
startWave3();
}
});
}, 1500);
}
});
} else if (currentWave === 3) {
// Show wave4 asset with fade in/out
var wave4Asset = game.addChild(LK.getAsset('Wave4', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave4 appears in front of all game objects
wave4Asset.zIndex = 1000;
// Fade in wave4 asset
tween(wave4Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out wave4 asset
tween(wave4Asset, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave4Asset.destroy();
// Start wave4
startWave4();
}
});
}
});
} else if (currentWave === 4) {
// Show wave5 asset with fade in/out
var wave5Asset = game.addChild(LK.getAsset('Wave5', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave5 appears in front of all game objects
wave5Asset.zIndex = 1000;
// Fade in wave5 asset
tween(wave5Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out wave5 asset
tween(wave5Asset, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave5Asset.destroy();
// Start wave5
startWave5();
}
});
}
});
} else if (currentWave === 5) {
// UFO boss defeated - transition to story5 cutscene
var story5Asset = game.addChild(LK.getAsset('Story5', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Fade in story5 asset
tween(story5Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Add click handler to story5 to transition to level2
story5Asset.down = function (x, y, obj) {
// Fade out story5 asset
tween(story5Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story5Asset.destroy();
// Start level2 with backdrop2
startLevel2();
}
});
};
}
});
} else {
waveActive = false;
}
};
}
// Start second wave of tower defense
function startWave2() {
calculateGuidelinePositions();
waveActive = true;
enemySpawnTimer = 0;
enemiesSpawned = 0;
maxEnemiesInWave = 20;
enemySpawnInterval = 300; // Spawn every 5 seconds at 60fps
currentWave = 2;
}
// Start third wave of tower defense
function startWave3() {
calculateGuidelinePositions();
waveActive = true;
enemySpawnTimer = 0;
enemiesSpawned = 0;
maxEnemiesInWave = 30;
enemySpawnInterval = 300; // Spawn every 5 seconds at 60fps (300 frames = 5 seconds at 60fps)
currentWave = 3;
}
// Start fourth wave of tower defense
function startWave4() {
calculateGuidelinePositions();
waveActive = true;
enemySpawnTimer = 0;
enemiesSpawned = 0;
maxEnemiesInWave = 40;
enemySpawnInterval = 180; // Spawn every 3 seconds at 60fps (180 frames = 3 seconds at 60fps)
currentWave = 4;
}
// Start fifth wave of tower defense (boss stage)
function startWave5() {
calculateGuidelinePositions();
waveActive = true;
enemySpawnTimer = 0;
enemiesSpawned = 0;
maxEnemiesInWave = 1; // Only 1 boss enemy
enemySpawnInterval = 60; // Spawn immediately
currentWave = 5;
}
// Spawn enemy on random guideline
function spawnEnemy() {
if (guidelineYPositions.length === 0) return;
// Pick random guideline with better distribution
var randomIndex = Math.floor(Math.random() * guidelineYPositions.length);
var spawnY = guidelineYPositions[randomIndex];
// Add some vertical variation to make movement less predictable
var verticalVariation = (Math.random() - 0.5) * 60; // +/- 30 pixels variation
spawnY += verticalVariation;
// Create new enemy
var enemy = new Enemy();
// Set asset type and health based on current wave
if (currentWave === 1) {
// Probedroids in wave 1 - make them slightly stronger
enemy.health = 150;
enemy.maxHealth = 150;
} else if (currentWave === 2) {
enemy.setAssetType('Drone');
enemy.health = 200;
enemy.maxHealth = 200;
enemy.speed = 2; // Reduced drone speed
} else if (currentWave === 3) {
enemy.setAssetType('SpaceDrone');
enemy.health = 250;
enemy.maxHealth = 250;
enemy.speed = 2.5; // Reduced SpaceDrone speed
} else if (currentWave === 4) {
enemy.setAssetType('Robot');
enemy.health = 350;
enemy.maxHealth = 350;
enemy.speed = 4; // Make robots slightly faster
} else if (currentWave === 5) {
enemy.setAssetType('UFO');
enemy.health = 5000;
enemy.maxHealth = 5000;
// UFO boss starts at top center of screen
enemy.x = 2048 / 2;
enemy.y = -200; // Start above screen
enemy.baseY = 2732 / 2; // Target center of screen
// Assign UFO boss to special guideline index -1 to indicate it can be targeted by all towers
enemy.guidelineIndex = -1;
// Add UFO to game before early return
enemies.push(enemy);
game.addChild(enemy);
return; // Skip normal positioning for UFO boss
}
enemy.x = 2048 + 100; // Start off right side of screen
enemy.y = spawnY;
// Store which guideline this enemy is following for reference
enemy.guidelineIndex = randomIndex;
enemy.baseY = guidelineYPositions[randomIndex];
enemies.push(enemy);
game.addChild(enemy);
// Play probedroid sound when first enemy appears
if (!firstEnemySpawned) {
LK.getSound('Probedroid').play();
firstEnemySpawned = true;
}
// Play probedroid2 sound when first robot appears in wave4
if (currentWave === 4 && enemiesSpawned === 0) {
LK.getSound('Probedroid2').play();
}
}
// Add tower placement move handler
game.move = function (x, y, obj) {
if (placingTower && towerPreview) {
// Find nearest snap position
var snapPos = findNearestSnapPosition(x, y);
towerPreview.x = snapPos.x;
towerPreview.y = snapPos.y;
// Update blade preview position if it exists
if (towerPreview.bladePreview) {
towerPreview.bladePreview.x = snapPos.x;
towerPreview.bladePreview.y = snapPos.y - 70;
}
if (confirmButton) {
confirmButton.x = snapPos.x + 60;
confirmButton.y = snapPos.y - 60;
}
// Update cancel button position if it exists
if (typeof cancelButton !== 'undefined' && cancelButton) {
cancelButton.x = snapPos.x + 60;
cancelButton.y = snapPos.y - 60 + 80 + 25 + 30;
}
}
// Handle slingshot aiming updates
for (var t = 0; t < towers.length; t++) {
var tower = towers[t];
if (tower.towerType === 'slingshot' && tower.isAiming) {
// Convert to global coordinates for consistent aiming
var globalPos = tower.toGlobal({
x: 0,
y: 0
});
var gamePos = game.toLocal(globalPos);
tower.aimEndX = x;
tower.aimEndY = y;
}
}
};
// Function to sort game objects by Y position (objects with higher Y appear in front)
function sortGameObjectsByY() {
// Collect all towers and enemies with their Y positions
var allObjects = [];
// Add towers to sorting array
for (var i = 0; i < towers.length; i++) {
if (towers[i].parent) {
allObjects.push({
object: towers[i],
y: towers[i].y,
type: 'tower'
});
}
}
// Add enemies to sorting array
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].parent) {
allObjects.push({
object: enemies[i],
y: enemies[i].y,
type: 'enemy'
});
}
}
// Sort by Y position (lower Y values first, higher Y values last)
// Objects with higher Y values will be rendered in front
allObjects.sort(function (a, b) {
return a.y - b.y;
});
// Set zIndex based on sorted order to ensure proper depth
for (var i = 0; i < allObjects.length; i++) {
var obj = allObjects[i].object;
if (obj.parent) {
// Set zIndex based on position in sorted array
// Higher index = rendered in front
obj.zIndex = i;
}
}
}
// Update tower defense game
game.update = function () {
// Sort game objects by Y position for proper depth ordering
// This ensures enemies and towers below others are rendered in front
sortGameObjectsByY();
// Only run tower defense logic when wave is active
if (!waveActive) return;
// Spawn enemies
if (enemiesSpawned < maxEnemiesInWave) {
enemySpawnTimer += gameSpeedMultiplier;
if (enemySpawnTimer >= enemySpawnInterval) {
spawnEnemy();
enemiesSpawned++;
enemySpawnTimer = 0;
}
}
// Check if wave is complete
if (enemiesSpawned >= maxEnemiesInWave && enemies.length === 0) {
waveActive = false;
// Award player $50 for completing wave
playerCash += 50;
if (cashDisplay) {
cashDisplay.setText('Cash: $' + playerCash);
}
// Wave complete - transition to next wave
if (currentWave === 1) {
// Show wave2 asset with fade in/out
var wave2Asset = game.addChild(LK.getAsset('Wave2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave2 appears in front of all game objects
wave2Asset.zIndex = 1000;
// Fade in wave2 asset
tween(wave2Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out wave2 asset
tween(wave2Asset, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave2Asset.destroy();
// Start wave2
startWave2();
}
});
}
});
} else if (currentWave === 2) {
// Show wave3 asset with fade in/out
var wave3Asset = game.addChild(LK.getAsset('Wave3', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave3 appears in front of all game objects
wave3Asset.zIndex = 1000;
// Fade in wave3 asset
tween(wave3Asset, {
alpha: 1
}, {
duration: 750,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Keep wave3 visible for 1.5 seconds then fade out
LK.setTimeout(function () {
tween(wave3Asset, {
alpha: 0
}, {
duration: 750,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave3Asset.destroy();
// Start wave3
startWave3();
}
});
}, 1500);
}
});
} else if (currentWave === 3) {
// Show wave4 asset with fade in/out
var wave4Asset = game.addChild(LK.getAsset('Wave4', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave4 appears in front of all game objects
wave4Asset.zIndex = 1000;
// Fade in wave4 asset
tween(wave4Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out wave4 asset
tween(wave4Asset, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave4Asset.destroy();
// Start wave4
startWave4();
}
});
}
});
} else if (currentWave === 4) {
// Show wave5 asset with fade in/out
var wave5Asset = game.addChild(LK.getAsset('Wave5', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Ensure wave5 appears in front of all game objects
wave5Asset.zIndex = 1000;
// Fade in wave5 asset
tween(wave5Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Fade out wave5 asset
tween(wave5Asset, {
alpha: 0
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
wave5Asset.destroy();
// Start wave5
startWave5();
}
});
}
});
} else if (currentWave === 5) {
// UFO boss defeated - transition to story5 cutscene
var story5Asset = game.addChild(LK.getAsset('Story5', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0
}));
// Fade in story5 asset
tween(story5Asset, {
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Add click handler to story5 to transition to level2
story5Asset.down = function (x, y, obj) {
// Fade out story5 asset
tween(story5Asset, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
story5Asset.destroy();
// Start level2 with backdrop2
startLevel2();
}
});
};
}
});
}
}
};
// Start the intro cutscene only if it hasn't been played yet and wave hasn't started
if (!introPlayed && !waveActive && currentWave === 1 && enemiesSpawned === 0) {
introPlayed = true;
introCutscene.play();
} else if (!waveActive && currentWave === 1 && enemiesSpawned === 0 && introPlayed) {
// If intro has been played, skip directly to backdrop scene
skipToBackdrop();
}
;
;
// Start level2 with backdrop2
function startLevel2() {
// Clear any existing game elements
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
// Reset game variables for level2
waveActive = false;
enemySpawnTimer = 0;
enemiesSpawned = 0;
maxEnemiesInWave = 10;
enemySpawnInterval = 420;
firstEnemySpawned = false;
currentWave = 1;
enemiesDefeated = 0;
// Add backdrop2 asset as the new background
var backdrop2Asset = game.addChild(LK.getAsset('Backdrop2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0,
scaleX: 1.0,
scaleY: 1.0
}));
// Fade in backdrop2
tween(backdrop2Asset, {
alpha: 1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Level2 is now ready - could start first wave or wait for player input
// For now, level1 is officially over and level2 has begun with backdrop2
}
});
// Recalculate guideline positions for level2
calculateGuidelinePositions();
// Add 6 horizontal guide lines for level2
var lineSpacing = 2732 / 7;
for (var i = 1; i <= 6; i++) {
var yPosition = lineSpacing * i;
if (i === 1) {
yPosition += 525; // Move guideline 1 down by 525 pixels (was 505)
} else if (i === 2) {
yPosition += 395; // Move guideline 2 down by 395 pixels (was 420)
} else if (i === 3) {
yPosition += 225; // Move guideline 3 down by 225 pixels (was 250)
} else if (i === 4) {
yPosition += 95; // Move guideline 4 down by 95 pixels (was 120)
} else if (i === 5) {
yPosition -= 95; // Move guideline 5 up by 95 pixels (was -75)
} else if (i === 6) {
yPosition -= 270; // Move guideline 6 up by 270 pixels (was -250)
}
var guideLine = game.addChild(LK.getAsset('guideLine', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: yPosition,
alpha: 0.5
}));
}
}
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
Fan blades symmetrical. In-Game asset. 2d. High contrast. No shadows
Plasma ball. 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
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 grey and say ??????
Make it say Wave 2
Make it say wave 3
Make it say wave 4
WiFi symbol. In-Game asset. 2d. High contrast. No shadows
explosion effect In-Game asset. 2d. High contrast. No shadows
Make it say wave 5
Remove laser beam
Make button hot pink and say 'Reflect $20'
Make button blue and change text to say 'Water $10' in a retro style font
Make button green and change test to say 'Gas $20'
Make button orange and change test to say 'Fire $40'
Make button very light blue and change test to say 'Air $30'
Make button gold and change text to say 'Electric $50'
Make button purple and change test to say 'Plasma $60'
Make button Teal and change test to say 'Slingshot $100'
Make button silver and change test to say 'WiFi $150'
Remove little kick so it's just a smooth oval shape
Make grid 6x8
Hand should be holding the gun by the Handle
Place laser cannon in both hands holding it like a shotgun
Make it stand still
Remove the words 5g
Make sure spelling in speech bubble is correct "We have found the earthlings weakness"
Fix the spelling of the word Planet
Slingshot. In-Game asset. 2d. High contrast. No shadows
Red button with a 'X' on it. In-Game asset. 2d. High contrast. No shadows
Green button with a tick on it
Fix the spelling of word saw
Display icon that says score sci fi comic style font. In-Game asset. 2d. High contrast. No shadows
Display icon that says cash sci fi comic style font. In-Game asset. 2d. High contrast. No shadows
Display icon that says X2 speed sci fi comic style font. In-Game asset. 2d. High contrast. No shadows
Make it say x1 speed and make the x1 blue
Canvasser
Sound effect
Alien1
Sound effect
Alien2
Sound effect
Alien3
Sound effect
Intro
Music
Probedroid
Sound effect
Probedroid2
Sound effect
Towerselect
Sound effect
Water
Sound effect
Explosion
Sound effect
Confirm
Sound effect
Fart
Sound effect
Electric
Sound effect
Fireball
Sound effect