User prompt
Revert all assets back how they were
User prompt
Extend the enemy system for level-specific behaviors
User prompt
Please fix the bug: 'waterTowerButton is not defined' in or related to this line: 'var towerMapping = {' Line Number: 958
User prompt
Please fix the bug: 'waterTowerButton is not defined' in or related to this line: 'var towerMapping = {' Line Number: 958
User prompt
Create level transition system ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Extend the asset management system for level-specific content
User prompt
Create global LevelManager instance and integrate with existing systems
User prompt
Create a LevelManager class or consolidated level management functions
User prompt
Add level state management to the existing game state system"**
User prompt
Add level state management to the existing game state system"** - Extend GAME_STATE to include current level tracking, level progression flags, and level completion status - Add level-specific variables like unlocked towers, level score, and completion time - Ensure the level state integrates seamlessly with the existing wave and player state management - Create level state reset functionality for when players restart levels ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Create level metadata like names, unlock requirements, and completion rewards - Ensure the configuration follows the existing pattern used for towers, enemies, and waves
User prompt
Define level-specific configurations including backgrounds, enemy pools, tower availability, wave counts, and special mechanics
User prompt
Create a level configuration system"** - Extend the existing GAME_CONFIG object to include a `levels` section
User prompt
What steps should I take now to remake level1
User prompt
Fix this please
User prompt
Ensure all global variables are properly organized and eliminate any remaining code duplication
User prompt
Ensure all global variables are properly organized and eliminate any remaining code duplication
User prompt
Review and optimize the game update loop to reduce redundant calculations and improve performance
User prompt
Remove redundant collision tracking variables and simplify the collision detection logic
User prompt
Consolidate asset initialization by grouping similar assets and using loops where appropriate
User prompt
Remove all debug console.log statements and unused variables throughout the codebase
User prompt
Extract common tower upgrade and interaction logic into reusable methods
User prompt
Merge similar click handlers and event listeners that follow the same pattern into consolidated functions
User prompt
Consolidate enemy asset management by extracting common cleanup and recreation logic from setAssetType methods
User prompt
Create a unified showWaveTransition() function instead of repeating the wave asset display logic
/**** * 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 // Stop tweens on cutscene assets if they exist if (self.intro2Asset) { tween.stop(self.intro2Asset); } if (self.cellAsset) { tween.stop(self.cellAsset); } if (self.skipIntroButton) { tween.stop(self.skipIntroButton); } if (self.story1Asset) { tween.stop(self.story1Asset); } if (self.story2Asset) { tween.stop(self.story2Asset); } if (self.story3Asset) { tween.stop(self.story3Asset); } if (self.story4Asset) { tween.stop(self.story4Asset); } }; 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 and reset collision flag self.lastX = self.x; 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; // Call comprehensive reset before showing game over resetGameCompletely(); 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.update(playerLives); } // Check for game over if (playerLives <= 0) { // Call comprehensive reset before showing game over resetGameCompletely(); 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.update(playerScore); } if (cashDisplay) { cashDisplay.update(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; // 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 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); var inRange = false; var targetDistance = distance; if (self.isTower1) { inRange = distance <= 650; } else if (self.towerType === 'fire') { inRange = Math.abs(dx) <= 800 && Math.abs(dy) <= 50; targetDistance = Math.abs(dx); } else if (self.towerType === 'dog') { inRange = dx >= 0 && dx <= 600 && Math.abs(dy) <= 50; targetDistance = dx; } else if (self.towerType === 'electric') { inRange = dx >= 0 && dx <= 500 && Math.abs(dy) <= 50; targetDistance = dx; } else { inRange = distance <= self.range; } if (inRange && targetDistance < nearestDistance) { nearestDistance = targetDistance; 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 + 20, y: self.y + 25 })); } // 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 }); // 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; // Convert local coordinates to game coordinates for consistent aiming var gamePos = game.toLocal(self.toGlobal({ x: x, y: y })); self.aimStartX = gamePos.x; self.aimStartY = gamePos.y; self.aimEndX = gamePos.x; // Initialize end position self.aimEndY = gamePos.y; } else {} }; 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; // Play electric sound for plasma damage LK.getSound('Electric').play(); // 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) { // Play wifi sound when attacking LK.getSound('Wifi').play(); // 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') { // Initialize aiming properties if not set if (self.isAiming === undefined) { self.isAiming = false; self.aimStartX = 0; self.aimStartY = 0; self.aimEndX = 0; self.aimEndY = 0; } // Handle aiming visualization during drag if (self.isAiming) { // Calculate aim direction for visual feedback var aimDx = self.aimStartX - self.aimEndX; var aimDy = self.aimStartY - self.aimEndY; var aimDistance = Math.sqrt(aimDx * aimDx + aimDy * aimDy); // Visual feedback - tilt slingshot based on aim direction if (aimDistance > 10) { var aimAngle = Math.atan2(aimDy, aimDx); // Limit rotation to reasonable range var maxRotation = Math.PI / 6; // 30 degrees var rotation = Math.max(-maxRotation, Math.min(maxRotation, aimAngle * 0.3)); self.rotation = rotation; } } else { // Reset rotation when not aiming self.rotation = 0; } 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); } }; // Move handler for slingshot aiming updates self.move = function (x, y, obj) { if (self.towerType === 'slingshot' && self.isAiming) { // Update end position during drag var gamePos = game.toLocal(self.toGlobal({ x: x, y: y })); self.aimEndX = gamePos.x; self.aimEndY = gamePos.y; } }; // Up handler for slingshot aiming self.up = function (x, y, obj) { if (self.towerType === 'slingshot' && self.isAiming) { // Convert to game coordinates for final aim position var gamePos = game.toLocal(self.toGlobal({ x: x, y: y })); self.aimEndX = gamePos.x; self.aimEndY = gamePos.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); // Play slingshot sound when firing LK.getSound('Slingshot').play(); // 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 ****/ // Initialize music // 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; // Consistent starting cash 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 // Track timeouts for story transitions so they can be cancelled var storyTimeouts = []; // 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 }); // Play intro music when cell appears LK.playMusic('Intro', { loop: false }); // 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 var cellToStory1Timeout = 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() { // Hide skip intro button during story scenes if (introCutscene.skipIntroButton) { introCutscene.skipIntroButton.alpha = 0; } // Store story1 reference for cleanup introCutscene.story1Asset = story1Asset; // Auto-transition to story2 after 3 seconds var story1ToStory2Timeout = 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 var story2ToStory3Timeout = 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 var story3ToStory4Timeout = 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 var story4CompletionTimeout = 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); storyTimeouts.push(story4CompletionTimeout); } }); } }); }, 3000); storyTimeouts.push(story3ToStory4Timeout); } }); } }); }, 3000); storyTimeouts.push(story2ToStory3Timeout); } }); } }); }, 3000); storyTimeouts.push(story1ToStory2Timeout); } }); } }); }, 5000); // Store timeout reference for cancellation storyTimeouts.push(cellToStory1Timeout); } }); // 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; // Cancel all pending story timeouts to prevent them from executing for (var i = 0; i < storyTimeouts.length; i++) { LK.clearTimeout(storyTimeouts[i]); } storyTimeouts = []; // Clear the array // Clean up intro2 assets tween.stop(intro2Asset); tween.stop(cellAsset); tween.stop(skipIntroButton); intro2Asset.destroy(); cellAsset.destroy(); skipIntroButton.destroy(); // Destroy story scenes 1-4 if they exist to prevent them from appearing later if (introCutscene.story1Asset && introCutscene.story1Asset.parent) { introCutscene.story1Asset.destroy(); introCutscene.story1Asset = null; } if (introCutscene.story2Asset && introCutscene.story2Asset.parent) { introCutscene.story2Asset.destroy(); introCutscene.story2Asset = null; } if (introCutscene.story3Asset && introCutscene.story3Asset.parent) { introCutscene.story3Asset.destroy(); introCutscene.story3Asset = null; } if (introCutscene.story4Asset && introCutscene.story4Asset.parent) { introCutscene.story4Asset.destroy(); introCutscene.story4Asset = null; } // Skip directly to backdrop scene instead of title screen skipToBackdrop(); }; // 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() { // Reset intro played flag so next game start goes to backdrop introPlayed = true; // 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); // Show skip intro button again if it was hidden during story scenes if (introCutscene.skipIntroButton) { introCutscene.skipIntroButton.alpha = 1; } // Add touch event to skip button skipButton.down = function (x, y, obj) { // Cancel all pending story timeouts to prevent them from executing for (var i = 0; i < storyTimeouts.length; i++) { LK.clearTimeout(storyTimeouts[i]); } storyTimeouts = []; // Clear the array // Destroy story scenes 1-4 if they exist to prevent them from appearing later if (introCutscene.story1Asset && introCutscene.story1Asset.parent) { introCutscene.story1Asset.destroy(); introCutscene.story1Asset = null; } if (introCutscene.story2Asset && introCutscene.story2Asset.parent) { introCutscene.story2Asset.destroy(); introCutscene.story2Asset = null; } if (introCutscene.story3Asset && introCutscene.story3Asset.parent) { introCutscene.story3Asset.destroy(); introCutscene.story3Asset = null; } if (introCutscene.story4Asset && introCutscene.story4Asset.parent) { introCutscene.story4Asset.destroy(); introCutscene.story4Asset = null; } // Cancel any running title screen animations and skip directly to backdrop tween.stopAll(); 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 = []; } // Global UI container management var uiContainer = null; // Function to create UI container function createUIContainer() { if (uiContainer && uiContainer.parent) { cleanupUIContainer(); } uiContainer = new Container(); LK.gui.addChild(uiContainer); } // Function to clean up UI container function cleanupUIContainer() { if (uiContainer && uiContainer.parent) { // Destroy all children first while (uiContainer.children && uiContainer.children.length > 0) { var child = uiContainer.children[0]; if (child && child.destroy) { child.destroy(); } } // Remove from parent and destroy container uiContainer.destroy(); } // Always reset to null regardless of parent state uiContainer = null; } // Comprehensive game reset function that is called just before game over function resetGameCompletely() { // Stop tweens on specific objects that might be tweening if (game && game.children) { for (var j = 0; j < game.children.length; j++) { var child = game.children[j]; if (child) { tween.stop(child); } } } // Stop tweens on enemies for (var k = 0; k < enemies.length; k++) { if (enemies[k]) { tween.stop(enemies[k]); } } // Stop tweens on towers for (var l = 0; l < towers.length; l++) { if (towers[l]) { tween.stop(towers[l]); } } // Clear all intervals and timeouts for (var i = 0; i < storyTimeouts.length; i++) { LK.clearTimeout(storyTimeouts[i]); } storyTimeouts = []; // Clear all enemies for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] && enemies[i].destroy) { enemies[i].destroy(); } } enemies = []; // Clear all towers for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] && towers[i].destroy) { towers[i].destroy(); } } towers = []; // Clean up placement mode placingTower = false; if (towerPreview) { if (towerPreview.bladePreview) { towerPreview.bladePreview.destroy(); } towerPreview.destroy(); towerPreview = null; } if (confirmButton) { confirmButton.destroy(); confirmButton = null; } if (cancelButton) { cancelButton.destroy(); cancelButton = null; } // Hide guidelines hideVerticalGuidelines(); // Complete LK.gui cleanup - remove all children if (LK.gui && LK.gui.children) { while (LK.gui.children.length > 0) { var child = LK.gui.children[0]; try { if (child && child.destroy) { child.destroy(); } else if (child && child.parent) { LK.gui.removeChild(child); } } catch (e) { // Force remove if destroy fails try { if (child && child.parent) { LK.gui.removeChild(child); } } catch (e2) { // Skip if both fail - continue with loop } } } } // Clean up UI container cleanupUIContainer(); // Reset all game state variables to initial values waveActive = false; enemySpawnTimer = 0; enemySpawnInterval = 420; enemiesSpawned = 0; maxEnemiesInWave = 10; firstEnemySpawned = false; currentWave = 1; enemiesDefeated = 0; playerScore = 0; playerLives = 10; playerCash = 50; gameSpeedMultiplier = 1; introPlayed = false; gameStarted = false; pendingTowerCost = 10; // Clear display references scoreDisplay = null; livesDisplay = null; cashDisplay = null; // Clear guideline positions guidelineYPositions = []; snapPositions = []; verticalGuidelines = []; } // Clean up completely when game resets LK.on('gameReset', function () { // Call comprehensive reset function to ensure everything is cleaned up resetGameCompletely(); // Recreate UI container for fresh start createUIContainer(); }); // Initialize UI container at game start createUIContainer(); // 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; // Ensure UI container exists if (!uiContainer) { createUIContainer(); } // Function to create digit display using number assets function createDigitDisplay(number, x, y, scale) { var digits = []; var numStr = number.toString(); var digitSpacing = 60; var startX = x - (numStr.length - 1) * digitSpacing / 2; for (var i = 0; i < numStr.length; i++) { var digitChar = numStr[i]; var assetName; // Map digits to asset names switch (digitChar) { case '0': assetName = 'Zero'; break; case '1': assetName = 'One'; break; case '2': assetName = 'Two'; break; case '3': assetName = 'Three'; break; case '4': assetName = 'Four'; break; case '5': assetName = 'Five'; break; case '6': assetName = 'Six'; break; case '7': assetName = 'Seven'; break; case '8': assetName = 'Eight'; break; case '9': assetName = 'Nine'; break; default: assetName = 'Zero'; break; } var digit = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5, x: startX + i * digitSpacing, y: y, scaleX: scale || 1, scaleY: scale || 1 }); uiContainer.addChild(digit); digits.push(digit); } return { digits: digits, update: function update(newNumber) { // Destroy old digits for (var j = 0; j < digits.length; j++) { if (digits[j] && digits[j].parent) { digits[j].destroy(); } } digits = []; // Create new digits var newNumStr = newNumber.toString(); var newStartX = x - (newNumStr.length - 1) * digitSpacing / 2; for (var k = 0; k < newNumStr.length; k++) { var digitChar = newNumStr[k]; var assetName; switch (digitChar) { case '0': assetName = 'Zero'; break; case '1': assetName = 'One'; break; case '2': assetName = 'Two'; break; case '3': assetName = 'Three'; break; case '4': assetName = 'Four'; break; case '5': assetName = 'Five'; break; case '6': assetName = 'Six'; break; case '7': assetName = 'Seven'; break; case '8': assetName = 'Eight'; break; case '9': assetName = 'Nine'; break; default: assetName = 'Zero'; break; } var digit = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5, x: newStartX + k * digitSpacing, y: y, scaleX: scale || 1, scaleY: scale || 1 }); uiContainer.addChild(digit); digits.push(digit); } }, destroy: function destroy() { for (var j = 0; j < digits.length; j++) { if (digits[j] && digits[j].parent) { digits[j].destroy(); } } digits = []; } }; } // Add score display at top left using Score asset as label var scoreLabel = LK.getAsset('Score', { anchorX: 0, anchorY: 0, x: 650, y: 50 }); uiContainer.addChild(scoreLabel); // Create digit display for score scoreDisplay = createDigitDisplay(playerScore, 750, 175, 0.8); // Add cash display using Cash asset as label var cashLabel = LK.getAsset('Cash', { anchorX: 0.5, anchorY: 0.5, x: 1175, y: 170 }); uiContainer.addChild(cashLabel); // Create digit display for cash cashDisplay = createDigitDisplay(playerCash, 1324, 170, 0.6); // Add lives asset as label for lives digit display var livesLabel = LK.getAsset('Lives', { anchorX: 0.5, anchorY: 0.5, x: 1224, y: 50 }); uiContainer.addChild(livesLabel); // Create digit display for lives livesDisplay = createDigitDisplay(playerLives, 1324, 50, 0.7); // Add speed up button to top of screen left of score asset using X1speed asset var speedUpButton = LK.getAsset('X1speed', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 75 }); uiContainer.addChild(speedUpButton); // Function to update speed button appearance and add click handler function updateSpeedButton() { speedUpButton.down = function (x, y, obj) { if (gameSpeedMultiplier === 1) { // Switch to double speed gameSpeedMultiplier = 2; // Replace with X2speed asset speedUpButton.destroy(); speedUpButton = LK.getAsset('X2speed', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 75 }); uiContainer.addChild(speedUpButton); updateSpeedButton(); // Re-add click handler to new button } else { // Switch to normal speed gameSpeedMultiplier = 1; // Replace with X1speed asset speedUpButton.destroy(); speedUpButton = LK.getAsset('X1speed', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 75 }); uiContainer.addChild(speedUpButton); updateSpeedButton(); // Re-add click handler to new button } }; } // Initialize the speed button with click handler updateSpeedButton(); // 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 = 420; uiContainer.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; } }; // Tower button configuration var towerButtonConfig = { y: 2400, spacing: 50, startX: 430, scale: 0.8 }; // Helper function to create tower buttons function createTowerButton(assetName, x, y, cost, towerType, towerAsset, properties) { var button = game.addChild(LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: towerButtonConfig.scale, scaleY: towerButtonConfig.scale })); button.down = function (bx, by, obj) { if (playerCash >= cost && !placingTower) { LK.getSound('Towerselect').play(); startTowerPlacement(cost, towerType, towerAsset, properties); } }; return button; } // Helper function to start tower placement function startTowerPlacement(cost, towerType, towerAsset, properties) { placingTower = true; pendingTowerCost = cost; calculateSnapPositions(); showVerticalGuidelines(); createTowerPreview(towerAsset, towerType, properties); } // Helper function to create tower preview function createTowerPreview(towerAsset, towerType, properties) { var snapPos = findNearestSnapPosition(1024, 1366); towerPreview = game.addChild(LK.getAsset(towerAsset, { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y, alpha: 0.7 })); if (towerType === 'air') { towerPreview.bladePreview = game.addChild(LK.getAsset('Blades', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x, y: snapPos.y - 70, alpha: 0.7 })); } createConfirmCancelButtons(snapPos); } // Helper function to create confirm/cancel buttons function createConfirmCancelButtons(snapPos) { confirmButton = game.addChild(LK.getAsset('ConfirmButton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60, zIndex: 2000 })); cancelButton = game.addChild(LK.getAsset('Cancelbutton', { anchorX: 0.5, anchorY: 0.5, x: snapPos.x + 60, y: snapPos.y - 60 + 155, zIndex: 2000 })); cancelButton.down = function () { cleanupTowerPlacement(); }; confirmButton.down = function () { confirmTowerPlacement(); }; } // Helper function to cleanup tower placement function cleanupTowerPlacement() { if (towerPreview) { if (towerPreview.bladePreview) { towerPreview.bladePreview.destroy(); } towerPreview.destroy(); towerPreview = null; } if (confirmButton) { confirmButton.destroy(); confirmButton = null; } if (cancelButton) { cancelButton.destroy(); cancelButton = null; } hideVerticalGuidelines(); placingTower = false; } // Helper function to confirm tower placement function confirmTowerPlacement() { if (!placingTower || !towerPreview) return; playerCash -= pendingTowerCost; if (cashDisplay) { cashDisplay.update(playerCash); } var newTower = createTowerFromPreview(); game.addChild(newTower); towers.push(newTower); sortGameObjectsByY(); cleanupTowerPlacement(); pendingTowerCost = 10; LK.getSound('Confirm').play(); } // Helper function to create tower from preview function createTowerFromPreview() { var newTower = new Tower(); newTower.x = towerPreview.x; newTower.y = towerPreview.y; newTower.cost = pendingTowerCost; // Set tower type and properties based on preview if (towerPreview.bladePreview) { newTower.setAssetType('Fan'); newTower.towerType = 'air'; newTower.damage = 35; newTower.range = 350; newTower.fireRate = 30; } // Assign 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; return newTower; } // Define tower button positioning variables var startX = 430; var towerButtonY = 2400; var buttonSpacing = 50; // 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 })); // WiFi Tower Button (to the right of plasma button) var wifiTowerButton = game.addChild(LK.getAsset('Wifibutton', { anchorX: 0.5, anchorY: 0.5, x: startX + electricTowerButton.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 + 400, y: towerButtonY, scaleX: 0.8, scaleY: 0.8 })); // Slingshot Tower Button (to the right of wifi button) var slingshotTowerButton = game.addChild(LK.getAsset('Slingshotbutton', { anchorX: 0.5, anchorY: 0.5, x: startX + electricTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing + wifiTowerButton.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, zIndex: 2000 })); // 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 zIndex: 2000 })); // 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.update(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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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 >= 80 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for wifi tower placingTower = true; pendingTowerCost = 80; // WiFi tower costs $80 // 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.update(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; newTower.range = 600; newTower.fireRate = 30; // 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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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.update(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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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.update(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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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.update(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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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.update(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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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.update(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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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(); } }; } }; slingshotTowerButton.down = function (x, y, obj) { if (playerCash >= 35 && !placingTower) { LK.getSound('Towerselect').play(); // Start tower placement mode for slingshot tower placingTower = true; pendingTowerCost = 35; // Slingshot tower costs $35 // 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.update(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 = 80; newTower.range = 1300; newTower.fireRate = 120; // Slower shooting (manual aiming) // 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); // Sort game objects immediately after adding tower to ensure proper depth rendering sortGameObjectsByY(); // 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(); } }; } }; // 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; } } // Slingshot aiming is now handled in Tower class move handler }; // Function to sort game objects by Y position (objects with lower 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 (higher Y values first, lower Y values last) // Objects with higher Y values (lower on screen) will be rendered behind (lower zIndex) // Objects with lower Y values (higher on screen) will be rendered in front (higher zIndex) allObjects.sort(function (a, b) { var yDiff = a.y - b.y; // Normal sort: lower Y first, higher Y last if (yDiff !== 0) { return yDiff; } // If Y positions are equal, maintain consistent ordering by type return a.type === 'tower' ? -1 : 1; }); // Set zIndex based on sorted order to ensure proper depth // Start with a lower base zIndex to avoid conflicts with UI elements like confirm/cancel buttons var baseZIndex = 50; for (var i = 0; i < allObjects.length; i++) { var obj = allObjects[i].object; if (obj.parent) { // Set zIndex based on position in sorted array // Objects earlier in the array (lower Y positions) get higher zIndex (rendered in front) // Objects later in the array (higher Y positions) get lower zIndex (rendered behind) var newZIndex = baseZIndex + (allObjects.length - i); obj.zIndex = newZIndex; // Force immediate visual update by temporarily removing and re-adding to parent var parent = obj.parent; parent.removeChild(obj); parent.addChild(obj); } } } // Update tower defense game game.update = function () { // Only run tower defense logic when wave is active if (!waveActive) return; // Sort game objects by Y position for proper depth ordering every frame during active wave // This ensures enemies and towers below others are rendered in front sortGameObjectsByY(); // 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) { // Wave complete - transition to next wave // Helper function to show wave transition var showWaveTransition = function showWaveTransition(waveNumber, callback) { var waveAsset = game.addChild(LK.getAsset('Wave' + waveNumber, { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0, zIndex: 1000 })); var duration = waveNumber === 3 ? 750 : 1500; var holdTime = waveNumber === 3 ? 1500 : 0; tween(waveAsset, { alpha: 1 }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { var fadeOut = function fadeOut() { tween(waveAsset, { alpha: 0 }, { duration: duration, easing: tween.easeInOut, onFinish: function onFinish() { waveAsset.destroy(); callback(); } }); }; if (holdTime > 0) { LK.setTimeout(fadeOut, holdTime); } else { fadeOut(); } } }); }; waveActive = false; // Award player $50 for completing wave playerCash += 50; if (cashDisplay) { cashDisplay.update(playerCash); } if (currentWave === 1) { showWaveTransition(2, 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 && !gameStarted) { introPlayed = true; introCutscene.play(); } else if (!waveActive && currentWave === 1 && enemiesSpawned === 0 && (introPlayed || gameStarted)) { // If intro has been played or game has been reset, 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 })); } }
===================================================================
--- original.js
+++ change.js
@@ -118,9 +118,9 @@
self.maxHealth = 100;
self.speed = 1.5;
self.targetX = -100; // Move to left side of screen
self.lastX = self.x;
- self.lastCollisionState = false; // Unified collision tracking
+ 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,
@@ -133,31 +133,23 @@
x: -30,
y: -80
});
self.update = function () {
- // Cache frequently used values
- var currentTick = LK.ticks;
- var speedMultiplier = typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1;
- var towersLength = towers.length;
- // Store last position and check current collision state
+ // Store last position and reset collision flag
self.lastX = self.x;
- var currentCollisionState = false;
- // Optimize collision detection - only check every other frame for non-critical enemies
- var shouldCheckCollision = self.isUFOBoss || currentTick % 2 === 0;
- if (shouldCheckCollision) {
- // Check collision with towers on same guideline that are directly to the left
- for (var i = 0; i < towersLength; 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)) {
- currentCollisionState = true;
- break;
- }
+ 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 && !currentCollisionState) {
- self.x -= self.speed * speedMultiplier;
+ 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;
@@ -166,34 +158,27 @@
if (Math.abs(yDifference) > 2) {
self.y += yDifference * 0.1; // Adjust 0.1 to control how quickly enemies center on guidelines
}
}
- // Update health bar only when health changes or every 10th frame
+ // Update health bar
var healthPercentage = self.health / self.maxHealth;
- if (self.lastHealthPercentage === undefined || self.lastHealthPercentage !== healthPercentage || currentTick % 10 === 0) {
- 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
- }
- self.lastHealthPercentage = healthPercentage;
+ 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
}
- // Consolidated enemy shooting logic using parameters
- self.performEnemyShooting = function (config) {
+ // SpaceDrone shooting logic
+ if (self.assetType === 'SpaceDrone') {
// Initialize shooting properties if not set
if (self.lastShotFrame === undefined) {
self.lastShotFrame = 0;
- self.shootingRange = config.range;
- self.fireRate = config.fireRate;
+ self.shootingRange = 400;
+ self.fireRate = 45; // Shoot every 0.75 seconds at 60fps
}
- // Handle special animations (like wheel rotation for robots)
- if (config.specialAnimation && !self.collidingWithTower) {
- config.specialAnimation();
- }
// Find nearest tower to shoot at
var nearestTower = null;
var nearestDistance = Infinity;
for (var t = 0; t < towers.length; t++) {
@@ -212,35 +197,36 @@
}
// 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 projectile
- var projectile = game.addChild(LK.getAsset(config.projectileAsset, {
+ // Create red bullet
+ var bullet = game.addChild(LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
- tint: config.projectileTint || 0xFFFFFF
+ 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 projectile to target tower
- tween(projectile, {
+ // Animate bullet to target tower
+ tween(bullet, {
x: nearestTower.x,
y: nearestTower.y
}, {
- duration: distance / config.projectileSpeed,
+ duration: distance / 3,
+ // Bullet speed
easing: tween.linear,
onFinish: function onFinish() {
- projectile.destroy();
+ bullet.destroy();
// Deal damage to target tower
if (nearestTower && nearestTower.parent) {
- nearestTower.health -= config.damage;
- // Flash tower to show damage
+ nearestTower.health -= 25; // SpaceDrone bullets deal 25 damage
+ // Flash tower red to show damage
tween(nearestTower, {
- tint: config.damageFlashColor
+ tint: 0xFF0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
@@ -292,48 +278,122 @@
}
});
self.lastShotFrame = LK.ticks;
}
- };
- // Data-driven enemy shooting logic
- var enemyConfig = GAME_CONFIG.enemies[self.assetType];
- if (enemyConfig && enemyConfig.shootingRange) {
- var shootingConfig = {
- range: enemyConfig.shootingRange,
- fireRate: enemyConfig.fireRate,
- damage: enemyConfig.damage
- };
- if (enemyConfig.shootingConfig) {
- if (enemyConfig.shootingConfig.projectileAsset) {
- shootingConfig.projectileAsset = enemyConfig.shootingConfig.projectileAsset;
+ }
+ // 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;
+ }
}
- if (enemyConfig.shootingConfig.projectileSpeed) {
- shootingConfig.projectileSpeed = enemyConfig.shootingConfig.projectileSpeed;
- }
- if (enemyConfig.shootingConfig.projectileTint) {
- shootingConfig.projectileTint = enemyConfig.shootingConfig.projectileTint;
- }
- if (enemyConfig.shootingConfig.damageFlashColor) {
- shootingConfig.damageFlashColor = enemyConfig.shootingConfig.damageFlashColor;
- }
}
- // Handle special animations that cannot be in config
- if (self.assetType === 'Robot') {
- shootingConfig.specialAnimation = function specialAnimation() {
- if (self.leftWheel && self.rightWheel) {
- self.leftWheel.rotation -= 0.15;
- self.rightWheel.rotation -= 0.15;
+ // 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;
}
- // Default projectile if not specified
- if (!shootingConfig.projectileAsset) {
- shootingConfig.projectileAsset = 'bullet';
- }
- if (!shootingConfig.projectileSpeed) {
- shootingConfig.projectileSpeed = 2;
- }
- self.performEnemyShooting(shootingConfig);
}
// UFO boss special behavior - simplified sequential abduction
if (self.isUFOBoss) {
// Initialize UFO properties if not set
@@ -528,9 +588,9 @@
});
}
// Check if UFO reached goal
if (self.headingToGoal && self.lastX > 0 && self.x <= 0) {
- gameState.player.lives = 0;
+ playerLives = 0;
// Call comprehensive reset before showing game over
resetGameCompletely();
LK.showGameOver();
return;
@@ -541,15 +601,15 @@
if (self.lastX > 0 && self.x <= 0) {
// Enemy reached the end - remove from game and reduce lives
if (self.parent) {
// Reduce player lives
- gameState.player.lives--;
+ playerLives--;
// Update lives display if it exists
- if (gameDisplays.lives) {
- gameDisplays.lives.update(gameState.player.lives);
+ if (livesDisplay) {
+ livesDisplay.update(playerLives);
}
// Check for game over
- if (gameState.player.lives <= 0) {
+ if (playerLives <= 0) {
// Call comprehensive reset before showing game over
resetGameCompletely();
LK.showGameOver();
}
@@ -567,16 +627,16 @@
// Method to defeat this enemy
self.defeat = function () {
if (self.parent) {
// Award player 50 points and $10 cash
- gameState.player.score += 50;
- gameState.player.cash += 10;
+ playerScore += 50;
+ playerCash += 10;
// Update displays if they exist
- if (gameDisplays.score) {
- gameDisplays.score.update(gameState.player.score);
+ if (scoreDisplay) {
+ scoreDisplay.update(playerScore);
}
- if (gameDisplays.cash) {
- gameDisplays.cash.update(gameState.player.cash);
+ if (cashDisplay) {
+ cashDisplay.update(playerCash);
}
// Create explosion effect at enemy position
var explosionEffect = game.addChild(LK.getAsset('explosion', {
anchorX: 0.5,
@@ -625,10 +685,20 @@
easing: tween.easeInOut,
onFinish: function onFinish() {
// Add click handler to story5 to transition to level2
story5Asset.down = function (x, y, obj) {
- story5Asset.assetType = 'Story5';
- handleGameClick(x, y, story5Asset, 'storyTransition');
+ // Fade out story5 asset
+ tween(story5Asset, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ story5Asset.destroy();
+ // Start level2 with backdrop2
+ startLevel2();
+ }
+ });
};
}
});
}
@@ -642,612 +712,90 @@
}
}
};
self.setAssetType = function (assetType) {
- // Use consolidated cleanup function
- cleanupEnemyAssets(self);
- // Store reference for cleanup function
- self.enemyGraphics = enemyGraphics;
- // Use consolidated recreation function
- recreateEnemyAssets(self, assetType);
- // Update main graphics reference
- enemyGraphics = self.enemyGraphics;
- // 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 (enemyGraphics && enemyGraphics.parent) {
+ enemyGraphics.destroy();
}
- if (self.healthBarForeground && self.healthBarForeground.parent) {
- self.removeChild(self.healthBarForeground);
- self.addChild(self.healthBarForeground);
+ // Clean up existing wheel graphics if they exist
+ if (self.leftWheel && self.leftWheel.parent) {
+ self.leftWheel.destroy();
+ self.leftWheel = null;
}
- };
- return self;
-});
-// LevelManager class for consolidated level management
-var LevelManager = Container.expand(function () {
- var self = Container.call(this);
- // Level state properties
- self.currentLevel = 1;
- self.levelInProgress = false;
- self.levelStartTime = 0;
- self.levelData = null;
- // Initialize a level with all required setup
- self.initializeLevel = function (levelNumber) {
- if (!GAME_CONFIG.levels[levelNumber]) {
- console.error('Level ' + levelNumber + ' not found in configuration');
- return false;
+ if (self.rightWheel && self.rightWheel.parent) {
+ self.rightWheel.destroy();
+ self.rightWheel = null;
}
- var levelConfig = GAME_CONFIG.levels[levelNumber];
- self.currentLevel = levelNumber;
- self.levelInProgress = true;
- self.levelStartTime = Date.now();
- self.levelData = levelConfig;
- // Update game state with level information
- gameState.level.current = levelNumber;
- gameState.level.inProgress = true;
- gameState.level.startTime = self.levelStartTime;
- // Reset level state and load assets
- self.resetLevelState();
- self.loadLevelAssets(levelConfig);
- self.setupLevelMechanics(levelConfig);
- self.updateAvailableTowers(levelConfig.availableTowers);
- return true;
- };
- // Reset game state for new level
- self.resetLevelState = function () {
- // Clear existing enemies and towers
- for (var i = enemies.length - 1; i >= 0; i--) {
- if (enemies[i] && enemies[i].destroy) {
- enemies[i].destroy();
- }
+ // Clean up existing roboarm graphics if they exist
+ if (self.roboarm && self.roboarm.parent) {
+ self.roboarm.destroy();
+ self.roboarm = null;
}
- enemies = [];
- for (var i = towers.length - 1; i >= 0; i--) {
- if (towers[i] && towers[i].destroy) {
- towers[i].destroy();
- }
- }
- towers = [];
- // Reset wave state
- gameState.wave.active = false;
- gameState.wave.current = 1;
- gameState.wave.enemiesSpawned = 0;
- gameState.wave.enemiesDefeated = 0;
- gameState.wave.spawnTimer = 0;
- gameState.wave.firstEnemySpawned = false;
- // Reset legacy variables
- waveActive = false;
- currentWave = 1;
- enemiesSpawned = 0;
- enemiesDefeated = 0;
- enemySpawnTimer = 0;
- firstEnemySpawned = false;
- // Apply level-specific starting resources
- var levelConfig = GAME_CONFIG.levels[self.currentLevel];
- if (levelConfig.startingResources) {
- gameState.player.cash = levelConfig.startingResources.cash || 50;
- gameState.player.lives = levelConfig.startingResources.lives || 10;
- } else {
- gameState.player.cash = 50;
- gameState.player.lives = 10;
- }
- // Update legacy variables
- playerCash = gameState.player.cash;
- // Update displays if they exist
- if (cashDisplay) {
- cashDisplay.update(gameState.player.cash);
- }
- if (livesDisplay) {
- livesDisplay.update(gameState.player.lives);
- }
- };
- // Load level-specific assets and background
- self.loadLevelAssets = function (levelConfig) {
- // Set background
- if (levelConfig.background) {
- var background = game.addChild(LK.getAsset(levelConfig.background.asset, {
+ 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: levelConfig.background.position.x,
- y: levelConfig.background.position.y,
- scaleX: levelConfig.background.scale.x,
- scaleY: levelConfig.background.scale.y,
- alpha: 0
- }));
- // Fade in background
- tween(background, {
- alpha: 1
+ 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: 2000,
+ duration: 1000,
easing: tween.easeInOut
});
- // Store background reference
- gameState.level.state.backgroundAsset = background;
- }
- // Setup guidelines for the level
- self.setupGuidelines(levelConfig);
- };
- // Setup level-specific mechanics and modifiers
- self.setupLevelMechanics = function (levelConfig) {
- // Apply tower cost modifiers
- if (levelConfig.towerCostModifier && levelConfig.towerCostModifier !== 1.0) {
- gameState.level.configuration.towerCostModifier = levelConfig.towerCostModifier;
- // Apply cost modifier to existing tower configurations
- for (var towerType in GAME_CONFIG.towers) {
- var tower = GAME_CONFIG.towers[towerType];
- tower.modifiedCost = Math.floor(tower.cost * levelConfig.towerCostModifier);
- }
- }
- // Apply enemy health and speed modifiers
- if (levelConfig.specialMechanics) {
- gameState.level.configuration.enemyHealthMultiplier = levelConfig.specialMechanics.increasedEnemyHealth || 1.0;
- gameState.level.configuration.enemySpeedMultiplier = levelConfig.specialMechanics.increasedEnemySpeed || 1.0;
- gameState.level.configuration.specialMechanicsEnabled = true;
- // Store active enemy pools and special rules
- gameState.level.state.activeEnemyPools = levelConfig.enemyPools || [];
- gameState.level.state.specialRules = levelConfig.specialMechanics;
- }
- // Setup wave configuration for this level
- if (levelConfig.waves) {
- gameState.level.state.waveConfiguration = levelConfig.waves;
- // Update current wave limits based on level config
- if (levelConfig.waves[1]) {
- maxEnemiesInWave = levelConfig.waves[1].maxEnemies;
- enemySpawnInterval = levelConfig.waves[1].spawnInterval;
- gameState.wave.maxEnemies = maxEnemiesInWave;
- gameState.wave.spawnInterval = enemySpawnInterval;
- }
- }
- };
- // Setup level-specific guidelines
- self.setupGuidelines = function (levelConfig) {
- // Clear existing guideline positions
- gameObjects.guidelineYPositions = [];
- // Create guidelines based on level layout
- var guidelineLayout = levelConfig.specialMechanics && levelConfig.specialMechanics.guidelineLayout || 'standard';
- self.createGuidelineLayout(guidelineLayout);
- };
- // Create different guideline layouts based on level
- self.createGuidelineLayout = function (layoutType) {
- var lineSpacing = GAME_CONFIG.guidelines.screenHeight / 7;
- for (var i = 1; i <= GAME_CONFIG.guidelines.horizontalCount; i++) {
- var yPosition = lineSpacing * i;
- // Apply layout-specific modifications
- if (layoutType === 'industrial') {
- // Tighter spacing for industrial level
- yPosition = yPosition * 0.9;
- } else if (layoutType === 'zero_gravity') {
- // Curved guidelines for space level
- yPosition += Math.sin(i * 0.5) * 100;
- }
- // Apply standard offsets
- if (GAME_CONFIG.guidelines.offsets[i]) {
- yPosition += GAME_CONFIG.guidelines.offsets[i];
- }
- // Create guideline visual
- var guideLine = game.addChild(LK.getAsset('guideLine', {
+ // Add right wheel
+ self.rightWheel = self.attachAsset('Wheel', {
anchorX: 0.5,
anchorY: 0.5,
- x: GAME_CONFIG.guidelines.screenWidth / 2,
- y: yPosition,
- alpha: GAME_CONFIG.guidelines.alpha
- }));
- // Store position for enemy spawning
- gameObjects.guidelineYPositions.push(yPosition);
- }
- // Update legacy variable
- guidelineYPositions = gameObjects.guidelineYPositions;
- };
- // Update available towers for current level
- self.updateAvailableTowers = function (availableTowers) {
- if (!availableTowers) return;
- gameState.level.state.availableTowers = availableTowers;
- // Tower button mapping for visibility control
- // Define tower button variables that will be referenced later
- var waterTowerButton = null;
- var gasTowerButton = null;
- var basicTowerButton = null;
- var electricTowerButton = null;
- var advancedTowerButton = null;
- var wifiTowerButton = null;
- var fireButton = null;
- var slingshotTowerButton = null;
- var towerMapping = {
- 'water': waterTowerButton,
- 'gas': gasTowerButton,
- 'air': basicTowerButton,
- 'electric': electricTowerButton,
- 'plasma': advancedTowerButton,
- 'wifi': wifiTowerButton,
- 'fire': fireButton,
- 'slingshot': slingshotTowerButton
- };
- var allTowerButtons = [waterTowerButton, gasTowerButton, basicTowerButton, electricTowerButton, advancedTowerButton, wifiTowerButton, fireButton, slingshotTowerButton];
- // Dim all towers first
- for (var i = 0; i < allTowerButtons.length; i++) {
- if (allTowerButtons[i]) {
- allTowerButtons[i].alpha = 0.3;
- }
- }
- // Show available towers
- for (var j = 0; j < availableTowers.length; j++) {
- var towerType = availableTowers[j];
- if (towerMapping[towerType]) {
- towerMapping[towerType].alpha = 1.0;
- }
- }
- };
- // Check if current level is completed
- self.checkLevelCompletion = function () {
- var levelConfig = GAME_CONFIG.levels[self.currentLevel];
- if (!levelConfig) return false;
- // Check if all waves are completed and no enemies remain
- if (gameState.wave.current > levelConfig.totalWaves && enemies.length === 0) {
- self.completeLevelSuccessfully();
- return true;
- }
- return false;
- };
- // Handle successful level completion
- self.completeLevelSuccessfully = function () {
- self.levelInProgress = false;
- gameState.level.inProgress = false;
- var completionTime = Date.now() - self.levelStartTime;
- gameState.level.completionTime = completionTime;
- var levelConfig = GAME_CONFIG.levels[self.currentLevel];
- // Award completion rewards
- if (levelConfig.completionReward) {
- gameState.player.score += levelConfig.completionReward.score || 0;
- gameState.player.cash += levelConfig.completionReward.cash || 0;
- // Update displays
- if (scoreDisplay) {
- scoreDisplay.update(gameState.player.score);
- }
- if (cashDisplay) {
- cashDisplay.update(gameState.player.cash);
- }
- }
- // Update level progress
- var levelStats = gameState.level.progression.levelStats[self.currentLevel];
- if (levelStats) {
- levelStats.completed = true;
- levelStats.bestScore = Math.max(levelStats.bestScore, gameState.player.score);
- if (levelStats.fastestTime === 0 || completionTime < levelStats.fastestTime) {
- levelStats.fastestTime = completionTime;
- }
- }
- // Unlock next level
- if (self.currentLevel < 3) {
- gameState.level.progression.highestUnlocked = Math.max(gameState.level.progression.highestUnlocked, self.currentLevel + 1);
- }
- // Use transition system instead of simple completion screen
- if (self.currentLevel < 3) {
- levelTransitionCutscene.startTransition(self.currentLevel, self.currentLevel + 1, function () {
- // After transition completes, start the new level
- if (self.currentLevel + 1 === 2) {
- startLevel2();
- } else if (self.currentLevel + 1 === 3) {
- console.log('Level 3 not yet implemented');
- }
+ x: 55,
+ // Move from 80 to 55 (25 pixels left)
+ y: 95,
+ scaleX: 0.6,
+ scaleY: 0.6
});
- } else {
- // Final level completed - show game completion
- self.showGameCompletionScreen();
- }
- };
- // Show level completion screen
- self.showLevelCompletionScreen = function () {
- // Create completion display
- var completionText = new Text2('LEVEL ' + self.currentLevel + ' COMPLETE!', {
- size: 80,
- fill: 0x00FF00
- });
- completionText.anchor.set(0.5, 0.5);
- completionText.x = 1024;
- completionText.y = 400;
- if (uiContainer) {
- uiContainer.addChild(completionText);
- } else {
- LK.gui.addChild(completionText);
- }
- // Add continue button for next level
- if (self.currentLevel < 3) {
- var continueButton = new Text2('CONTINUE TO LEVEL ' + (self.currentLevel + 1), {
- size: 50,
- fill: 0xFFFFFF
- });
- continueButton.anchor.set(0.5, 0.5);
- continueButton.x = 1024;
- continueButton.y = 600;
- continueButton.down = function () {
- completionText.destroy();
- continueButton.destroy();
- self.transitionToNextLevel();
- };
- if (uiContainer) {
- uiContainer.addChild(continueButton);
- } else {
- LK.gui.addChild(continueButton);
- }
- }
- // Auto-remove screen after delay
- LK.setTimeout(function () {
- if (completionText && completionText.parent) {
- completionText.destroy();
- }
- }, 5000);
- };
- // Transition to next level
- self.transitionToNextLevel = function () {
- if (self.currentLevel >= 3) return;
- var nextLevel = self.currentLevel + 1;
- // Clear current level state
- self.cleanupCurrentLevel();
- // Initialize next level
- self.initializeLevel(nextLevel);
- // Start first wave of new level
- if (nextLevel === 2) {
- startLevel2();
- } else if (nextLevel === 3) {
- // Level 3 initialization would go here
- console.log('Level 3 not yet implemented');
- }
- };
- // Clean up current level assets and state
- self.cleanupCurrentLevel = function () {
- // Clean up background asset
- if (gameState.level.state.backgroundAsset && gameState.level.state.backgroundAsset.parent) {
- gameState.level.state.backgroundAsset.destroy();
- gameState.level.state.backgroundAsset = null;
- }
- // Clean up any level-specific assets
- // This could be expanded for more complex level cleanup
- };
- // Handle level failure
- self.handleLevelFailure = function () {
- self.levelInProgress = false;
- gameState.level.inProgress = false;
- // Show failure screen with retry option
- self.showLevelFailureScreen();
- };
- // Show level failure screen
- self.showLevelFailureScreen = function () {
- var failureText = new Text2('LEVEL ' + self.currentLevel + ' FAILED', {
- size: 80,
- fill: 0xFF0000
- });
- failureText.anchor.set(0.5, 0.5);
- failureText.x = 1024;
- failureText.y = 400;
- if (uiContainer) {
- uiContainer.addChild(failureText);
- } else {
- LK.gui.addChild(failureText);
- }
- var retryButton = new Text2('RETRY LEVEL', {
- size: 50,
- fill: 0xFFFFFF
- });
- retryButton.anchor.set(0.5, 0.5);
- retryButton.x = 1024;
- retryButton.y = 600;
- retryButton.down = function () {
- failureText.destroy();
- retryButton.destroy();
- self.initializeLevel(self.currentLevel);
- };
- if (uiContainer) {
- uiContainer.addChild(retryButton);
- } else {
- LK.gui.addChild(retryButton);
- }
- };
- // Get current level configuration
- self.getCurrentLevelConfig = function () {
- return GAME_CONFIG.levels[self.currentLevel];
- };
- // Check if a level is unlocked
- self.isLevelUnlocked = function (levelNumber) {
- return levelNumber <= gameState.level.progression.highestUnlocked;
- };
- // Apply level-specific modifiers to enemy stats
- self.applyLevelModifiers = function (enemyType, baseStats) {
- var enemyConfig = GAME_CONFIG.enemies[enemyType];
- if (enemyConfig && enemyConfig.levelVariations && enemyConfig.levelVariations[self.currentLevel]) {
- var variation = enemyConfig.levelVariations[self.currentLevel];
- return {
- health: Math.floor(baseStats.health * (variation.healthMultiplier || 1.0)),
- maxHealth: Math.floor(baseStats.maxHealth * (variation.healthMultiplier || 1.0)),
- speed: baseStats.speed * (variation.speedMultiplier || 1.0),
- damage: baseStats.damage ? Math.floor(baseStats.damage * (variation.damageMultiplier || 1.0)) : baseStats.damage
- };
- }
- return baseStats;
- };
- return self;
-});
-// Level Transition Cutscene class for smooth level changes
-var LevelTransitionCutscene = Container.expand(function () {
- var self = Container.call(this);
- // Transition properties
- self.isPlaying = false;
- self.currentLevel = 1;
- self.targetLevel = 1;
- self.transitionAssets = [];
- // Start level transition with fade effects
- self.startTransition = function (fromLevel, toLevel, callback) {
- if (self.isPlaying) return;
- self.isPlaying = true;
- self.currentLevel = fromLevel;
- self.targetLevel = toLevel;
- self.onComplete = callback;
- // Phase 1: Fade out current level
- self.fadeOutCurrentLevel();
- };
- // Fade out current level content
- self.fadeOutCurrentLevel = function () {
- // Create dark overlay for transition
- var overlay = game.addChild(LK.getAsset('guideLine', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 2048 / 2,
- y: 2732 / 2,
- scaleX: 2048 / 2200,
- scaleY: 2732 / 4,
- tint: 0x000000,
- alpha: 0
- }));
- self.transitionAssets.push(overlay);
- // Fade to black
- tween(overlay, {
- alpha: 0.8
- }, {
- duration: 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- self.showLevelCompletionText();
- }
- });
- };
- // Show level completion text
- self.showLevelCompletionText = function () {
- var completionText = new Text2('LEVEL ' + self.currentLevel + ' COMPLETE!', {
- size: 80,
- fill: 0x00FF00
- });
- completionText.anchor.set(0.5, 0.5);
- completionText.x = 2048 / 2;
- completionText.y = 2732 / 2 - 200;
- completionText.alpha = 0;
- game.addChild(completionText);
- self.transitionAssets.push(completionText);
- // Fade in completion text
- tween(completionText, {
- alpha: 1
- }, {
- duration: 1500,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- // Show for 2 seconds then continue
- LK.setTimeout(function () {
- self.showNextLevelPreview();
- }, 2000);
- }
- });
- };
- // Show preview of next level
- self.showNextLevelPreview = function () {
- var nextLevelText = new Text2('ENTERING LEVEL ' + self.targetLevel, {
- size: 60,
- fill: 0xFFFFFF
- });
- nextLevelText.anchor.set(0.5, 0.5);
- nextLevelText.x = 2048 / 2;
- nextLevelText.y = 2732 / 2;
- nextLevelText.alpha = 0;
- game.addChild(nextLevelText);
- self.transitionAssets.push(nextLevelText);
- // Fade in next level text
- tween(nextLevelText, {
- alpha: 1
- }, {
- duration: 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- // Show for 1.5 seconds then load new level
- LK.setTimeout(function () {
- self.loadNewLevel();
- }, 1500);
- }
- });
- };
- // Load and display new level
- self.loadNewLevel = function () {
- // Clean up current level (handled by LevelManager)
- if (levelManager) {
- levelManager.cleanupCurrentLevel();
- }
- // Initialize new level
- if (levelManager && levelManager.initializeLevel(self.targetLevel)) {
- self.fadeInNewLevel();
- } else {
- // Fallback if level manager fails
- self.completeTransition();
- }
- };
- // Fade in new level content
- self.fadeInNewLevel = function () {
- // Get new level background asset
- var levelConfig = GAME_CONFIG.levels[self.targetLevel];
- if (levelConfig && levelConfig.background) {
- var newBackground = game.addChild(LK.getAsset(levelConfig.background.asset, {
+ // Add roboarm attachment
+ self.roboarm = self.attachAsset('Roboarm', {
anchorX: 0.5,
anchorY: 0.5,
- x: levelConfig.background.position.x,
- y: levelConfig.background.position.y,
- scaleX: levelConfig.background.scale.x,
- scaleY: levelConfig.background.scale.y,
- alpha: 0
- }));
- // Fade in new background
- tween(newBackground, {
- alpha: 1
- }, {
- duration: 2000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- self.completeTransition();
- }
+ x: 0,
+ y: -5,
+ scaleX: 0.5,
+ scaleY: 0.5
});
- } else {
- self.completeTransition();
}
- };
- // Complete transition and cleanup
- self.completeTransition = function () {
- // Fade out overlay and transition elements
- for (var i = 0; i < self.transitionAssets.length; i++) {
- var asset = self.transitionAssets[i];
- if (asset && asset.parent) {
- tween(asset, {
- alpha: 0
- }, {
- duration: 1000,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- if (asset && asset.parent) {
- asset.destroy();
- }
- }
- });
- }
+ // 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
}
- self.transitionAssets = [];
- self.isPlaying = false;
- // Call completion callback
- if (self.onComplete) {
- self.onComplete();
+ // Ensure health bars are brought to front after asset change
+ if (self.healthBarBackground && self.healthBarBackground.parent) {
+ self.removeChild(self.healthBarBackground);
+ self.addChild(self.healthBarBackground);
}
- };
- // Stop transition and cleanup
- self.stop = function () {
- self.isPlaying = false;
- // Stop all tweens on transition assets
- for (var i = 0; i < self.transitionAssets.length; i++) {
- var asset = self.transitionAssets[i];
- if (asset) {
- tween.stop(asset);
- if (asset.parent) {
- asset.destroy();
- }
- }
+ if (self.healthBarForeground && self.healthBarForeground.parent) {
+ self.removeChild(self.healthBarForeground);
+ self.addChild(self.healthBarForeground);
}
- self.transitionAssets = [];
};
return self;
});
-// Show final game completion screen
// Tower class for tower defense
var Tower = Container.expand(function () {
var self = Container.call(this);
// Tower properties
@@ -1260,12 +808,14 @@
self.upgradeCost = 150;
self.lastShotFrame = 0;
self.target = null;
// 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;
@@ -1311,92 +861,106 @@
// Method to find nearest enemy in range
self.findTarget = function () {
var nearestEnemy = null;
var nearestDistance = Infinity;
- var enemiesLength = enemies.length;
- var towerRange = self.range;
- var towerX = self.x;
- var towerY = self.y;
- var guidelineIndex = self.guidelineIndex;
- for (var i = 0; i < enemiesLength; i++) {
+ 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 !== guidelineIndex && enemy.guidelineIndex !== -1) {
+ if (enemy.guidelineIndex !== self.guidelineIndex && enemy.guidelineIndex !== -1) {
continue; // Skip enemies not on same line
}
- var dx = enemy.x - towerX;
- var dy = enemy.y - towerY;
- // Quick distance check using Manhattan distance first (cheaper than sqrt)
- var manhattanDistance = Math.abs(dx) + Math.abs(dy);
- if (manhattanDistance > towerRange * 1.5) {
- // Early exit for obviously out-of-range enemies
- continue;
- }
+ var dx = enemy.x - self.x;
+ var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var inRange = false;
var targetDistance = distance;
if (self.isTower1) {
inRange = distance <= 650;
} else if (self.towerType === 'fire') {
- var absDx = Math.abs(dx);
- var absDy = Math.abs(dy);
- inRange = absDx <= 800 && absDy <= 50;
- targetDistance = absDx;
+ inRange = Math.abs(dx) <= 800 && Math.abs(dy) <= 50;
+ targetDistance = Math.abs(dx);
} else if (self.towerType === 'dog') {
inRange = dx >= 0 && dx <= 600 && Math.abs(dy) <= 50;
targetDistance = dx;
} else if (self.towerType === 'electric') {
inRange = dx >= 0 && dx <= 500 && Math.abs(dy) <= 50;
targetDistance = dx;
} else {
- inRange = distance <= towerRange;
+ inRange = distance <= self.range;
}
if (inRange && targetDistance < nearestDistance) {
nearestDistance = targetDistance;
nearestEnemy = enemy;
}
}
return nearestEnemy;
};
- // Consolidated shooting method that handles all tower types through parameters
- self.shootAtTarget = function (target, shootingConfig) {
+ // Method to shoot at target
+ self.shoot = function (target) {
if (!target || !target.parent) return;
- // Handle pre-shooting animations and sounds
- if (shootingConfig.preShootAnimation) {
- shootingConfig.preShootAnimation();
+ // 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
}
- if (shootingConfig.soundEffect) {
- LK.getSound(shootingConfig.soundEffect).play();
+ // 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;
+ }
+ }
+ });
}
- // Handle special tower behaviors
- if (shootingConfig.specialBehavior) {
- shootingConfig.specialBehavior(target);
+ // Play water sound for water tower (Tower1)
+ if (self.isTower1) {
+ LK.getSound('Water').play();
}
- // Handle water tower special spray attack
- if (shootingConfig.isWaterTower) {
+ // 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) {
- var streamWidth = 25;
- var streamLength = Math.min(distance, 650);
- var stream = game.addChild(LK.getAsset('guideLine', {
- anchorX: 0,
- anchorY: 0.5,
- x: self.x,
- y: self.y,
- scaleX: streamLength / 2200,
- scaleY: streamWidth / 4,
- tint: 0x66CCFF,
- alpha: 0.8
- }));
- var angle = Math.atan2(dy, dx);
- stream.rotation = angle;
- var sprayDuration = 1500;
- var damageInterval = 100;
- var damageDealt = 0;
- var maxDamage = self.damage;
- // Pulsing spray effect
+ // Create pulsing spray effect
var pulseSpray = function pulseSpray() {
if (stream && stream.parent) {
tween(stream, {
scaleY: (streamWidth + 10) / 4,
@@ -1416,17 +980,43 @@
}
}
});
}
- };
+ }; // 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
+ // 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,
@@ -1446,9 +1036,9 @@
LK.clearInterval(pulseInterval);
}
}
}, damageInterval);
- // Clean up spray effect
+ // Clean up spray effect after duration
LK.setTimeout(function () {
LK.clearInterval(damageTimer);
LK.clearInterval(pulseInterval);
if (stream && stream.parent) {
@@ -1463,36 +1053,77 @@
});
}
}, sprayDuration);
}
- return;
- }
- // Handle projectile-based towers
- if (!shootingConfig.skipProjectile) {
- var bullet = game.addChild(LK.getAsset(shootingConfig.projectileAsset || 'bullet', {
+ } else {
+ // Create bullet/projectile effect for non-water towers
+ var bullet = game.addChild(LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
- x: shootingConfig.projectileStartX || self.x,
- y: shootingConfig.projectileStartY || self.y,
- tint: shootingConfig.projectileTint || 0xFFFFFF
+ x: self.x,
+ y: self.y
}));
- // Add special effects
- if (shootingConfig.additionalEffect) {
- shootingConfig.additionalEffect();
+ // 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
+ }));
}
- // Calculate trajectory
+ // 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 + 20,
+ y: self.y + 25
+ }));
+ }
+ // 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 projectile
+ // Animate bullet to target
tween(bullet, {
x: target.x,
y: target.y
}, {
- duration: distance / (shootingConfig.projectileSpeed || 2),
+ 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();
@@ -1501,105 +1132,17 @@
}
});
}
};
- // Method to shoot at target (legacy wrapper)
- self.shoot = function (target) {
- if (!target || !target.parent) return;
- var shootingConfig = {};
- // Configure based on tower type
- if (self.towerType === 'slingshot') {
- shootingConfig.preShootAnimation = function () {
- tween(self, {
- scaleX: 0.8,
- scaleY: 1.1,
- y: self.y + 20
- }, {
- duration: 150,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1.0,
- scaleY: 1.0,
- y: self.y - 20
- }, {
- duration: 100,
- easing: tween.easeIn
- });
- }
- });
- };
- shootingConfig.skipProjectile = true;
- return; // Slingshot handled elsewhere
- } else if (self.towerType === 'dog') {
- shootingConfig.specialBehavior = function () {
- if (self.dogGraphics && self.dog1Graphics) {
- self.dogGraphics.visible = false;
- self.dog1Graphics.visible = true;
- tween(self, {}, {
- duration: 200,
- onFinish: function onFinish() {
- if (self.dogGraphics && self.dog1Graphics) {
- self.dogGraphics.visible = true;
- self.dog1Graphics.visible = false;
- }
- }
- });
- }
- };
- shootingConfig.soundEffect = 'Fart';
- shootingConfig.projectileAsset = 'Gas';
- shootingConfig.projectileStartX = self.x + 20;
- shootingConfig.projectileStartY = self.y + 25;
- } else if (self.isTower1) {
- shootingConfig.isWaterTower = true;
- shootingConfig.soundEffect = 'Water';
- } else if (self.towerType === 'fire') {
- shootingConfig.soundEffect = 'Fireball';
- shootingConfig.projectileAsset = 'Fireball';
- } else if (self.towerType === 'electric') {
- shootingConfig.soundEffect = 'Electric';
- shootingConfig.projectileTint = 0x00FFFF;
- shootingConfig.additionalEffect = function () {
- 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
- }));
- tween(electricEffect, {
- alpha: 0
- }, {
- duration: 400,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- electricEffect.destroy();
- }
- });
- };
- }
- self.shootAtTarget(target, shootingConfig);
- };
- // Consolidated tower upgrade method that handles all upgrade logic uniformly
- self.performUpgrade = function (upgradeConfig) {
- upgradeConfig = upgradeConfig || {};
- var cost = upgradeConfig.cost || self.upgradeCost;
- var maxLevel = upgradeConfig.maxLevel || 3;
- var damageIncrease = upgradeConfig.damageIncrease || 15;
- var rangeIncrease = upgradeConfig.rangeIncrease || 50;
- var fireRateReduction = upgradeConfig.fireRateReduction || 10;
- var costMultiplier = upgradeConfig.costMultiplier || 1.5;
- var soundEffect = upgradeConfig.soundEffect || 'Towerselect';
- if (playerCash >= cost && self.level < maxLevel) {
- playerCash -= cost;
+ // Method to upgrade tower
+ self.upgrade = function () {
+ if (playerCash >= self.upgradeCost && self.level < 3) {
+ playerCash -= self.upgradeCost;
self.level++;
- self.damage += damageIncrease;
- self.range += rangeIncrease;
- self.fireRate = Math.max(30, self.fireRate - fireRateReduction);
- self.upgradeCost = Math.floor(self.upgradeCost * costMultiplier);
+ 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,
@@ -1609,228 +1152,50 @@
scaleX: self.level,
scaleY: self.level
});
// Play upgrade sound
- LK.getSound(soundEffect).play();
- // Execute custom upgrade callback if provided
- if (upgradeConfig.onUpgrade) {
- upgradeConfig.onUpgrade(self);
- }
+ LK.getSound('Towerselect').play();
return true;
}
return false;
};
- // Method to upgrade tower (legacy wrapper)
- self.upgrade = function () {
- return self.performUpgrade();
- };
- // Consolidated tower interaction handler for click events and UI responses
- self.handleInteraction = function (x, y, obj, interactionType) {
- interactionType = interactionType || 'down';
- // Handle slingshot aiming interaction
- if (self.towerType === 'slingshot' && interactionType === 'down') {
+ // Click handler for tower interaction
+ self.down = function (x, y, obj) {
+ if (self.towerType === 'slingshot') {
+ // Start aiming for slingshot
self.isAiming = true;
+ // Convert local coordinates to game coordinates for consistent aiming
var gamePos = game.toLocal(self.toGlobal({
x: x,
y: y
}));
self.aimStartX = gamePos.x;
self.aimStartY = gamePos.y;
- self.aimEndX = gamePos.x;
+ self.aimEndX = gamePos.x; // Initialize end position
self.aimEndY = gamePos.y;
- return;
- }
- // Handle upgrade interaction for other tower types
- if (interactionType === 'upgrade') {
- return self.performUpgrade();
- }
- // Handle selection interaction
- if (interactionType === 'select') {
- // Flash tower to show selection
- tween(self, {
- tint: 0xFFFF00
- }, {
- duration: 200,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- tween(self, {
- tint: 0xFFFFFF
- }, {
- duration: 200,
- easing: tween.easeOut
- });
- }
- });
- return;
- }
- // Handle range display interaction
- if (interactionType === 'showRange') {
- self.showRangeIndicator();
- return;
- }
+ } else {}
};
- // Click handler for tower interaction (legacy wrapper)
- self.down = function (x, y, obj) {
- self.handleInteraction(x, y, obj, 'down');
- };
- // Consolidated method to show tower range indicator
- self.showRangeIndicator = function () {
- // Remove existing range indicator if present
- if (self.rangeIndicator && self.rangeIndicator.parent) {
- self.rangeIndicator.destroy();
- }
- // Create range circle based on tower type
- var rangeSize = self.range * 2;
- if (self.towerType === 'fire' || self.towerType === 'dog' || self.towerType === 'electric') {
- // Rectangular range for directional towers
- self.rangeIndicator = game.addChild(LK.getAsset('guideLine', {
- anchorX: 0,
- anchorY: 0.5,
- x: self.x,
- y: self.y,
- scaleX: self.range / 2200,
- scaleY: 50 / 4,
- alpha: 0.3,
- tint: 0x00FF00
- }));
- } else {
- // Circular range for other towers
- self.rangeIndicator = game.addChild(LK.getAsset('rangeCircle', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: self.x,
- y: self.y,
- scaleX: rangeSize / 1000,
- scaleY: rangeSize / 1000,
- alpha: 0.3,
- tint: 0x00FF00
- }));
- }
- // Auto-hide range indicator after 3 seconds
- LK.setTimeout(function () {
- if (self.rangeIndicator && self.rangeIndicator.parent) {
- tween(self.rangeIndicator, {
- alpha: 0
- }, {
- duration: 500,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- self.rangeIndicator.destroy();
- self.rangeIndicator = null;
- }
- });
- }
- }, 3000);
- };
- // Consolidated tower status management method for health and damage effects
- self.manageTowerStatus = function (enemies, towerGraphics) {
- // 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 if not set
- if (enemy.lastCollisionState === undefined) {
- enemy.lastCollisionState = false;
- }
- // Check if this enemy just started colliding (state transition)
- if (!enemy.lastCollisionState) {
- // Apply collision damage
- self.applyCollisionDamage(5, towerGraphics);
- // Check if tower should be destroyed
- if (self.health <= 0) {
- self.handleTowerDestruction();
- return;
- }
- }
- // Update collision state
- enemy.lastCollisionState = true;
- } else {
- // Enemy is not colliding - reset collision state
- enemy.lastCollisionState = false;
- }
- }
- };
- // Consolidated method to apply collision damage with visual feedback
- self.applyCollisionDamage = function (damage, graphics) {
- self.health -= damage;
- // Flash tower red to show damage
- tween(graphics, {
- tint: 0xFF0000
- }, {
- duration: 200,
- easing: tween.easeOut,
- onFinish: function onFinish() {
- tween(graphics, {
- tint: 0xFFFFFF
- }, {
- duration: 300,
- easing: tween.easeOut
- });
- }
- });
- };
- // Consolidated method to handle tower destruction
- self.handleTowerDestruction = function () {
- // 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();
- };
self.update = function () {
// Only operate when wave is active
if (!waveActive) return;
- // Cache frequently used values
- var currentTick = LK.ticks;
- var enemiesLength = enemies.length;
- var speedMultiplier = typeof gameSpeedMultiplier !== 'undefined' ? gameSpeedMultiplier : 1;
// 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 < enemiesLength; j++) {
+ 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 collision state if not set
- if (enemy.lastCollisionState === undefined) {
- enemy.lastCollisionState = false;
+ // Initialize last blown back tracking if not set
+ if (enemy.lastBlownBack === undefined) {
+ enemy.lastBlownBack = false;
}
- // Only blow back if enemy wasn't affected last frame (prevent continuous blowing)
- if (!enemy.lastCollisionState) {
+ // 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, {
@@ -1860,34 +1225,107 @@
easing: tween.easeOut
});
}
});
+ // Mark enemy as blown back this frame
+ enemy.lastBlownBack = true;
}
- // Update collision state
- enemy.lastCollisionState = true;
} else {
- // Enemy is not in range, reset collision state
- enemy.lastCollisionState = false;
+ // Enemy is not in range, reset blown back tracking
+ enemy.lastBlownBack = false;
}
}
}
}
- // Consolidated tower status management using extracted method
- self.manageTowerStatus(enemies, towerGraphics);
- // Update tower health bar only when health changes or every 10th frame
- var healthPercentage = self.health / self.maxHealth;
- if (self.lastHealthPercentage === undefined || self.lastHealthPercentage !== healthPercentage || currentTick % 10 === 0) {
- 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
+ // 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 {
- self.towerHealthBarForeground.tint = 0xff0000; // Red
+ // Enemy is not colliding - reset collision tracking
+ if (enemy.lastCollidingWithTower === undefined) {
+ enemy.lastCollidingWithTower = false;
+ } else {
+ enemy.lastCollidingWithTower = false;
+ }
}
- self.lastHealthPercentage = healthPercentage;
}
+ // 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();
}
@@ -2322,1034 +1760,40 @@
/****
* Game Code
****/
-// Level-specific asset initialization function
-// Level-agnostic core assets (always loaded)
-// Consolidated Configuration System
-function initializeLevelAssets(levelNumber) {
- // Example: Add/override assets for each level as needed
- if (levelNumber === 1) {
- // Urban Defense: no extra assets needed (uses core)
- } else if (levelNumber === 2) {} else if (levelNumber === 3) {}
-}
-// Call this function from level initialization logic when switching levels
-function _typeof(o) {
- "@babel/helpers - typeof";
- return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
- return typeof o;
- } : function (o) {
- return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
- }, _typeof(o);
-}
-function _defineProperty(e, r, t) {
- return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
- value: t,
- enumerable: !0,
- configurable: !0,
- writable: !0
- }) : e[r] = t, e;
-}
-function _toPropertyKey(t) {
- var i = _toPrimitive(t, "string");
- return "symbol" == _typeof(i) ? i : i + "";
-}
-function _toPrimitive(t, r) {
- if ("object" != _typeof(t) || !t) return t;
- var e = t[Symbol.toPrimitive];
- if (void 0 !== e) {
- var i = e.call(t, r || "default");
- if ("object" != _typeof(i)) return i;
- throw new TypeError("@@toPrimitive must return a primitive value.");
- }
- return ("string" === r ? String : Number)(t);
-}
-var GAME_CONFIG = {
- // Level-specific configurations
- levels: {
- 1: _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
- name: 'Urban Defense',
- background: {
- asset: 'backdrop',
- scale: {
- x: 0.5,
- y: 0.5
- },
- position: {
- x: 2048 / 2,
- y: 2732 / 2
- }
- },
- enemyPools: ['Probedroid', 'Drone', 'SpaceDrone', 'Robot', 'UFO'],
- availableTowers: ['water', 'gas', 'air', 'electric', 'fire', 'plasma', 'wifi', 'slingshot'],
- towerCostModifier: 1.0,
- waves: {
- 1: {
- maxEnemies: 10,
- spawnInterval: 420,
- enemyType: 'Probedroid',
- cashReward: 50
- },
- 2: {
- maxEnemies: 20,
- spawnInterval: 300,
- enemyType: 'Drone',
- cashReward: 50
- },
- 3: {
- maxEnemies: 30,
- spawnInterval: 300,
- enemyType: 'SpaceDrone',
- cashReward: 50
- },
- 4: {
- maxEnemies: 40,
- spawnInterval: 180,
- enemyType: 'Robot',
- cashReward: 50
- },
- 5: {
- maxEnemies: 1,
- spawnInterval: 60,
- enemyType: 'UFO',
- cashReward: 50
- }
- },
- totalWaves: 5,
- unlockRequirement: null,
- completionReward: {
- score: 500,
- cash: 100
- },
- specialMechanics: {
- guidelineLayout: 'standard',
- enemyBehavior: 'normal',
- environmentalHazards: false,
- tutorialEnabled: true,
- introMusic: 'Intro'
- }
- }, "name", 'Urban Defense'), "displayName", 'Level 1: Urban Defense'), "description", 'Defend the city from the first wave of alien invaders'), "unlockRequirement", null), "unlockConditions", {
- previousLevelComplete: false,
- minimumScore: 0,
- minimumStars: 0
- }), "completionReward", {
- score: 500,
- cash: 100,
- experience: 100,
- unlockedTowers: ['mirror'],
- unlockedUpgrades: ['range_boost_1']
- }), "starRequirements", {
- oneStar: {
- condition: 'complete',
- value: true
- },
- twoStar: {
- condition: 'livesRemaining',
- value: 5
- },
- threeStar: {
- condition: 'perfectDefense',
- value: true
- }
- }), "difficulty", 'beginner'), "estimatedTime", '10-15 minutes'),
- 2: _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
- name: 'Industrial Complex',
- displayName: 'Level 2: Industrial Siege',
- description: 'Navigate through factory obstacles as stronger enemies attack',
- background: {
- asset: 'Backdrop2',
- scale: {
- x: 1.0,
- y: 1.0
- },
- position: {
- x: 2048 / 2,
- y: 2732 / 2
- }
- },
- enemyPools: ['Drone', 'SpaceDrone', 'Robot', 'Aliencar', 'Flyingalien', 'Giantrobot'],
- availableTowers: ['water', 'gas', 'air', 'electric', 'fire', 'plasma', 'wifi', 'slingshot'],
- towerCostModifier: 1.2,
- waves: {
- 1: {
- maxEnemies: 15,
- spawnInterval: 360,
- enemyType: 'Drone',
- cashReward: 60
- },
- 2: {
- maxEnemies: 25,
- spawnInterval: 240,
- enemyType: 'SpaceDrone',
- cashReward: 60
- },
- 3: {
- maxEnemies: 35,
- spawnInterval: 180,
- enemyType: 'Robot',
- cashReward: 60
- },
- 4: {
- maxEnemies: 20,
- spawnInterval: 300,
- enemyType: 'Aliencar',
- cashReward: 80
- },
- 5: {
- maxEnemies: 3,
- spawnInterval: 480,
- enemyType: 'Flyingalien',
- cashReward: 100
- },
- 6: {
- maxEnemies: 1,
- spawnInterval: 60,
- enemyType: 'Giantrobot',
- cashReward: 200
- }
- },
- totalWaves: 6
- }, "displayName", 'Level 2: Industrial Siege'), "description", 'Heavy machinery and armored enemies challenge your defenses'), "unlockRequirement", 'level1Complete'), "unlockConditions", {
- previousLevelComplete: true,
- minimumScore: 1000,
- minimumStars: 1
- }), "completionReward", {
- score: 1000,
- cash: 200,
- experience: 200,
- unlockedTowers: ['laser', 'freeze'],
- unlockedUpgrades: ['damage_boost_1', 'fire_rate_1']
- }), "starRequirements", {
- oneStar: {
- condition: 'complete',
- value: true
- },
- twoStar: {
- condition: 'livesRemaining',
- value: 7
- },
- threeStar: {
- condition: 'timeLimit',
- value: 900
- } // 15 minutes
- }), "difficulty", 'intermediate'), "estimatedTime", '15-20 minutes'), "specialMechanics", {
- guidelineLayout: 'industrial',
- enemyBehavior: 'aggressive',
- environmentalHazards: true,
- factoryObstacles: true,
- increasedEnemySpeed: 1.3,
- enhancedEnemyAI: true
- }),
- 3: _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({
- name: 'Alien Mothership',
- displayName: 'Level 3: Final Assault',
- description: 'Board the alien mothership for the ultimate confrontation',
- background: {
- asset: 'Mothership',
- scale: {
- x: 1.2,
- y: 1.2
- },
- position: {
- x: 2048 / 2,
- y: 2732 / 2
- }
- },
- enemyPools: ['Flyingalien', 'Alien', 'UFO', 'Giantrobot'],
- availableTowers: ['air', 'electric', 'plasma', 'wifi'],
- towerCostModifier: 0.8,
- waves: {
- 1: {
- maxEnemies: 20,
- spawnInterval: 300,
- enemyType: 'Flyingalien',
- cashReward: 80
- },
- 2: {
- maxEnemies: 30,
- spawnInterval: 240,
- enemyType: 'Alien',
- cashReward: 80
- },
- 3: {
- maxEnemies: 15,
- spawnInterval: 360,
- enemyType: 'UFO',
- cashReward: 120
- },
- 4: {
- maxEnemies: 5,
- spawnInterval: 600,
- enemyType: 'Giantrobot',
- cashReward: 200
- },
- 5: {
- maxEnemies: 1,
- spawnInterval: 60,
- enemyType: 'MothershipCore',
- cashReward: 500
- }
- },
- totalWaves: 5
- }, "displayName", 'Level 3: Final Assault'), "description", 'Face the mothership core in zero gravity combat'), "unlockRequirement", 'level2Complete'), "unlockConditions", {
- previousLevelComplete: true,
- minimumScore: 2500,
- minimumStars: 2
- }), "completionReward", {
- score: 2000,
- cash: 500,
- experience: 500,
- unlockedTowers: ['quantum', 'antimatter'],
- unlockedUpgrades: ['master_upgrade'],
- gameComplete: true
- }), "starRequirements", {
- oneStar: {
- condition: 'complete',
- value: true
- },
- twoStar: {
- condition: 'livesRemaining',
- value: 8
- },
- threeStar: {
- condition: 'noTowerLoss',
- value: true
- }
- }), "difficulty", 'expert'), "estimatedTime", '20-25 minutes'), "specialMechanics", {
- guidelineLayout: 'zero_gravity',
- enemyBehavior: 'aerial',
- environmentalHazards: true,
- gravityWells: true,
- energyShields: true,
- finalBossSequence: true,
- unlimitedTowerEnergy: true
- })
- },
- // Level metadata for progression system
- metadata: {
- currentLevel: 1,
- highestUnlocked: 1,
- levelProgress: {
- 1: {
- completed: false,
- bestScore: 0,
- fastestTime: 0,
- starsEarned: 0
- },
- 2: {
- completed: false,
- bestScore: 0,
- fastestTime: 0,
- starsEarned: 0
- },
- 3: {
- completed: false,
- bestScore: 0,
- fastestTime: 0,
- starsEarned: 0
- }
- },
- unlockedTowers: ['water', 'gas', 'air'],
- globalUpgrades: {
- towerDamageBonus: 0,
- towerRangeBonus: 0,
- enemySlowEffect: 0,
- cashMultiplier: 1.0
- }
- },
- // Asset configurations
- assets: {
- shapes: [{
- name: 'Placemat',
- width: 200,
- height: 200,
- color: 0x33cb0b,
- shape: 'box'
- }, {
- name: 'defense',
- width: 100,
- height: 100,
- color: 0x0abcaa,
- shape: 'box'
- }, {
- name: 'guideLine',
- width: 2200,
- height: 4,
- color: 0x666666,
- shape: 'box'
- }, {
- name: 'healthBarBackground',
- width: 60,
- height: 8,
- color: 0x444444,
- shape: 'box'
- }, {
- name: 'healthBarForeground',
- width: 60,
- height: 8,
- color: 0x00ff00,
- shape: 'box'
- }, {
- name: 'rangeCircle',
- width: 1000,
- height: 1000,
- color: 0xffffff,
- shape: 'ellipse'
- }, {
- name: 'squareLight',
- width: 30,
- height: 30,
- color: 0x00ff00,
- shape: 'box'
- }, {
- name: 'tower',
- width: 2,
- height: 2,
- color: 0x2df5c0,
- shape: 'box'
- }, {
- name: 'towerHealthBarBackground',
- width: 80,
- height: 10,
- color: 0x444444,
- shape: 'box'
- }, {
- name: 'towerHealthBarForeground',
- width: 80,
- height: 10,
- color: 0x00ff00,
- shape: 'box'
- }, {
- name: 'towerLevelIndicator',
- width: 10,
- height: 10,
- color: 0xffffff,
- shape: 'box'
- }, {
- name: 'towerpreview',
- width: 100,
- height: 100,
- color: 0xffffff,
- shape: 'box'
- }, {
- name: 'verticalGuideLine',
- width: 4,
- height: 1500,
- color: 0x666666,
- shape: 'box'
- }],
- numbered: [{
- name: '1',
- width: 187.3,
- id: '687e8cb1b06057c5f9deadd2'
- }, {
- name: '2',
- width: 218.4,
- id: '687e8cdbb06057c5f9deadd5'
- }, {
- name: '3',
- width: 108,
- id: '687e8cf9b06057c5f9deadd8'
- }, {
- name: '4',
- width: 170.73,
- id: '687e8d18b06057c5f9deaddb'
- }],
- digits: [{
- name: 'Zero',
- height: 108.94,
- id: '6883b90671447ae2e425a89a'
- }, {
- name: 'One',
- height: 125,
- id: '6883b92071447ae2e425a89d'
- }, {
- name: 'Two',
- height: 116.89,
- id: '6883b93571447ae2e425a8a1'
- }, {
- name: 'Three',
- height: 117.97,
- id: '6883b94a71447ae2e425a8a5'
- }, {
- name: 'Four',
- height: 121.9,
- id: '6883b97671447ae2e425a8a9'
- }, {
- name: 'Five',
- height: 124.27,
- id: '6883b98f71447ae2e425a8b0'
- }, {
- name: 'Six',
- height: 128,
- id: '6883b9ab71447ae2e425a8b4'
- }, {
- name: 'Seven',
- height: 127.36,
- id: '6883b9ca71447ae2e425a8bc'
- }, {
- name: 'Eight',
- height: 116.36,
- id: '6883b9e571447ae2e425a8c0'
- }, {
- name: 'Nine',
- height: 113.78,
- id: '6883ba0b71447ae2e425a8cf'
- }],
- story: [{
- name: 'Story1',
- id: '6881191e41db13ea527b2565'
- }, {
- name: 'Story2',
- id: '6881195441db13ea527b256e'
- }, {
- name: 'Story3',
- id: '6881198441db13ea527b2572'
- }, {
- name: 'Story4',
- id: '6881726749b756ddb352615c'
- }, {
- name: 'Story5',
- id: '68827a863bc9457bd8fe4ee4'
- }],
- waves: [{
- name: 'Wave1',
- height: 416.02,
- id: '6876a5c7282810e95e7b2e71'
- }, {
- name: 'Wave2',
- height: 400.39,
- id: '6877ec933e8c5761702af89f'
- }, {
- name: 'Wave3',
- height: 400,
- id: '6877eccf3e8c5761702af8a9'
- }, {
- name: 'Wave4',
- height: 400,
- id: '6877ed053e8c5761702af8b3'
- }, {
- name: 'Wave5',
- height: 400,
- id: '68788ee96cd3a5bb82a92af7'
- }],
- sounds: [{
- name: 'Alien1',
- id: '686f7806e85c05a4e97b90fe'
- }, {
- name: 'Alien2',
- id: '686f791ae85c05a4e97b9107'
- }, {
- name: 'Alien3',
- id: '686f7d58e85c05a4e97b911f'
- }, {
- name: 'Canvasser',
- id: '686d14ebf11e6c3a82bb7d19'
- }, {
- name: 'Probedroid',
- id: '687f2a4cc4e580a313211215'
- }, {
- name: 'Probedroid2',
- id: '687f2b03c4e580a31321121a'
- }, {
- name: 'Wifi',
- id: '688588ad48e567a9f8c09f89'
- }]
- },
- // Tower configurations
- towers: {
- water: {
- cost: 10,
- assetType: 'Tower1',
- damage: 35,
- range: 650,
- fireRate: 60,
- health: 500,
- maxHealth: 500,
- sound: 'Water'
- },
- gas: {
- cost: 20,
- assetType: 'Dog',
- damage: 30,
- range: 600,
- fireRate: 45,
- health: 500,
- maxHealth: 500,
- sound: 'Fart'
- },
- air: {
- cost: 30,
- assetType: 'Fan',
- damage: 35,
- range: 350,
- fireRate: 30,
- health: 500,
- maxHealth: 500,
- blowbackDamage: 15,
- blowbackDistance: 400,
- blowbackRange: 300
- },
- electric: {
- cost: 50,
- assetType: 'Bugzapper',
- damage: 60,
- range: 350,
- fireRate: 40,
- health: 500,
- maxHealth: 500,
- sound: 'Electric'
- },
- fire: {
- cost: 40,
- assetType: 'Fireworks',
- damage: 40,
- range: 400,
- fireRate: 50,
- health: 500,
- maxHealth: 500,
- projectileAsset: 'Fireball',
- sound: 'Fireball'
- },
- plasma: {
- cost: 60,
- assetType: 'Plasmaball',
- damage: 80,
- range: 400,
- fireRate: 60,
- pulseRate: 90,
- pulseRange: 400,
- health: 500,
- maxHealth: 500,
- sound: 'Electric'
- },
- wifi: {
- cost: 80,
- assetType: 'Wifitower',
- damage: 100,
- range: 600,
- fireRate: 30,
- health: 500,
- maxHealth: 500,
- projectileAsset: 'WiFi',
- sound: 'Wifi'
- },
- slingshot: {
- cost: 35,
- assetType: 'Slingshot',
- damage: 80,
- range: 1300,
- fireRate: 120,
- health: 500,
- maxHealth: 500,
- sound: 'Slingshot'
- }
- },
- // Enemy configurations with level-specific variations
- enemies: {
- Probedroid: {
- health: 150,
- maxHealth: 150,
- speed: 1.5,
- reward: {
- score: 50,
- cash: 10
- },
- levelVariations: {
- 1: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0
- },
- 2: {
- healthMultiplier: 1.3,
- speedMultiplier: 1.2
- },
- 3: {
- healthMultiplier: 1.5,
- speedMultiplier: 1.4
- }
- }
- },
- Drone: {
- health: 200,
- maxHealth: 200,
- speed: 2,
- reward: {
- score: 50,
- cash: 10
- },
- levelVariations: {
- 1: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0
- },
- 2: {
- healthMultiplier: 1.4,
- speedMultiplier: 1.3
- },
- 3: {
- healthMultiplier: 1.6,
- speedMultiplier: 1.5
- }
- }
- },
- SpaceDrone: {
- health: 250,
- maxHealth: 250,
- speed: 2.5,
- reward: {
- score: 50,
- cash: 10
- },
- shootingRange: 400,
- fireRate: 45,
- damage: 25,
- shootingConfig: {
- projectileAsset: 'bullet',
- projectileSpeed: 3,
- projectileTint: 0xFF0000,
- damageFlashColor: 0xFF0000
- },
- levelVariations: {
- 1: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0,
- damageMultiplier: 1.0
- },
- 2: {
- healthMultiplier: 1.5,
- speedMultiplier: 1.2,
- damageMultiplier: 1.3
- },
- 3: {
- healthMultiplier: 1.8,
- speedMultiplier: 1.4,
- damageMultiplier: 1.5
- }
- }
- },
- Robot: {
- health: 350,
- maxHealth: 350,
- speed: 4,
- reward: {
- score: 50,
- cash: 10
- },
- shootingRange: 450,
- fireRate: 60,
- damage: 35,
- shootingConfig: {
- projectileAsset: 'Laserfire',
- projectileSpeed: 4,
- damageFlashColor: 0x00FFFF
- },
- levelVariations: {
- 1: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0,
- damageMultiplier: 1.0
- },
- 2: {
- healthMultiplier: 1.6,
- speedMultiplier: 1.1,
- damageMultiplier: 1.4
- },
- 3: {
- healthMultiplier: 2.0,
- speedMultiplier: 1.3,
- damageMultiplier: 1.6
- }
- }
- },
- UFO: {
- health: 5000,
- maxHealth: 5000,
- speed: 1,
- reward: {
- score: 50,
- cash: 10
- },
- guidelineIndex: -1,
- levelVariations: {
- 1: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0
- },
- 2: {
- healthMultiplier: 1.5,
- speedMultiplier: 1.2
- },
- 3: {
- healthMultiplier: 2.0,
- speedMultiplier: 1.4
- }
- }
- },
- Aliencar: {
- health: 400,
- maxHealth: 400,
- speed: 3,
- reward: {
- score: 75,
- cash: 15
- },
- special: 'armored',
- levelVariations: {
- 2: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0,
- armorRating: 0.2
- },
- 3: {
- healthMultiplier: 1.3,
- speedMultiplier: 1.2,
- armorRating: 0.3
- }
- }
- },
- Flyingalien: {
- health: 300,
- maxHealth: 300,
- speed: 2.5,
- reward: {
- score: 80,
- cash: 20
- },
- special: 'aerial',
- levelVariations: {
- 2: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0,
- evasionChance: 0.1
- },
- 3: {
- healthMultiplier: 1.4,
- speedMultiplier: 1.3,
- evasionChance: 0.2
- }
- }
- },
- Giantrobot: {
- health: 8000,
- maxHealth: 8000,
- speed: 0.5,
- reward: {
- score: 500,
- cash: 100
- },
- special: 'boss',
- levelVariations: {
- 2: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0,
- shieldCapacity: 2000
- },
- 3: {
- healthMultiplier: 1.5,
- speedMultiplier: 1.2,
- shieldCapacity: 3000
- }
- }
- },
- MothershipCore: {
- health: 12000,
- maxHealth: 12000,
- speed: 0.3,
- reward: {
- score: 1000,
- cash: 500
- },
- special: 'final_boss',
- levelVariations: {
- 3: {
- healthMultiplier: 1.0,
- speedMultiplier: 1.0,
- phases: ['shield', 'regeneration', 'berserk'],
- specialAttacks: ['gravity_well', 'energy_blast', 'minion_spawn']
- }
- }
- }
- },
- // Wave configurations (legacy compatibility)
- waves: {
- 1: {
- maxEnemies: 10,
- spawnInterval: 420,
- enemyType: 'Probedroid',
- cashReward: 50
- },
- 2: {
- maxEnemies: 20,
- spawnInterval: 300,
- enemyType: 'Drone',
- cashReward: 50
- },
- 3: {
- maxEnemies: 30,
- spawnInterval: 300,
- enemyType: 'SpaceDrone',
- cashReward: 50
- },
- 4: {
- maxEnemies: 40,
- spawnInterval: 180,
- enemyType: 'Robot',
- cashReward: 50
- },
- 5: {
- maxEnemies: 1,
- spawnInterval: 60,
- enemyType: 'UFO',
- cashReward: 50
- }
- },
- // Game settings
- game: {
- startingLives: 10,
- startingCash: 50,
- startingScore: 0,
- gameSpeedMultiplier: 1,
- guidelineCount: 6,
- verticalGuidelineCount: 8,
- towerCollisionDamage: 5,
- enemyEndReward: {
- livesLost: 1
- }
- }
-};
-// Consolidated Game State Management
-var GAME_STATE = {
- player: {
- score: 0,
- lives: 10,
- cash: 50
- },
- wave: {
- active: false,
- current: 1,
- enemiesSpawned: 0,
- maxEnemies: 10,
- spawnTimer: 0,
- spawnInterval: 420,
- firstEnemySpawned: false,
- enemiesDefeated: 0
- },
- game: {
- speedMultiplier: 1,
- introPlayed: false,
- started: false
- },
- placement: {
- active: false,
- pendingCost: 10,
- preview: null,
- confirmButton: null,
- cancelButton: null
- },
- level: {
- current: 1,
- inProgress: false,
- startTime: 0,
- completionTime: 0,
- progression: {
- currentLevel: 1,
- highestUnlocked: 1,
- completedLevels: [],
- levelStats: {
- 1: {
- completed: false,
- bestScore: 0,
- fastestTime: 0,
- starsEarned: 0,
- attempts: 0,
- perfectRuns: 0
- },
- 2: {
- completed: false,
- bestScore: 0,
- fastestTime: 0,
- starsEarned: 0,
- attempts: 0,
- perfectRuns: 0
- },
- 3: {
- completed: false,
- bestScore: 0,
- fastestTime: 0,
- starsEarned: 0,
- attempts: 0,
- perfectRuns: 0
- }
- }
- },
- unlocks: {
- towers: ['water', 'gas', 'air'],
- upgrades: [],
- features: []
- },
- configuration: {
- enemyHealthMultiplier: 1.0,
- enemySpeedMultiplier: 1.0,
- towerCostModifier: 1.0,
- specialMechanicsEnabled: false
- },
- state: {
- backgroundAsset: null,
- activeEnemyPools: [],
- availableTowers: [],
- waveConfiguration: {},
- specialRules: {}
- }
- }
-};
-// Consolidated Game Objects
-var GAME_OBJECTS = {
- enemies: [],
- towers: [],
- verticalGuidelines: [],
- snapPositions: [],
- guidelineYPositions: []
-};
-// Consolidated Game Displays
-var GAME_DISPLAYS = {
- score: null,
- lives: null,
- cash: null
-};
-// Consolidated configuration aliases
-var towerConfig = GAME_CONFIG.towers;
-var enemyConfig = GAME_CONFIG.enemies;
-var waveConfig = GAME_CONFIG.waves;
-var gameConfig = GAME_CONFIG.game;
-var gameState = GAME_STATE;
-var gameObjects = GAME_OBJECTS;
-var gameDisplays = GAME_DISPLAYS;
-// UI Management
-var uiContainer = null;
-var storyTimeouts = [];
-// Legacy variable aliases - all point to gameState for unified management
-var placingTower = false;
-var towerPreview = null;
-var confirmButton = null;
-var cancelButton = null;
-var pendingTowerCost = 10;
+// Initialize music
+// Fire tower already uses 'Fireworks' asset - goal already achieved
+var enemies = [];
+var towers = [];
var waveActive = false;
-var currentWave = 1;
var enemySpawnTimer = 0;
+var enemySpawnInterval = 420; // Spawn every 7 seconds at 60fps
var enemiesSpawned = 0;
var maxEnemiesInWave = 10;
-var enemySpawnInterval = 420;
+var guidelineYPositions = [];
var firstEnemySpawned = false;
+var currentWave = 1;
var enemiesDefeated = 0;
-var playerCash = 50;
-var gameSpeedMultiplier = 1;
-var enemies = [];
-var towers = [];
-var guidelineYPositions = [];
-var scoreDisplay = null;
-var cashDisplay = null;
-var livesDisplay = null;
+var playerScore = 0;
+var playerLives = 10;
+var playerCash = 50; // Consistent starting cash
+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
+// Track timeouts for story transitions so they can be cancelled
+var storyTimeouts = [];
+// Create intro cutscene using intro2 asset
var introCutscene = new Cutscene();
// Add intro2 scene
introCutscene.addScene({
onStart: function onStart() {
@@ -3557,30 +2001,24 @@
}, {
duration: 1000,
easing: tween.easeInOut
});
- // Consolidated skip intro handler function
- function handleSkipIntro(skipButtonRef, intro2Ref, cellRef) {
+ // Add click event handler to skip button
+ skipIntroButton.down = function (x, y, obj) {
// Don't allow skip if game has already started
- if (gameState.game.started) return;
+ if (gameStarted) return;
// Cancel all pending story timeouts to prevent them from executing
for (var i = 0; i < storyTimeouts.length; i++) {
LK.clearTimeout(storyTimeouts[i]);
}
storyTimeouts = []; // Clear the array
- // Clean up intro assets if provided
- if (intro2Ref) {
- tween.stop(intro2Ref);
- intro2Ref.destroy();
- }
- if (cellRef) {
- tween.stop(cellRef);
- cellRef.destroy();
- }
- if (skipButtonRef) {
- tween.stop(skipButtonRef);
- skipButtonRef.destroy();
- }
+ // Clean up intro2 assets
+ tween.stop(intro2Asset);
+ tween.stop(cellAsset);
+ tween.stop(skipIntroButton);
+ intro2Asset.destroy();
+ cellAsset.destroy();
+ skipIntroButton.destroy();
// Destroy story scenes 1-4 if they exist to prevent them from appearing later
if (introCutscene.story1Asset && introCutscene.story1Asset.parent) {
introCutscene.story1Asset.destroy();
introCutscene.story1Asset = null;
@@ -3596,14 +2034,10 @@
if (introCutscene.story4Asset && introCutscene.story4Asset.parent) {
introCutscene.story4Asset.destroy();
introCutscene.story4Asset = null;
}
- // Skip directly to backdrop scene
+ // Skip directly to backdrop scene instead of title screen
skipToBackdrop();
- }
- // Add click event handler to skip button
- skipIntroButton.down = function (x, y, obj) {
- handleSkipIntro(skipIntroButton, intro2Asset, cellAsset);
};
// Cell asset now has no click handler to prevent story replay
}
});
@@ -3613,9 +2047,9 @@
};
// Function to skip directly to backdrop scene
function skipToBackdrop() {
// Reset intro played flag so next game start goes to backdrop
- gameState.game.introPlayed = true;
+ introPlayed = true;
// Add backdrop asset to game
var backdropAsset = game.addChild(LK.getAsset('backdrop', {
anchorX: 0.5,
anchorY: 0.5,
@@ -3715,9 +2149,9 @@
}
// Function to show title screen
function showTitleScreen() {
// Don't show title screen if game has already started
- if (gameState.game.started) return;
+ if (gameStarted) return;
// Display the title
// Import tween plugin
var titleImage = game.addChild(LK.getAsset('Title', {
anchorX: 0.5,
@@ -3739,11 +2173,33 @@
introCutscene.skipIntroButton.alpha = 1;
}
// Add touch event to skip button
skipButton.down = function (x, y, obj) {
- handleSkipIntro(null, null, null); // No specific assets to clean up in title screen
- // Cancel any running title screen animations
+ // Cancel all pending story timeouts to prevent them from executing
+ for (var i = 0; i < storyTimeouts.length; i++) {
+ LK.clearTimeout(storyTimeouts[i]);
+ }
+ storyTimeouts = []; // Clear the array
+ // Destroy story scenes 1-4 if they exist to prevent them from appearing later
+ if (introCutscene.story1Asset && introCutscene.story1Asset.parent) {
+ introCutscene.story1Asset.destroy();
+ introCutscene.story1Asset = null;
+ }
+ if (introCutscene.story2Asset && introCutscene.story2Asset.parent) {
+ introCutscene.story2Asset.destroy();
+ introCutscene.story2Asset = null;
+ }
+ if (introCutscene.story3Asset && introCutscene.story3Asset.parent) {
+ introCutscene.story3Asset.destroy();
+ introCutscene.story3Asset = null;
+ }
+ if (introCutscene.story4Asset && introCutscene.story4Asset.parent) {
+ introCutscene.story4Asset.destroy();
+ introCutscene.story4Asset = null;
+ }
+ // Cancel any running title screen animations and skip directly to backdrop
tween.stopAll();
+ skipToBackdrop();
};
// Add flyby asset to title - start at right side of screen
var flybyImage = game.addChild(LK.getAsset('Flyby', {
anchorX: 0.5,
@@ -3948,190 +2404,98 @@
}, 5000);
}
});
}
-// Integrate guideline configuration into main config
-GAME_CONFIG.guidelines = {
- screenHeight: 2732,
- screenWidth: 2048,
- horizontalCount: 6,
- verticalCount: 8,
- offsets: {
- 1: 525,
- 2: 395,
- 3: 225,
- 4: 95,
- 5: -95,
- 6: -270
- },
- alpha: 0.5,
- verticalAlpha: 0.3
-};
-// Legacy alias
-var guidelineConfig = GAME_CONFIG.guidelines;
-// Utility functions for common calculations
-var utilityFunctions = {
- // Calculate guideline Y positions for enemy spawning
- calculateGuidelinePositions: function calculateGuidelinePositions() {
- var lineSpacing = guidelineConfig.screenHeight / 7;
- gameObjects.guidelineYPositions = [];
- for (var i = 1; i <= guidelineConfig.horizontalCount; i++) {
- var yPosition = lineSpacing * i;
- if (guidelineConfig.offsets[i]) {
- yPosition += guidelineConfig.offsets[i];
- }
- gameObjects.guidelineYPositions.push(yPosition);
+// 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)
}
- },
- // Calculate snap positions from guideline intersections
- calculateSnapPositions: function calculateSnapPositions() {
- gameObjects.snapPositions = [];
- // Create vertical guideline positions (8 columns across screen width)
- var verticalSpacing = guidelineConfig.screenWidth / 9;
- var verticalXPositions = [];
- for (var i = 1; i <= guidelineConfig.verticalCount; i++) {
- var xPosition = verticalSpacing * i;
- verticalXPositions.push(xPosition);
- }
- // Create snap positions at intersections of horizontal and vertical guidelines
- for (var h = 0; h < gameObjects.guidelineYPositions.length; h++) {
- for (var v = 0; v < verticalXPositions.length; v++) {
- gameObjects.snapPositions.push({
- x: verticalXPositions[v],
- y: gameObjects.guidelineYPositions[h]
- });
- }
- }
- },
- // Find nearest snap position to given coordinates
- findNearestSnapPosition: function findNearestSnapPosition(x, y) {
- var nearestPosition = {
- x: x,
- y: y
- };
- var nearestDistance = Infinity;
- for (var i = 0; i < gameObjects.snapPositions.length; i++) {
- var snap = gameObjects.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
- showVerticalGuidelines: function showVerticalGuidelines() {
- utilityFunctions.hideVerticalGuidelines(); // Clear any existing guidelines
- var verticalSpacing = guidelineConfig.screenWidth / 9;
- for (var i = 1; i <= guidelineConfig.verticalCount; i++) {
- var xPosition = verticalSpacing * i;
- var yPosition = guidelineConfig.screenHeight / 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: guidelineConfig.verticalAlpha
- }));
- gameObjects.verticalGuidelines.push(guideLine);
- }
- },
- // Hide vertical guidelines
- hideVerticalGuidelines: function hideVerticalGuidelines() {
- for (var i = 0; i < gameObjects.verticalGuidelines.length; i++) {
- gameObjects.verticalGuidelines[i].destroy();
- }
- gameObjects.verticalGuidelines = [];
- },
- // Clean up placement assets (tower preview, buttons, guidelines)
- cleanupPlacementAssets: function cleanupPlacementAssets() {
- if (gameState.placement.preview) {
- if (gameState.placement.preview.bladePreview) {
- gameState.placement.preview.bladePreview.destroy();
- }
- gameState.placement.preview.destroy();
- gameState.placement.preview = null;
- }
- if (gameState.placement.confirmButton) {
- gameState.placement.confirmButton.destroy();
- gameState.placement.confirmButton = null;
- }
- if (gameState.placement.cancelButton) {
- gameState.placement.cancelButton.destroy();
- gameState.placement.cancelButton = null;
- }
- utilityFunctions.hideVerticalGuidelines();
- gameState.placement.active = false;
- },
- // Find nearest guideline index for a given Y position
- findNearestGuidelineIndex: function findNearestGuidelineIndex(yPosition) {
- var nearestGuidelineIndex = 0;
- var nearestDistance = Infinity;
- for (var g = 0; g < gameObjects.guidelineYPositions.length; g++) {
- var distance = Math.abs(yPosition - gameObjects.guidelineYPositions[g]);
- if (distance < nearestDistance) {
- nearestDistance = distance;
- nearestGuidelineIndex = g;
- }
- }
- return nearestGuidelineIndex;
+ guidelineYPositions.push(yPosition);
}
-};
-// Legacy function wrappers for backward compatibility
-function calculateGuidelinePositions() {
- utilityFunctions.calculateGuidelinePositions();
}
-// Create global LevelManager instance
-var levelManager = new LevelManager();
-// Create global level transition cutscene
-var levelTransitionCutscene = new LevelTransitionCutscene();
-// Integrate LevelManager with existing level management functions
-function initializeLevel(levelNumber) {
- return levelManager.initializeLevel(levelNumber);
-}
-function getCurrentLevelConfig() {
- return levelManager.getCurrentLevelConfig();
-}
-function checkLevelCompletion() {
- return levelManager.checkLevelCompletion();
-}
-function handleLevelFailure() {
- return levelManager.handleLevelFailure();
-}
-function isLevelUnlocked(levelNumber) {
- return levelManager.isLevelUnlocked(levelNumber);
-}
-function applyLevelModifiers(enemyType, baseStats) {
- return levelManager.applyLevelModifiers(enemyType, baseStats);
-}
-// Initialize level 1 at game start
-levelManager.initializeLevel(1);
-// Update game state references to use LevelManager
-gameState.level.current = levelManager.currentLevel;
-gameState.level.inProgress = levelManager.levelInProgress;
-// Legacy function wrapper for backward compatibility
+// Calculate snap positions from guideline intersections
function calculateSnapPositions() {
- utilityFunctions.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]
+ });
+ }
+ }
}
-// Legacy function wrapper for backward compatibility
+// Find nearest snap position to given coordinates
function findNearestSnapPosition(x, y) {
- return utilityFunctions.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;
}
-// Legacy function wrapper for backward compatibility
+// Show vertical guidelines during tower placement
function showVerticalGuidelines() {
- utilityFunctions.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);
+ }
}
-// Legacy function wrapper for backward compatibility
+// Hide vertical guidelines
function hideVerticalGuidelines() {
- utilityFunctions.hideVerticalGuidelines();
+ for (var i = 0; i < verticalGuidelines.length; i++) {
+ verticalGuidelines[i].destroy();
+ }
+ verticalGuidelines = [];
}
// Global UI container management
+var uiContainer = null;
// Function to create UI container
function createUIContainer() {
if (uiContainer && uiContainer.parent) {
cleanupUIContainer();
@@ -4239,59 +2603,32 @@
}
}
// Clean up UI container
cleanupUIContainer();
- // Stop any active level transitions
- if (levelTransitionCutscene) {
- levelTransitionCutscene.stop();
- }
// Reset all game state variables to initial values
- gameState.wave.active = false;
- gameState.wave.spawnTimer = 0;
- gameState.wave.spawnInterval = 420;
- gameState.wave.enemiesSpawned = 0;
- gameState.wave.maxEnemies = 10;
- gameState.wave.firstEnemySpawned = false;
- gameState.wave.current = 1;
- gameState.wave.enemiesDefeated = 0;
- gameState.player.score = 0;
- gameState.player.lives = 10;
- gameState.player.cash = 50;
- gameState.game.speedMultiplier = 1;
- gameState.game.introPlayed = false;
- gameState.game.started = false;
- gameState.placement.pendingCost = 10;
- // Reset level state using LevelManager
- if (levelManager) {
- levelManager.cleanupCurrentLevel();
- levelManager.currentLevel = 1;
- levelManager.levelInProgress = false;
- levelManager.levelStartTime = 0;
- levelManager.levelData = null;
- // Reinitialize level 1 after cleanup
- levelManager.initializeLevel(1);
- }
- gameState.level.current = 1;
- gameState.level.inProgress = false;
- gameState.level.startTime = 0;
- gameState.level.completionTime = 0;
- gameState.level.configuration.enemyHealthMultiplier = 1.0;
- gameState.level.configuration.enemySpeedMultiplier = 1.0;
- gameState.level.configuration.towerCostModifier = 1.0;
- gameState.level.configuration.specialMechanicsEnabled = false;
- gameState.level.state.backgroundAsset = null;
- gameState.level.state.activeEnemyPools = [];
- gameState.level.state.availableTowers = [];
- gameState.level.state.waveConfiguration = {};
- gameState.level.state.specialRules = {};
+ waveActive = false;
+ enemySpawnTimer = 0;
+ enemySpawnInterval = 420;
+ enemiesSpawned = 0;
+ maxEnemiesInWave = 10;
+ firstEnemySpawned = false;
+ currentWave = 1;
+ enemiesDefeated = 0;
+ playerScore = 0;
+ playerLives = 10;
+ playerCash = 50;
+ gameSpeedMultiplier = 1;
+ introPlayed = false;
+ gameStarted = false;
+ pendingTowerCost = 10;
// Clear display references
- gameDisplays.score = null;
- gameDisplays.lives = null;
- gameDisplays.cash = null;
+ scoreDisplay = null;
+ livesDisplay = null;
+ cashDisplay = null;
// Clear guideline positions
- gameObjects.guidelineYPositions = [];
- gameObjects.snapPositions = [];
- gameObjects.verticalGuidelines = [];
+ guidelineYPositions = [];
+ snapPositions = [];
+ verticalGuidelines = [];
}
// Clean up completely when game resets
LK.on('gameReset', function () {
// Call comprehensive reset function to ensure everything is cleaned up
@@ -4300,54 +2637,66 @@
createUIContainer();
});
// Initialize UI container at game start
createUIContainer();
-// Consolidated wave starting function that takes wave parameters
-function startWave(waveNumber, config) {
+// Start first wave of tower defense
+function startWave1() {
calculateGuidelinePositions();
waveActive = true;
- if (waveNumber === 1) {
- gameState.game.started = true; // Mark that the game has officially started
- }
+ gameStarted = true; // Mark that the game has officially started
enemySpawnTimer = 0;
enemiesSpawned = 0;
- currentWave = waveNumber;
+ currentWave = 1;
enemiesDefeated = 0;
- maxEnemiesInWave = config.maxEnemies;
- enemySpawnInterval = config.spawnInterval;
-}
-// Start first wave of tower defense
-function startWave1() {
- startWave(1, waveConfig[1]);
// Ensure UI container exists
if (!uiContainer) {
createUIContainer();
}
- // Helper function to map digit character to asset name
- function getDigitAssetName(digitChar) {
- var digitAssetMap = {
- '0': 'Zero',
- '1': 'One',
- '2': 'Two',
- '3': 'Three',
- '4': 'Four',
- '5': 'Five',
- '6': 'Six',
- '7': 'Seven',
- '8': 'Eight',
- '9': 'Nine'
- };
- return digitAssetMap[digitChar] || 'Zero';
- }
// Function to create digit display using number assets
function createDigitDisplay(number, x, y, scale) {
var digits = [];
var numStr = number.toString();
var digitSpacing = 60;
var startX = x - (numStr.length - 1) * digitSpacing / 2;
for (var i = 0; i < numStr.length; i++) {
var digitChar = numStr[i];
- var assetName = getDigitAssetName(digitChar);
+ var assetName;
+ // Map digits to asset names
+ switch (digitChar) {
+ case '0':
+ assetName = 'Zero';
+ break;
+ case '1':
+ assetName = 'One';
+ break;
+ case '2':
+ assetName = 'Two';
+ break;
+ case '3':
+ assetName = 'Three';
+ break;
+ case '4':
+ assetName = 'Four';
+ break;
+ case '5':
+ assetName = 'Five';
+ break;
+ case '6':
+ assetName = 'Six';
+ break;
+ case '7':
+ assetName = 'Seven';
+ break;
+ case '8':
+ assetName = 'Eight';
+ break;
+ case '9':
+ assetName = 'Nine';
+ break;
+ default:
+ assetName = 'Zero';
+ break;
+ }
var digit = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
x: startX + i * digitSpacing,
@@ -4372,9 +2721,44 @@
var newNumStr = newNumber.toString();
var newStartX = x - (newNumStr.length - 1) * digitSpacing / 2;
for (var k = 0; k < newNumStr.length; k++) {
var digitChar = newNumStr[k];
- var assetName = getDigitAssetName(digitChar);
+ var assetName;
+ switch (digitChar) {
+ case '0':
+ assetName = 'Zero';
+ break;
+ case '1':
+ assetName = 'One';
+ break;
+ case '2':
+ assetName = 'Two';
+ break;
+ case '3':
+ assetName = 'Three';
+ break;
+ case '4':
+ assetName = 'Four';
+ break;
+ case '5':
+ assetName = 'Five';
+ break;
+ case '6':
+ assetName = 'Six';
+ break;
+ case '7':
+ assetName = 'Seven';
+ break;
+ case '8':
+ assetName = 'Eight';
+ break;
+ case '9':
+ assetName = 'Nine';
+ break;
+ default:
+ assetName = 'Zero';
+ break;
+ }
var digit = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
x: newStartX + k * digitSpacing,
@@ -4404,9 +2788,9 @@
y: 50
});
uiContainer.addChild(scoreLabel);
// Create digit display for score
- scoreDisplay = createDigitDisplay(gameState.player.score, 750, 175, 0.8);
+ scoreDisplay = createDigitDisplay(playerScore, 750, 175, 0.8);
// Add cash display using Cash asset as label
var cashLabel = LK.getAsset('Cash', {
anchorX: 0.5,
anchorY: 0.5,
@@ -4414,9 +2798,9 @@
y: 170
});
uiContainer.addChild(cashLabel);
// Create digit display for cash
- cashDisplay = createDigitDisplay(gameState.player.cash, 1324, 170, 0.6);
+ cashDisplay = createDigitDisplay(playerCash, 1324, 170, 0.6);
// Add lives asset as label for lives digit display
var livesLabel = LK.getAsset('Lives', {
anchorX: 0.5,
anchorY: 0.5,
@@ -4424,9 +2808,9 @@
y: 50
});
uiContainer.addChild(livesLabel);
// Create digit display for lives
- livesDisplay = createDigitDisplay(gameState.player.lives, 1324, 50, 0.7);
+ livesDisplay = createDigitDisplay(playerLives, 1324, 50, 0.7);
// Add speed up button to top of screen left of score asset using X1speed asset
var speedUpButton = LK.getAsset('X1speed', {
anchorX: 0.5,
anchorY: 0.5,
@@ -4476,595 +2860,73 @@
skipWaveButton.anchor.set(0, 0);
skipWaveButton.x = 400;
skipWaveButton.y = 420;
uiContainer.addChild(skipWaveButton);
- // Consolidated click handler function to handle different click scenarios
- function handleGameClick(x, y, obj, clickType) {
- clickType = clickType || 'down';
- // Handle tower placement cancellation on empty space
- if (clickType === 'down' && placingTower && obj === game) {
- cleanupTowerPlacement();
- }
- // Handle story asset click transitions
- if (clickType === 'storyTransition' && obj && obj.destroy) {
- // Fade out story asset
- tween(obj, {
- alpha: 0
- }, {
- duration: 1000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- obj.destroy();
- // Continue to next level if this was story5
- if (obj.assetType === 'Story5') {
- // Use transition system for level change
- levelTransitionCutscene.startTransition(1, 2, function () {
- startLevel2();
- });
- }
- }
- });
- }
- }
// Add global click handler to cancel tower placement
game.down = function (x, y, obj) {
- handleGameClick(x, y, obj, 'down');
- };
- // Integrate UI configuration into main config
- GAME_CONFIG.ui = {
- towerButtons: {
- y: 2400,
- spacing: 50,
- startX: 430,
- scale: 0.8,
- secondRowOffset: 150
- },
- displays: {
- score: {
- labelX: 650,
- labelY: 50,
- valueX: 750,
- valueY: 175,
- scale: 0.8
- },
- cash: {
- labelX: 1175,
- labelY: 170,
- valueX: 1324,
- valueY: 170,
- scale: 0.6
- },
- lives: {
- labelX: 1224,
- labelY: 50,
- valueX: 1324,
- valueY: 50,
- scale: 0.7
- },
- speedButton: {
- x: 400,
- y: 75
- },
- skipWave: {
- x: 400,
- y: 420,
- size: 40,
- color: 0xFFFF00
- }
- },
- digitSpacing: 60,
- confirmButton: {
- offsetX: 60,
- offsetY: -60
- },
- cancelButton: {
- offsetX: 60,
- offsetY: 80 + 25 + 30
- }
- };
- // Legacy aliases for backward compatibility
- var uiConfig = GAME_CONFIG.ui;
- var towerButtonConfig = GAME_CONFIG.ui.towerButtons;
- // Level Management System
- var LEVEL_MANAGER = {
- // Current level state
- currentLevel: 1,
- levelInProgress: false,
- levelStartTime: 0,
- // Level initialization
- initializeLevel: function initializeLevel(levelNumber) {
- if (!GAME_CONFIG.levels[levelNumber]) {
- console.error('Level ' + levelNumber + ' not found in configuration');
- return false;
- }
- var levelConfig = GAME_CONFIG.levels[levelNumber];
- this.currentLevel = levelNumber;
- this.levelInProgress = true;
- this.levelStartTime = Date.now();
- // Reset game state for new level
- this.resetLevelState();
- // Load level-specific configurations
- this.loadLevelAssets(levelConfig);
- this.setupLevelMechanics(levelConfig);
- return true;
- },
- // Reset game state for level start
- resetLevelState: function resetLevelState() {
- // Clear existing enemies and towers
- for (var i = enemies.length - 1; i >= 0; i--) {
- if (enemies[i] && enemies[i].destroy) {
- enemies[i].destroy();
+ // 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;
}
- enemies = [];
- for (var i = towers.length - 1; i >= 0; i--) {
- if (towers[i] && towers[i].destroy) {
- towers[i].destroy();
- }
+ if (confirmButton) {
+ confirmButton.destroy();
+ confirmButton = null;
}
- towers = [];
- // Reset wave state
- gameState.wave.active = false;
- gameState.wave.current = 1;
- gameState.wave.enemiesSpawned = 0;
- gameState.wave.enemiesDefeated = 0;
- gameState.wave.spawnTimer = 0;
- gameState.wave.firstEnemySpawned = false;
- // Apply level-specific starting resources
- var levelConfig = GAME_CONFIG.levels[this.currentLevel];
- if (levelConfig.startingResources) {
- gameState.player.cash = levelConfig.startingResources.cash || 50;
- gameState.player.lives = levelConfig.startingResources.lives || 10;
- } else {
- gameState.player.cash = 50;
- gameState.player.lives = 10;
- }
- },
- // Load level-specific assets and background
- loadLevelAssets: function loadLevelAssets(levelConfig) {
- // Set background
- if (levelConfig.background) {
- var background = game.addChild(LK.getAsset(levelConfig.background.asset, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: levelConfig.background.position.x,
- y: levelConfig.background.position.y,
- scaleX: levelConfig.background.scale.x,
- scaleY: levelConfig.background.scale.y,
- alpha: 0
- }));
- // Fade in background
- tween(background, {
- alpha: 1
- }, {
- duration: 2000,
- easing: tween.easeInOut
- });
- }
- // Setup guidelines
- this.setupGuidelines(levelConfig);
- },
- // Setup level-specific mechanics
- setupLevelMechanics: function setupLevelMechanics(levelConfig) {
- // Apply tower cost modifiers
- if (levelConfig.towerCostModifier && levelConfig.towerCostModifier !== 1.0) {
- for (var towerType in GAME_CONFIG.towers) {
- var tower = GAME_CONFIG.towers[towerType];
- tower.modifiedCost = Math.floor(tower.cost * levelConfig.towerCostModifier);
- }
- }
- // Enable/disable specific towers based on level
- this.updateAvailableTowers(levelConfig.availableTowers);
- // Setup special mechanics
- if (levelConfig.specialMechanics) {
- this.applySpecialMechanics(levelConfig.specialMechanics);
- }
- },
- // Setup level-specific guidelines
- setupGuidelines: function setupGuidelines(levelConfig) {
- // Clear existing guidelines
- if (gameObjects.guidelines) {
- for (var i = 0; i < gameObjects.guidelines.length; i++) {
- if (gameObjects.guidelines[i] && gameObjects.guidelines[i].destroy) {
- gameObjects.guidelines[i].destroy();
- }
- }
- }
- gameObjects.guidelines = [];
- // Create new guidelines based on level layout
- var guidelineLayout = levelConfig.specialMechanics.guidelineLayout || 'standard';
- this.createGuidelineLayout(guidelineLayout);
- },
- // Create different guideline layouts
- createGuidelineLayout: function createGuidelineLayout(layoutType) {
- var lineSpacing = GAME_CONFIG.guidelines.screenHeight / 7;
- var guidelines = [];
- for (var i = 1; i <= GAME_CONFIG.guidelines.horizontalCount; i++) {
- var yPosition = lineSpacing * i;
- // Apply layout-specific modifications
- if (layoutType === 'industrial') {
- // Tighter spacing for industrial level
- yPosition = yPosition * 0.9;
- } else if (layoutType === 'zero_gravity') {
- // Curved guidelines for space level
- yPosition += Math.sin(i * 0.5) * 100;
- }
- // Apply standard offsets
- if (GAME_CONFIG.guidelines.offsets[i]) {
- yPosition += GAME_CONFIG.guidelines.offsets[i];
- }
- var guideLine = game.addChild(LK.getAsset('guideLine', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: GAME_CONFIG.guidelines.screenWidth / 2,
- y: yPosition,
- alpha: GAME_CONFIG.guidelines.alpha
- }));
- guidelines.push(guideLine);
- gameObjects.guidelineYPositions.push(yPosition);
- }
- gameObjects.guidelines = guidelines;
- },
- // Update available towers for level
- updateAvailableTowers: function updateAvailableTowers(availableTowers) {
- // Hide/show tower buttons based on availability
- // This would integrate with the existing tower button system
- var allTowerButtons = [waterTowerButton, gasTowerButton, basicTowerButton, electricTowerButton, advancedTowerButton, wifiTowerButton, fireButton, slingshotTowerButton];
- var towerMapping = {
- 'water': waterTowerButton || null,
- 'gas': gasTowerButton || null,
- 'air': basicTowerButton || null,
- 'electric': electricTowerButton || null,
- 'plasma': advancedTowerButton || null,
- 'wifi': wifiTowerButton || null,
- 'fire': fireButton || null,
- 'slingshot': slingshotTowerButton || null
- };
- // Hide all towers first
- for (var i = 0; i < allTowerButtons.length; i++) {
- if (allTowerButtons[i]) {
- allTowerButtons[i].alpha = 0.3; // Dim unavailable towers
- }
- }
- // Show available towers
- for (var j = 0; j < availableTowers.length; j++) {
- var towerType = availableTowers[j];
- if (towerMapping[towerType]) {
- towerMapping[towerType].alpha = 1.0; // Full opacity for available towers
- }
- }
- },
- // Apply special level mechanics
- applySpecialMechanics: function applySpecialMechanics(mechanics) {
- if (mechanics.environmentalHazards) {
- this.enableEnvironmentalHazards();
- }
- if (mechanics.enhancedEnemyAI) {
- this.enableEnhancedEnemyAI();
- }
- if (mechanics.gravityWells) {
- this.enableGravityWells();
- }
- if (mechanics.energyShields) {
- this.enableEnergyShields();
- }
- },
- // Environmental hazards system
- enableEnvironmentalHazards: function enableEnvironmentalHazards() {
- // Placeholder for environmental hazard implementation
- // Could include obstacles, moving platforms, etc.
- },
- // Enhanced enemy AI system
- enableEnhancedEnemyAI: function enableEnhancedEnemyAI() {
- // Placeholder for enhanced AI behaviors
- // Could include path adaptation, target prioritization, etc.
- },
- // Gravity wells system for space level
- enableGravityWells: function enableGravityWells() {
- // Placeholder for gravity well implementation
- // Could affect enemy movement patterns
- },
- // Energy shields system
- enableEnergyShields: function enableEnergyShields() {
- // Placeholder for energy shield implementation
- // Could provide temporary tower protection
- },
- // Level completion check
- checkLevelCompletion: function checkLevelCompletion() {
- var levelConfig = GAME_CONFIG.levels[this.currentLevel];
- // Check if all waves are completed
- if (gameState.wave.current > levelConfig.totalWaves && enemies.length === 0) {
- this.completeLevelSuccessfully();
- return true;
- }
- return false;
- },
- // Handle successful level completion
- completeLevelSuccessfully: function completeLevelSuccessfully() {
- this.levelInProgress = false;
- var completionTime = Date.now() - this.levelStartTime;
- var levelConfig = GAME_CONFIG.levels[this.currentLevel];
- // Award completion rewards
- if (levelConfig.completionReward) {
- gameState.player.score += levelConfig.completionReward.score;
- gameState.player.cash += levelConfig.completionReward.cash;
- }
- // Update level progress
- GAME_CONFIG.metadata.levelProgress[this.currentLevel].completed = true;
- GAME_CONFIG.metadata.levelProgress[this.currentLevel].bestScore = Math.max(GAME_CONFIG.metadata.levelProgress[this.currentLevel].bestScore, gameState.player.score);
- // Unlock next level
- if (this.currentLevel < 3) {
- GAME_CONFIG.metadata.highestUnlocked = Math.max(GAME_CONFIG.metadata.highestUnlocked, this.currentLevel + 1);
- }
- // Show level completion UI or transition to next level
- this.showLevelCompletionScreen();
- },
- // Show level completion screen
- showLevelCompletionScreen: function showLevelCompletionScreen() {
- // Create level completion display
- var completionScreen = new Container();
- LK.gui.addChild(completionScreen);
- var completionText = new Text2('LEVEL ' + this.currentLevel + ' COMPLETE!', {
- size: 80,
- fill: 0x00FF00
- });
- completionText.anchor.set(0.5, 0.5);
- completionText.x = 1024;
- completionText.y = 400;
- completionScreen.addChild(completionText);
- // Add continue button for next level
- if (this.currentLevel < 3) {
- var continueButton = new Text2('CONTINUE TO LEVEL ' + (this.currentLevel + 1), {
- size: 50,
- fill: 0xFFFFFF
- });
- continueButton.anchor.set(0.5, 0.5);
- continueButton.x = 1024;
- continueButton.y = 600;
- continueButton.down = function () {
- completionScreen.destroy();
- LEVEL_MANAGER.initializeLevel(LEVEL_MANAGER.currentLevel + 1);
- };
- completionScreen.addChild(continueButton);
- }
- // Auto-remove screen after delay
- LK.setTimeout(function () {
- if (completionScreen && completionScreen.parent) {
- completionScreen.destroy();
- }
- }, 5000);
- },
- // Handle level failure
- handleLevelFailure: function handleLevelFailure() {
- this.levelInProgress = false;
- // Reset to checkpoint or restart level
- this.showLevelFailureScreen();
- },
- // Show level failure screen
- showLevelFailureScreen: function showLevelFailureScreen() {
- // Create level failure display
- var failureScreen = new Container();
- LK.gui.addChild(failureScreen);
- var failureText = new Text2('LEVEL ' + this.currentLevel + ' FAILED', {
- size: 80,
- fill: 0xFF0000
- });
- failureText.anchor.set(0.5, 0.5);
- failureText.x = 1024;
- failureText.y = 400;
- failureScreen.addChild(failureText);
- // Add retry button
- var retryButton = new Text2('RETRY LEVEL', {
- size: 50,
- fill: 0xFFFFFF
- });
- retryButton.anchor.set(0.5, 0.5);
- retryButton.x = 1024;
- retryButton.y = 600;
- retryButton.down = function () {
- failureScreen.destroy();
- LEVEL_MANAGER.initializeLevel(LEVEL_MANAGER.currentLevel);
- };
- failureScreen.addChild(retryButton);
- // Auto-remove screen after delay
- LK.setTimeout(function () {
- if (failureScreen && failureScreen.parent) {
- failureScreen.destroy();
- }
- }, 5000);
+ hideVerticalGuidelines();
+ placingTower = false;
}
};
- // Extend utility functions with level-aware methods
- utilityFunctions.getCurrentLevelConfig = function () {
- return GAME_CONFIG.levels[LEVEL_MANAGER.currentLevel];
+ // Tower button configuration
+ var towerButtonConfig = {
+ y: 2400,
+ spacing: 50,
+ startX: 430,
+ scale: 0.8
};
- utilityFunctions.isLevelUnlocked = function (levelNumber) {
- return levelNumber <= GAME_CONFIG.metadata.highestUnlocked;
- };
- utilityFunctions.applyLevelModifiers = function (enemyType, baseStats) {
- var levelConfig = utilityFunctions.getCurrentLevelConfig();
- var enemyConfig = GAME_CONFIG.enemies[enemyType];
- if (enemyConfig.levelVariations && enemyConfig.levelVariations[LEVEL_MANAGER.currentLevel]) {
- var variation = enemyConfig.levelVariations[LEVEL_MANAGER.currentLevel];
- var modifiedStats = {
- health: Math.floor(baseStats.health * (variation.healthMultiplier || 1.0)),
- maxHealth: Math.floor(baseStats.maxHealth * (variation.healthMultiplier || 1.0)),
- speed: baseStats.speed * (variation.speedMultiplier || 1.0),
- damage: baseStats.damage ? Math.floor(baseStats.damage * (variation.damageMultiplier || 1.0)) : baseStats.damage
- };
- return modifiedStats;
- }
- return baseStats;
- };
- // Level state management utility functions
- utilityFunctions.initializeLevelState = function (levelNumber) {
- if (!GAME_CONFIG.levels[levelNumber]) {
- console.error('Level ' + levelNumber + ' not found in configuration');
- return false;
- }
- var levelConfig = GAME_CONFIG.levels[levelNumber];
- // Update level state
- gameState.level.current = levelNumber;
- gameState.level.inProgress = true;
- gameState.level.startTime = Date.now();
- gameState.level.progression.levelStats[levelNumber].attempts++;
- gameState.level.progression.currentLevel = levelNumber;
- // Apply level-specific configurations
- gameState.level.configuration.towerCostModifier = levelConfig.towerCostModifier || 1.0;
- gameState.level.state.activeEnemyPools = levelConfig.enemyPools || [];
- gameState.level.state.availableTowers = levelConfig.availableTowers || [];
- gameState.level.state.waveConfiguration = levelConfig.waves || {};
- gameState.level.state.specialRules = levelConfig.specialMechanics || {};
- // Apply enemy and tower modifiers
- if (levelConfig.specialMechanics) {
- gameState.level.configuration.enemyHealthMultiplier = levelConfig.specialMechanics.increasedEnemyHealth || 1.0;
- gameState.level.configuration.enemySpeedMultiplier = levelConfig.specialMechanics.increasedEnemySpeed || 1.0;
- gameState.level.configuration.specialMechanicsEnabled = true;
- }
- return true;
- };
- utilityFunctions.completeLevelSuccessfully = function () {
- if (!gameState.level.inProgress) return;
- var currentLevel = gameState.level.current;
- var completionTime = Date.now() - gameState.level.startTime;
- gameState.level.completionTime = completionTime;
- gameState.level.inProgress = false;
- // Update level progression stats
- var levelStats = gameState.level.progression.levelStats[currentLevel];
- levelStats.completed = true;
- levelStats.bestScore = Math.max(levelStats.bestScore, gameState.player.score);
- if (levelStats.fastestTime === 0 || completionTime < levelStats.fastestTime) {
- levelStats.fastestTime = completionTime;
- }
- // Check for perfect run (no lives lost)
- if (gameState.player.lives === 10) {
- levelStats.perfectRuns++;
- }
- // Unlock next level
- if (currentLevel < 3 && !gameState.level.progression.completedLevels.includes(currentLevel)) {
- gameState.level.progression.completedLevels.push(currentLevel);
- gameState.level.progression.highestUnlocked = Math.max(gameState.level.progression.highestUnlocked, currentLevel + 1);
- }
- // Award completion rewards
- var levelConfig = GAME_CONFIG.levels[currentLevel];
- if (levelConfig.completionReward) {
- gameState.player.score += levelConfig.completionReward.score || 0;
- gameState.player.cash += levelConfig.completionReward.cash || 0;
- // Unlock new towers
- if (levelConfig.completionReward.unlockedTowers) {
- for (var i = 0; i < levelConfig.completionReward.unlockedTowers.length; i++) {
- var towerType = levelConfig.completionReward.unlockedTowers[i];
- if (gameState.level.unlocks.towers.indexOf(towerType) === -1) {
- gameState.level.unlocks.towers.push(towerType);
- }
- }
- }
- }
- return {
- completionTime: completionTime,
- score: gameState.player.score,
- nextLevelUnlocked: gameState.level.progression.highestUnlocked > currentLevel
- };
- };
- utilityFunctions.failLevel = function () {
- if (!gameState.level.inProgress) return;
- gameState.level.inProgress = false;
- gameState.level.completionTime = Date.now() - gameState.level.startTime;
- // No progression rewards for failure, but track attempt
- // Level can be retried without penalty
- };
- utilityFunctions.isLevelCompleted = function (levelNumber) {
- return gameState.level.progression.levelStats[levelNumber] && gameState.level.progression.levelStats[levelNumber].completed;
- };
- utilityFunctions.getLevelStats = function (levelNumber) {
- return gameState.level.progression.levelStats[levelNumber] || null;
- };
- utilityFunctions.resetLevelState = function () {
- gameState.level.inProgress = false;
- gameState.level.startTime = 0;
- gameState.level.completionTime = 0;
- gameState.level.configuration.enemyHealthMultiplier = 1.0;
- gameState.level.configuration.enemySpeedMultiplier = 1.0;
- gameState.level.configuration.towerCostModifier = 1.0;
- gameState.level.configuration.specialMechanicsEnabled = false;
- gameState.level.state.backgroundAsset = null;
- gameState.level.state.activeEnemyPools = [];
- gameState.level.state.availableTowers = [];
- gameState.level.state.waveConfiguration = {};
- gameState.level.state.specialRules = {};
- };
- // Reusable tower button creation function
- function createTowerButton(buttonAssetName, x, y, config) {
- var button = game.addChild(LK.getAsset(buttonAssetName, {
+ // Helper function to create tower buttons
+ function createTowerButton(assetName, x, y, cost, towerType, towerAsset, properties) {
+ var button = game.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
scaleX: towerButtonConfig.scale,
scaleY: towerButtonConfig.scale
}));
button.down = function (bx, by, obj) {
- if (gameState.player.cash >= config.cost && !placingTower) {
+ if (playerCash >= cost && !placingTower) {
LK.getSound('Towerselect').play();
- // Start tower placement mode - update both systems
- placingTower = true;
- gameState.placement.active = true;
- pendingTowerCost = config.cost;
- gameState.placement.pendingCost = config.cost;
- // Calculate snap positions and show guidelines
- utilityFunctions.calculateSnapPositions();
- utilityFunctions.showVerticalGuidelines();
- // Find initial snap position
- var snapPos = utilityFunctions.findNearestSnapPosition(bx, by);
- // Create tower preview
- towerPreview = game.addChild(LK.getAsset(config.towerAsset, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: snapPos.x,
- y: snapPos.y,
- alpha: 0.7
- }));
- gameState.placement.preview = towerPreview;
- // Create blade preview for air towers
- if (config.towerType === 'air') {
- towerPreview.bladePreview = game.addChild(LK.getAsset('Blades', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: snapPos.x,
- y: snapPos.y - 70,
- alpha: 0.7
- }));
- }
- // Create placement buttons using consolidated function
- var buttons = createPlacementButtons(snapPos, config);
- confirmButton = buttons.confirm;
- cancelButton = buttons.cancel;
- gameState.placement.confirmButton = confirmButton;
- gameState.placement.cancelButton = cancelButton;
+ startTowerPlacement(cost, towerType, towerAsset, properties);
}
};
return button;
}
// Helper function to start tower placement
function startTowerPlacement(cost, towerType, towerAsset, properties) {
- gameState.placement.active = true;
- gameState.placement.pendingCost = cost;
- utilityFunctions.calculateSnapPositions();
- utilityFunctions.showVerticalGuidelines();
+ placingTower = true;
+ pendingTowerCost = cost;
+ calculateSnapPositions();
+ showVerticalGuidelines();
createTowerPreview(towerAsset, towerType, properties);
}
// Helper function to create tower preview
function createTowerPreview(towerAsset, towerType, properties) {
- var snapPos = utilityFunctions.findNearestSnapPosition(1024, 1366);
- gameState.placement.preview = game.addChild(LK.getAsset(towerAsset, {
+ var snapPos = findNearestSnapPosition(1024, 1366);
+ towerPreview = game.addChild(LK.getAsset(towerAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y,
alpha: 0.7
}));
if (towerType === 'air') {
- gameState.placement.preview.bladePreview = game.addChild(LK.getAsset('Blades', {
+ towerPreview.bladePreview = game.addChild(LK.getAsset('Blades', {
anchorX: 0.5,
anchorY: 0.5,
x: snapPos.x,
y: snapPos.y - 70,
@@ -5072,67 +2934,31 @@
}));
}
createConfirmCancelButtons(snapPos);
}
- // Consolidated function to handle placement button creation and event handling
- function createPlacementButtons(snapPos, towerConfig) {
- // Create confirmation button
- var confirmBtn = game.addChild(LK.getAsset('ConfirmButton', {
+ // Helper function to create confirm/cancel buttons
+ function createConfirmCancelButtons(snapPos) {
+ confirmButton = game.addChild(LK.getAsset('ConfirmButton', {
anchorX: 0.5,
anchorY: 0.5,
- x: snapPos.x + uiConfig.confirmButton.offsetX,
- y: snapPos.y + uiConfig.confirmButton.offsetY,
+ x: snapPos.x + 60,
+ y: snapPos.y - 60,
zIndex: 2000
}));
- // Create cancellation button
- var cancelBtn = game.addChild(LK.getAsset('Cancelbutton', {
+ cancelButton = game.addChild(LK.getAsset('Cancelbutton', {
anchorX: 0.5,
anchorY: 0.5,
- x: snapPos.x + uiConfig.cancelButton.offsetX,
- y: snapPos.y + uiConfig.cancelButton.offsetY,
+ x: snapPos.x + 60,
+ y: snapPos.y - 60 + 155,
zIndex: 2000
}));
- // Consolidated cancel handler
- cancelBtn.down = function () {
- handlePlacementCancel();
+ cancelButton.down = function () {
+ cleanupTowerPlacement();
};
- // Consolidated confirm handler
- confirmBtn.down = function () {
- handlePlacementConfirm(towerConfig);
+ confirmButton.down = function () {
+ confirmTowerPlacement();
};
- return {
- confirm: confirmBtn,
- cancel: cancelBtn
- };
}
- // Consolidated placement cancellation handler
- function handlePlacementCancel() {
- cleanupTowerPlacement();
- }
- // Consolidated placement confirmation handler
- function handlePlacementConfirm(towerConfig) {
- if (!placingTower || !towerPreview) return;
- // Deduct cost from both systems
- gameState.player.cash -= pendingTowerCost;
- playerCash = gameState.player.cash;
- if (cashDisplay) {
- cashDisplay.update(gameState.player.cash);
- }
- // Create tower from preview
- var newTower = createTowerFromPlacement(towerConfig);
- game.addChild(newTower);
- towers.push(newTower);
- sortGameObjectsByY();
- // Clean up and play sound
- cleanupTowerPlacement();
- LK.getSound('Confirm').play();
- }
- // Helper function to create confirm/cancel buttons (legacy wrapper)
- function createConfirmCancelButtons(snapPos) {
- var buttons = createPlacementButtons(snapPos, {});
- confirmButton = buttons.confirm;
- cancelButton = buttons.cancel;
- }
// Helper function to cleanup tower placement
function cleanupTowerPlacement() {
if (towerPreview) {
if (towerPreview.bladePreview) {
@@ -5166,196 +2992,914 @@
cleanupTowerPlacement();
pendingTowerCost = 10;
LK.getSound('Confirm').play();
}
- // Helper function to create tower from preview (legacy)
+ // Helper function to create tower from preview
function createTowerFromPreview() {
- return createTowerFromPlacement({});
- }
- // Consolidated enemy asset cleanup function
- function cleanupEnemyAssets(enemy) {
- // Clean up main graphics
- if (enemy.enemyGraphics && enemy.enemyGraphics.parent) {
- enemy.enemyGraphics.destroy();
- enemy.enemyGraphics = null;
+ var newTower = new Tower();
+ newTower.x = towerPreview.x;
+ newTower.y = towerPreview.y;
+ newTower.cost = pendingTowerCost;
+ // Set tower type and properties based on preview
+ if (towerPreview.bladePreview) {
+ newTower.setAssetType('Fan');
+ newTower.towerType = 'air';
+ newTower.damage = 35;
+ newTower.range = 350;
+ newTower.fireRate = 30;
}
- // Clean up wheel graphics
- if (enemy.leftWheel && enemy.leftWheel.parent) {
- enemy.leftWheel.destroy();
- enemy.leftWheel = null;
+ // Assign 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;
+ }
}
- if (enemy.rightWheel && enemy.rightWheel.parent) {
- enemy.rightWheel.destroy();
- enemy.rightWheel = null;
+ newTower.guidelineIndex = nearestGuidelineIndex;
+ return newTower;
+ }
+ // Define tower button positioning variables
+ var startX = 430;
+ var towerButtonY = 2400;
+ var buttonSpacing = 50;
+ // 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
+ }));
+ // WiFi Tower Button (to the right of plasma button)
+ var wifiTowerButton = game.addChild(LK.getAsset('Wifibutton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: startX + electricTowerButton.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 + 400,
+ y: towerButtonY,
+ scaleX: 0.8,
+ scaleY: 0.8
+ }));
+ // Slingshot Tower Button (to the right of wifi button)
+ var slingshotTowerButton = game.addChild(LK.getAsset('Slingshotbutton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: startX + electricTowerButton.width * 0.8 + buttonSpacing + advancedTowerButton.width * 0.8 + buttonSpacing + wifiTowerButton.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,
+ zIndex: 2000
+ }));
+ // 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
+ zIndex: 2000
+ }));
+ // 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.update(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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
}
- // Clean up roboarm graphics
- if (enemy.roboarm && enemy.roboarm.parent) {
- enemy.roboarm.destroy();
- enemy.roboarm = null;
+ };
+ wifiTowerButton.down = function (x, y, obj) {
+ if (playerCash >= 80 && !placingTower) {
+ LK.getSound('Towerselect').play();
+ // Start tower placement mode for wifi tower
+ placingTower = true;
+ pendingTowerCost = 80; // WiFi tower costs $80
+ // 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.update(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;
+ newTower.range = 600;
+ newTower.fireRate = 30;
+ // 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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
}
- }
- // Consolidated enemy asset recreation function
- function recreateEnemyAssets(enemy, assetType) {
- enemy.assetType = assetType;
- enemy.enemyGraphics = enemy.attachAsset(assetType, {
- anchorX: 0.5,
- anchorY: 0.5
- });
- // Add special assets for Robot enemies
- if (assetType === 'Robot') {
- // Add left wheel
- enemy.leftWheel = enemy.attachAsset('Wheel', {
+ };
+ 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: -80,
- y: 95,
- scaleX: 0.6,
- scaleY: 0.6
- });
- // Move wheel1 (leftWheel) left 25 pixels from its current position
- tween(enemy.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
- enemy.rightWheel = enemy.attachAsset('Wheel', {
+ 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: 55,
- // Move from 80 to 55 (25 pixels left)
- y: 95,
- scaleX: 0.6,
- scaleY: 0.6
- });
- // Add roboarm attachment
- enemy.roboarm = enemy.attachAsset('Roboarm', {
+ 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: 0,
- y: -5,
- scaleX: 0.5,
- scaleY: 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.update(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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
}
- // UFO boss special properties
- if (assetType === 'UFO') {
- enemy.isUFOBoss = true;
- enemy.abductionTarget = null;
- enemy.isAbducting = false;
- enemy.hasHoveredDown = false;
- enemy.tractorBeam = null;
- enemy.speed = 1; // Slower movement for boss
- enemy.targetX = 0; // Move to goal at x=0
+ };
+ 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.update(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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
}
- }
- // Helper function to create tower from placement data
- function createTowerFromPlacement(config) {
- var newTower = new Tower();
- var preview = gameState.placement.preview || towerPreview;
- newTower.x = preview.x;
- newTower.y = preview.y;
- newTower.cost = gameState.placement.pendingCost || pendingTowerCost;
- newTower.towerType = config.towerType || 'basic';
- // Set asset and properties from config
- if (config.towerAsset) {
- newTower.setAssetType(config.towerAsset);
+ };
+ 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.update(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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // Clean up placement mode
+ towerPreview.destroy();
+ confirmButton.destroy();
+ cancelButton.destroy();
+ towerPreview = null;
+ confirmButton = null;
+ hideVerticalGuidelines();
+ placingTower = false;
+ // Play confirmation sound
+ LK.getSound('Confirm').play();
+ }
+ };
}
- if (config.damage) newTower.damage = config.damage;
- if (config.range) newTower.range = config.range;
- if (config.fireRate) newTower.fireRate = config.fireRate;
- if (config.isTower1) newTower.isTower1 = true;
- // Special handling for blade preview (air towers)
- if (preview.bladePreview) {
- newTower.setAssetType('Fan');
- newTower.towerType = 'air';
- newTower.damage = 35;
- newTower.range = 350;
- newTower.fireRate = 30;
+ };
+ 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.update(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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
}
- // Special handling for water towers
- if (config.towerType === 'water') {
- newTower.attachAsset('Tower1', {
+ };
+ 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
- });
+ 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.update(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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
}
- // Assign to nearest guideline
- newTower.guidelineIndex = utilityFunctions.findNearestGuidelineIndex(newTower.y);
- return newTower;
- }
- // Use consolidated configuration for tower button positioning
- var startX = GAME_CONFIG.ui.towerButtons.startX;
- var towerButtonY = GAME_CONFIG.ui.towerButtons.y;
- var buttonSpacing = GAME_CONFIG.ui.towerButtons.spacing;
- // Create all tower buttons using consolidated function
- var waterTowerButton = createTowerButton('Waterbutton', startX, towerButtonY, {
- cost: 10,
- towerType: 'water',
- towerAsset: 'Tower1',
- damage: 35,
- range: 650,
- fireRate: 60,
- isTower1: true
- });
- var gasTowerButton = createTowerButton('Gasbutton', startX + 341 + buttonSpacing, towerButtonY, {
- cost: 20,
- towerType: 'dog',
- towerAsset: 'Dog',
- damage: 30,
- range: 600,
- fireRate: 45
- });
- var basicTowerButton = createTowerButton('Airbutton', startX + 341 + buttonSpacing + 341 + buttonSpacing, towerButtonY, {
- cost: 30,
- towerType: 'air',
- towerAsset: 'Fan',
- damage: 35,
- range: 350,
- fireRate: 30
- });
- var electricTowerButton = createTowerButton('Electricbutton', startX, towerButtonY + 150 + buttonSpacing, {
- cost: 50,
- towerType: 'electric',
- towerAsset: 'Bugzapper',
- damage: 60,
- range: 350,
- fireRate: 40
- });
- var advancedTowerButton = createTowerButton('Plasmabutton', startX + 341 + buttonSpacing, towerButtonY + 150 + buttonSpacing, {
- cost: 60,
- towerType: 'plasma',
- towerAsset: 'Plasmaball',
- damage: 80,
- range: 400,
- fireRate: 60
- });
- var wifiTowerButton = createTowerButton('Wifibutton', startX + 341 + buttonSpacing + 360 + buttonSpacing, towerButtonY + 150 + buttonSpacing, {
- cost: 80,
- towerType: 'wifi',
- towerAsset: 'Wifitower',
- damage: 100,
- range: 600,
- fireRate: 30
- });
- var fireButton = createTowerButton('Firebutton', startX + 341 + buttonSpacing + 341 + buttonSpacing + 400, towerButtonY, {
- cost: 40,
- towerType: 'fire',
- towerAsset: 'Fireworks',
- damage: 40,
- range: 400,
- fireRate: 50
- });
- var slingshotTowerButton = createTowerButton('Slingshotbutton', startX + 341 + buttonSpacing + 360 + buttonSpacing + 340 + buttonSpacing, towerButtonY + 150 + buttonSpacing, {
- cost: 35,
- towerType: 'slingshot',
- towerAsset: 'Slingshot',
- damage: 80,
- range: 1300,
- fireRate: 120
- });
- // Tower button click handlers are now consolidated into the createTowerButton function
+ };
+ slingshotTowerButton.down = function (x, y, obj) {
+ if (playerCash >= 35 && !placingTower) {
+ LK.getSound('Towerselect').play();
+ // Start tower placement mode for slingshot tower
+ placingTower = true;
+ pendingTowerCost = 35; // Slingshot tower costs $35
+ // 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.update(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 = 80;
+ newTower.range = 1300;
+ newTower.fireRate = 120; // Slower shooting (manual aiming)
+ // 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);
+ // Sort game objects immediately after adding tower to ensure proper depth rendering
+ sortGameObjectsByY();
+ // 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();
+ }
+ };
+ }
+ };
// 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--) {
@@ -5365,15 +3909,137 @@
enemiesSpawned = maxEnemiesInWave;
enemiesDefeated = maxEnemiesInWave;
// Check which wave we're skipping and trigger appropriate transition
if (currentWave === 1) {
- showWaveTransition(2, startWave2);
+ // 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) {
- showWaveTransition(3, startWave3);
+ // 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) {
- showWaveTransition(4, startWave4);
+ // 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) {
- showWaveTransition(5, startWave5);
+ // 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,
@@ -5390,10 +4056,20 @@
easing: tween.easeInOut,
onFinish: function onFinish() {
// Add click handler to story5 to transition to level2
story5Asset.down = function (x, y, obj) {
- story5Asset.assetType = 'Story5';
- handleGameClick(x, y, story5Asset, 'storyTransition');
+ // Fade out story5 asset
+ tween(story5Asset, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ story5Asset.destroy();
+ // Start level2 with backdrop2
+ startLevel2();
+ }
+ });
};
}
});
} else {
@@ -5402,96 +4078,114 @@
};
}
// Start second wave of tower defense
function startWave2() {
- startWave(2, waveConfig[2]);
+ 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() {
- startWave(3, waveConfig[3]);
+ 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() {
- startWave(4, waveConfig[4]);
+ 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() {
- startWave(5, waveConfig[5]);
+ 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;
- // Get current level and wave configuration
- var levelConfig = levelManager.getCurrentLevelConfig();
- if (!levelConfig || !levelConfig.waves || !levelConfig.waves[currentWave]) {
- console.error('Invalid wave configuration for level ' + levelManager.currentLevel + ', wave ' + currentWave);
- return;
- }
- var waveConfig = levelConfig.waves[currentWave];
- var enemyType = waveConfig.enemyType;
- var baseEnemyConfig = GAME_CONFIG.enemies[enemyType];
- if (!baseEnemyConfig) {
- console.error('Enemy type ' + enemyType + ' not found in configuration');
- return;
- }
- // Create new enemy instance
- var enemy = new Enemy();
- // Apply base stats and asset type
- enemy.setAssetType(enemyType);
- // Apply level-specific modifiers to base stats
- var baseStats = {
- health: baseEnemyConfig.health,
- maxHealth: baseEnemyConfig.maxHealth,
- speed: baseEnemyConfig.speed,
- damage: baseEnemyConfig.damage || 0
- };
- var modifiedStats = applyLevelModifiers(enemyType, baseStats);
- // Set enemy stats
- enemy.health = modifiedStats.health;
- enemy.maxHealth = modifiedStats.maxHealth;
- enemy.speed = modifiedStats.speed;
- // Handle special enemy behaviors based on level configuration
- if (levelConfig.specialMechanics) {
- if (levelConfig.specialMechanics.enemyBehavior === 'aggressive') {
- enemy.speed *= 1.2;
- }
- if (levelConfig.specialMechanics.enhancedEnemyAI) {
- enemy.hasEnhancedAI = true;
- }
- }
- // Pick random guideline for spawning
+ // 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;
- // Special positioning for boss-type enemies
- if (enemyType === 'UFO' || enemyType === 'Giantrobot' || enemyType === 'MothershipCore') {
+ // 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;
- enemy.baseY = 2732 / 2;
- enemy.guidelineIndex = -1; // Can be targeted by all towers
- } else {
- enemy.x = 2048 + 100; // Start off right side of screen
- enemy.y = spawnY;
- enemy.guidelineIndex = randomIndex;
- enemy.baseY = guidelineYPositions[randomIndex];
+ 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);
- // Sound effects
+ // 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 = utilityFunctions.findNearestSnapPosition(x, y);
+ var snapPos = findNearestSnapPosition(x, y);
towerPreview.x = snapPos.x;
towerPreview.y = snapPos.y;
// Update blade preview position if it exists
if (towerPreview.bladePreview) {
@@ -5502,9 +4196,9 @@
confirmButton.x = snapPos.x + 60;
confirmButton.y = snapPos.y - 60;
}
// Update cancel button position if it exists
- if (cancelButton) {
+ if (typeof cancelButton !== 'undefined' && cancelButton) {
cancelButton.x = snapPos.x + 60;
cancelButton.y = snapPos.y - 60 + 80 + 25 + 30;
}
}
@@ -5562,133 +4256,212 @@
parent.addChild(obj);
}
}
}
-// Unified function to show wave transition with fade in/out effects
-function showWaveTransition(waveNumber, callback) {
- var waveAsset = game.addChild(LK.getAsset('Wave' + waveNumber, {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 2048 / 2,
- y: 2732 / 2,
- alpha: 0,
- zIndex: 1000
- }));
- var duration = waveNumber === 3 ? 750 : 1500;
- var holdTime = waveNumber === 3 ? 1500 : 0;
- tween(waveAsset, {
- alpha: 1
- }, {
- duration: duration,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- var fadeOut = function fadeOut() {
- tween(waveAsset, {
- alpha: 0
- }, {
- duration: duration,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- waveAsset.destroy();
- callback();
- }
- });
- };
- if (holdTime > 0) {
- LK.setTimeout(fadeOut, holdTime);
- } else {
- fadeOut();
- }
- }
- });
-}
// Update tower defense game
game.update = function () {
- // Only operate when wave is active
- if (!gameState.wave.active) return;
- // Cache frequently accessed values to avoid repeated property lookups
- var enemiesLength = enemies.length;
- var towersLength = towers.length;
- var currentTick = LK.ticks;
- // Only sort game objects every 5th frame instead of every frame to reduce CPU load
- if (currentTick % 5 === 0) {
- sortGameObjectsByY();
- }
- // Spawn enemies - cache speed multiplier calculation
- var adjustedSpawnTimer = gameSpeedMultiplier;
+ // Only run tower defense logic when wave is active
+ if (!waveActive) return;
+ // Sort game objects by Y position for proper depth ordering every frame during active wave
+ // This ensures enemies and towers below others are rendered in front
+ sortGameObjectsByY();
+ // Spawn enemies
if (enemiesSpawned < maxEnemiesInWave) {
- enemySpawnTimer += adjustedSpawnTimer;
+ enemySpawnTimer += gameSpeedMultiplier;
if (enemySpawnTimer >= enemySpawnInterval) {
spawnEnemy();
enemiesSpawned++;
enemySpawnTimer = 0;
}
}
- // Check if wave is complete - use cached length value
- if (enemiesSpawned >= maxEnemiesInWave && enemiesLength === 0) {
+ // Check if wave is complete
+ if (enemiesSpawned >= maxEnemiesInWave && enemies.length === 0) {
// Wave complete - transition to next wave
+ // Helper function to show wave transition
+ var showWaveTransition = function showWaveTransition(waveNumber, callback) {
+ var waveAsset = game.addChild(LK.getAsset('Wave' + waveNumber, {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 2732 / 2,
+ alpha: 0,
+ zIndex: 1000
+ }));
+ var duration = waveNumber === 3 ? 750 : 1500;
+ var holdTime = waveNumber === 3 ? 1500 : 0;
+ tween(waveAsset, {
+ alpha: 1
+ }, {
+ duration: duration,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ var fadeOut = function fadeOut() {
+ tween(waveAsset, {
+ alpha: 0
+ }, {
+ duration: duration,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ waveAsset.destroy();
+ callback();
+ }
+ });
+ };
+ if (holdTime > 0) {
+ LK.setTimeout(fadeOut, holdTime);
+ } else {
+ fadeOut();
+ }
+ }
+ });
+ };
waveActive = false;
// Award player $50 for completing wave
- var waveReward = 50;
- var levelConfig = levelManager.getCurrentLevelConfig();
- if (levelConfig && levelConfig.waves && levelConfig.waves[currentWave]) {
- waveReward = levelConfig.waves[currentWave].cashReward || 50;
- }
- playerCash += waveReward;
- gameState.player.cash += waveReward;
+ playerCash += 50;
if (cashDisplay) {
cashDisplay.update(playerCash);
}
- // Check for level completion before transitioning to next wave
- if (levelManager.checkLevelCompletion()) {
- return; // Level completed, transition system will handle level change
+ if (currentWave === 1) {
+ showWaveTransition(2, 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();
+ }
+ });
+ };
+ }
+ });
}
- // Use cached currentWave value and switch statement for better performance
- switch (currentWave) {
- case 1:
- showWaveTransition(2, startWave2);
- break;
- case 2:
- showWaveTransition(3, startWave3);
- break;
- case 3:
- showWaveTransition(4, startWave4);
- break;
- case 4:
- showWaveTransition(5, startWave5);
- break;
- case 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) {
- story5Asset.assetType = 'Story5';
- handleGameClick(x, y, story5Asset, 'storyTransition');
- };
- }
- });
- break;
- }
}
};
// Start the intro cutscene only if it hasn't been played yet and wave hasn't started
-if (!gameState.game.introPlayed && !gameState.wave.active && gameState.wave.current === 1 && gameState.wave.enemiesSpawned === 0 && !gameState.game.started) {
- gameState.game.introPlayed = true;
+if (!introPlayed && !waveActive && currentWave === 1 && enemiesSpawned === 0 && !gameStarted) {
+ introPlayed = true;
introCutscene.play();
-} else if (!gameState.wave.active && gameState.wave.current === 1 && gameState.wave.enemiesSpawned === 0 && (gameState.game.introPlayed || gameState.game.started)) {
+} else if (!waveActive && currentWave === 1 && enemiesSpawned === 0 && (introPlayed || gameStarted)) {
// If intro has been played or game has been reset, skip directly to backdrop scene
skipToBackdrop();
}
;
@@ -5756,65 +4529,5 @@
y: yPosition,
alpha: 0.5
}));
}
-}
-// Show final game completion screen
-self.showGameCompletionScreen = function () {
- var completionOverlay = game.addChild(LK.getAsset('guideLine', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 2048 / 2,
- y: 2732 / 2,
- scaleX: 2048 / 2200,
- scaleY: 2732 / 4,
- tint: 0x000000,
- alpha: 0
- }));
- // Fade to black
- tween(completionOverlay, {
- alpha: 0.9
- }, {
- duration: 1500,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- // Show victory text
- var victoryText = new Text2('CONGRATULATIONS!', {
- size: 100,
- fill: 0xFFD700
- });
- victoryText.anchor.set(0.5, 0.5);
- victoryText.x = 2048 / 2;
- victoryText.y = 2732 / 2 - 200;
- victoryText.alpha = 0;
- game.addChild(victoryText);
- var gameCompleteText = new Text2('GAME COMPLETE', {
- size: 60,
- fill: 0x00FF00
- });
- gameCompleteText.anchor.set(0.5, 0.5);
- gameCompleteText.x = 2048 / 2;
- gameCompleteText.y = 2732 / 2;
- gameCompleteText.alpha = 0;
- game.addChild(gameCompleteText);
- // Fade in victory text
- tween(victoryText, {
- alpha: 1
- }, {
- duration: 2000,
- easing: tween.easeInOut
- });
- tween(gameCompleteText, {
- alpha: 1
- }, {
- duration: 2000,
- easing: tween.easeInOut,
- onFinish: function onFinish() {
- // Show final score and stats
- LK.setTimeout(function () {
- LK.showYouWin();
- }, 3000);
- }
- });
- }
- });
-};
\ No newline at end of file
+}
\ No newline at end of file
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