User prompt
Fix errors
User prompt
"Start game" starts from last level player passed ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Create restart button
User prompt
Make colonists' assets disappear when they reach the bunker
User prompt
Each level's coin count is based on leftover coins from the previous level. ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Start coin count at 25.
User prompt
Move coin count left 100 spaces
User prompt
Move coin count left 100 spaces
User prompt
Move coin count left 50 spaces
User prompt
Move coin count left 15 spaces
User prompt
Cannot see colonists when they reach bunker
User prompt
Add colonists fleeing the colony to the bunker BEFORE enemy spawns in-game. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add trash can in right bottom corner.
User prompt
Double click to upgrade
User prompt
Cannot sell or upgrade tower
User prompt
Fix errora
User prompt
Place different types of towers to defend against waves of enemies - Manage gold resources to buy and upgrade towers - Survive increasingly difficult waves of enemies
User prompt
Create tower system
User prompt
Towers doesn't shoot bugs
User prompt
Cannot click on towers and place them in gameplay
User prompt
Still cannot see towers
User prompt
Shrink the gameplay screen to make the coin count and towers visible.
User prompt
Make tower icons visible during GAME PLAY
User prompt
Place towers on botton of play screen
User prompt
Level progress when all bugs are killed
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bug = Container.expand(function (type, wave) { var self = Container.call(this); self.type = type || 'small'; self.wave = wave || 1; self.isBoss = false; self.isFlying = false; self.isArmored = false; self.slowResistance = 0; self.pathIndex = 0; // Set base stats based on type switch (self.type) { case 'small': self.health = 15; self.speed = 2.5; self.reward = 5; break; case 'medium': self.health = 35; self.speed = 2; self.reward = 7; break; case 'large': self.health = 80; self.speed = 1.5; self.reward = 10; break; case 'fast': self.health = 20; self.speed = 4; self.reward = 8; break; case 'armored': self.health = 120; self.speed = 1; self.reward = 15; self.isArmored = true; break; case 'flying': self.health = 40; self.speed = 3; self.reward = 12; self.isFlying = true; break; case 'boss': self.health = 500; self.speed = 0.8; self.reward = 50; self.isBoss = true; self.slowResistance = 0.7; break; } // Scale with wave var waveMultiplier = 1 + (self.wave - 1) * 0.15; self.health = Math.floor(self.health * waveMultiplier); self.maxHealth = self.health; self.slowEffect = 1; self.poisonTimer = 0; var bugGraphics = self.attachAsset(self.type + 'Bug', { anchorX: 0.5, anchorY: 0.5 }); // Boss scaling if (self.isBoss) { bugGraphics.scaleX = 1.5; bugGraphics.scaleY = 1.5; bugGraphics.tint = 0xFF4444; } // Flying effect if (self.isFlying) { bugGraphics.alpha = 0.8; var originalY = bugGraphics.y; tween(bugGraphics, { y: originalY - 10 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(bugGraphics, { y: originalY + 10 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Create infinite loop tween(bugGraphics, { y: originalY - 10 }, { duration: 1000, easing: tween.easeInOut, onFinish: arguments.callee.caller }); } }); } }); } self.takeDamage = function (damage, damageType) { var actualDamage = damage; if (self.isArmored && damageType !== 'piercing') { actualDamage = Math.max(1, damage * 0.5); } if (damageType === 'poison') { self.poisonTimer = 180; // 3 seconds at 60fps actualDamage = damage * 0.3; } self.health -= actualDamage; var healthPercent = self.health / self.maxHealth; if (damageType === 'poison') { bugGraphics.tint = 0x00FF00; } else { bugGraphics.tint = healthPercent < 0.5 ? 0xFF6666 : 0xFFFFFF; } return self.health <= 0; }; self.applySlow = function (slowAmount, duration) { if (self.slowResistance < 1) { var effectiveSlowAmount = slowAmount * (1 - self.slowResistance); self.slowEffect = Math.min(self.slowEffect, 1 - effectiveSlowAmount); LK.setTimeout(function () { self.slowEffect = 1; }, duration); } }; self.update = function () { // Handle poison damage if (self.poisonTimer > 0) { self.poisonTimer--; if (self.poisonTimer % 30 === 0) { // Damage every 0.5 seconds self.takeDamage(5, 'poison'); } if (self.poisonTimer <= 0) { bugGraphics.tint = 0xFFFFFF; } } // Check if bug is visible on screen and play appropriate sound var isOnScreen = self.x >= -50 && self.x <= 2098 && self.y >= -50 && self.y <= 2782; if (isOnScreen) { // Play bug sound based on type every 2 seconds (120 frames) if (LK.ticks % 120 === 0) { if (self.isFlying) { var flyingSound = LK.getSound('bugflying'); if (flyingSound) flyingSound.play(); } else { var crawlingSound = LK.getSound('bugcrawling'); if (crawlingSound) crawlingSound.play(); } } } // Move along path if (self.pathIndex < gamePath.length) { var target = gamePath[self.pathIndex]; var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 30) { self.pathIndex++; } else { var actualSpeed = self.speed * self.slowEffect; self.x += dx / distance * actualSpeed; self.y += dy / distance * actualSpeed; } } }; return self; }); var Bullet = Container.expand(function (bulletType, tower) { var self = Container.call(this); self.speed = 8; self.target = null; self.damage = 1; self.type = bulletType || 'basic'; self.tower = tower; self.splashRadius = 0; self.piercing = false; var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); // Customize bullet based on type switch (self.type) { case 'rapid': bulletGraphics.tint = 0x00AAFF; bulletGraphics.scaleX = 0.7; bulletGraphics.scaleY = 0.7; self.speed = 12; break; case 'sniper': bulletGraphics.tint = 0xFF4444; bulletGraphics.scaleX = 1.2; bulletGraphics.scaleY = 1.2; self.speed = 15; self.piercing = true; break; case 'cannon': bulletGraphics.tint = 0xFF8800; bulletGraphics.scaleX = 1.5; bulletGraphics.scaleY = 1.5; self.speed = 6; self.splashRadius = 100; break; case 'freeze': bulletGraphics.tint = 0x88CCFF; self.speed = 10; break; case 'poison': bulletGraphics.tint = 0x00FF00; self.speed = 9; break; } self.update = function () { if (!self.target || !self.target.parent) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.hitTarget(); self.destroy(); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.hitTarget = function () { var damageType = self.type === 'sniper' ? 'piercing' : self.type === 'poison' ? 'poison' : 'normal'; // Apply main damage if (self.target.takeDamage(self.damage, damageType)) { // Play bugdeath sound immediately when bug dies var bugDeathSound = LK.getSound('bugDeath'); if (bugDeathSound) bugDeathSound.play(); self.killBug(self.target); } // Special effects if (self.type === 'freeze') { self.target.applySlow(0.6, 2000); // 60% slow for 2 seconds } // Splash damage if (self.splashRadius > 0) { for (var i = 0; i < bugs.length; i++) { var bug = bugs[i]; if (bug !== self.target) { var dx = bug.x - self.target.x; var dy = bug.y - self.target.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.splashRadius) { if (bug.takeDamage(self.damage * 0.6, damageType)) { // Play bugdeath sound for splash damage kills too var bugDeathSound = LK.getSound('bugDeath'); if (bugDeathSound) bugDeathSound.play(); self.killBug(bug); } } } } } }; self.killBug = function (bug) { coins += bug.reward; bugsKilled++; updateUI(); for (var i = bugs.length - 1; i >= 0; i--) { if (bugs[i] === bug) { bug.destroy(); bugs.splice(i, 1); break; } } }; return self; }); var Colonist = Container.expand(function () { var self = Container.call(this); self.speed = 1; self.pathIndex = 0; self.isSafe = false; var colonistGraphics = self.attachAsset('colonist', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (self.isSafe) return; if (self.pathIndex < colonistPath.length) { var target = colonistPath[self.pathIndex]; var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.pathIndex++; if (self.pathIndex >= colonistPath.length) { self.isSafe = true; colonistsSaved++; updateUI(); } } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } } }; return self; }); var MainMenu = Container.expand(function () { var self = Container.call(this); // Main menu background var menuBg = self.attachAsset('menu', { anchorX: 0.5, anchorY: 0.5 }); menuBg.x = 1024; menuBg.y = 1366; menuBg.width = 2048; menuBg.height = 2732; menuBg.alpha = 0.7; // Title text var titleText = new Text2('COLONY DEFENSE', { size: 120, fill: 0xFFFF00 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; self.addChild(titleText); // Subtitle text var subtitleText = new Text2('Defend the colonies from alien bugs!', { size: 60, fill: 0x00FFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 1024; subtitleText.y = 950; self.addChild(subtitleText); // Start button var startButton = self.attachAsset('playbutton', { anchorX: 0.5, anchorY: 0.5 }); startButton.x = 1024; startButton.y = 1400; startButton.width = 300; startButton.height = 150; startButton.tint = 0x00AA00; var startText = new Text2('START GAME', { size: 50, fill: 0xFFFF00 }); startText.anchor.set(0.5, 0.5); startText.x = 1024; startText.y = 1400; self.addChild(startText); // Instructions text var instructText = new Text2('Use robot towers to stop the alien invasion!\nSave the colonists and defend your colonies!', { size: 40, fill: 0xFFFFFF }); instructText.anchor.set(0.5, 0.5); instructText.x = 1024; instructText.y = 1800; self.addChild(instructText); // High scores display for main menu var menuHighScores = storage.highScores || []; var scoreText = 'TOP 5 SCORES:\n'; for (var i = 0; i < Math.min(5, menuHighScores.length); i++) { scoreText += i + 1 + '. ' + menuHighScores[i] + '\n'; } // Fill empty spots for (var i = menuHighScores.length; i < 5; i++) { scoreText += i + 1 + '. ---\n'; } var menuScoreDisplay = new Text2(scoreText, { size: 45, fill: 0xFFFF00 }); menuScoreDisplay.anchor.set(0.5, 0.5); menuScoreDisplay.x = 1024; menuScoreDisplay.y = 2200; self.addChild(menuScoreDisplay); // Add pulsing animation to start button tween(startButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(startButton, { scaleX: 1, scaleY: 1 }, { duration: 1000, easing: tween.easeInOut, onFinish: arguments.callee.caller }); } }); self.down = function (x, y, obj) { // Check if clicked on start button area var dx = x - startButton.x; var dy = y - startButton.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 150) { startGame(); } }; return self; }); var Tower = Container.expand(function (towerType) { var self = Container.call(this); self.type = towerType || 'basic'; self.range = 150; self.damage = 1; self.fireRate = 30; self.lastShot = 0; self.level = 1; self.maxLevel = 5; self.upgradeCost = 20; self.sellValue = 15; // Set base stats based on tower type switch (self.type) { case 'basic': self.range = 150; self.damage = 8; self.fireRate = 40; self.upgradeCost = 20; break; case 'rapid': self.range = 120; self.damage = 4; self.fireRate = 15; self.upgradeCost = 30; break; case 'sniper': self.range = 250; self.damage = 25; self.fireRate = 80; self.upgradeCost = 50; break; case 'cannon': self.range = 140; self.damage = 20; self.fireRate = 60; self.upgradeCost = 40; break; case 'freeze': self.range = 130; self.damage = 6; self.fireRate = 35; self.upgradeCost = 35; break; case 'poison': self.range = 140; self.damage = 12; self.fireRate = 45; self.upgradeCost = 45; break; } var towerGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); // Ensure proper scaling for visibility towerGraphics.width = 80; towerGraphics.height = 80; towerGraphics.alpha = 1.0; // Move tower up 10 spaces towerGraphics.y = -10; // Set tower color based on type switch (self.type) { case 'rapid': towerGraphics.tint = 0x00AAFF; break; case 'sniper': towerGraphics.tint = 0xFF4444; break; case 'cannon': towerGraphics.tint = 0xFF8800; break; case 'freeze': towerGraphics.tint = 0x88CCFF; break; case 'poison': towerGraphics.tint = 0x00FF00; break; default: towerGraphics.tint = 0x4169E1; } self.canShoot = function () { return LK.ticks - self.lastShot >= self.fireRate; }; self.findTarget = function () { var bestTarget = null; var bestScore = -1; for (var i = 0; i < bugs.length; i++) { var bug = bugs[i]; var dx = bug.x - self.x; var dy = bug.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range) { // Prioritize based on progress along path and bug type var score = bug.pathIndex * 1000 + (bug.isBoss ? 500 : 0) - distance; if (score > bestScore) { bestScore = score; bestTarget = bug; } } } return bestTarget; }; self.shoot = function (target) { if (!self.canShoot()) return; var bullet = new Bullet(self.type, self); bullet.x = self.x; bullet.y = self.y; bullet.target = target; bullet.damage = self.damage; bullets.push(bullet); game.addChild(bullet); self.lastShot = LK.ticks; var shootSound = LK.getSound('shoot'); if (shootSound) shootSound.play(); // Visual muzzle flash towerGraphics.alpha = 1.5; tween(towerGraphics, { alpha: 1 }, { duration: 100 }); }; self.upgrade = function () { if (self.level >= self.maxLevel) return false; if (coins >= self.upgradeCost) { coins -= self.upgradeCost; self.level++; // Upgrade stats self.damage = Math.floor(self.damage * 1.3); self.range += 15; if (self.type === 'rapid') { self.fireRate = Math.max(8, self.fireRate - 3); } else { self.fireRate = Math.max(15, self.fireRate - 5); } self.sellValue += Math.floor(self.upgradeCost * 0.7); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); towerGraphics.tint = 0x00FF00; tween(towerGraphics, { tint: self.type === 'rapid' ? 0x00AAFF : self.type === 'sniper' ? 0xFF4444 : self.type === 'cannon' ? 0xFF8800 : self.type === 'freeze' ? 0x88CCFF : self.type === 'poison' ? 0x00FF00 : 0x4169E1 }, { duration: 500 }); updateUI(); return true; } return false; }; self.sell = function () { coins += self.sellValue; selectedTower = null; for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === self) { towers.splice(i, 1); break; } } self.destroy(); updateUI(); }; self.update = function () { var target = self.findTarget(); if (target) { self.shoot(target); } }; self.down = function (x, y, obj) { selectedTower = self; showTowerInfo(); }; return self; }); var TowerButton = Container.expand(function (towerType, cost) { var self = Container.call(this); self.towerType = towerType; self.cost = cost; var buttonBg = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); buttonBg.width = 120; buttonBg.height = 120; // Set button color based on tower type switch (towerType) { case 'rapid': buttonBg.tint = 0x00AAFF; break; case 'sniper': buttonBg.tint = 0xFF4444; break; case 'cannon': buttonBg.tint = 0xFF8800; break; case 'freeze': buttonBg.tint = 0x88CCFF; break; case 'poison': buttonBg.tint = 0x00FF00; break; default: buttonBg.tint = 0x4169E1; } var costText = new Text2('$' + cost, { size: 30, fill: 0xFFFF00 }); costText.anchor.set(0.5, 0.5); costText.y = 50; self.addChild(costText); var typeText = new Text2(towerType.toUpperCase(), { size: 25, fill: 0xFFFFFF }); typeText.anchor.set(0.5, 0.5); typeText.y = -50; self.addChild(typeText); self.update = function () { var canAfford = coins >= self.cost; self.alpha = canAfford ? 1 : 0.5; }; self.down = function (x, y, obj) { if (coins >= self.cost) { placingTowerType = self.towerType; selectedTower = null; updateUI(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F4F2F }); /**** * Game Code ****/ var bugs = []; var towers = []; var bullets = []; var colonists = []; var coins = 80; var wave = 1; var maxWaves = 500; // 250 levels × 2 waves per level = 500 total waves var bugsKilled = 0; var colonistsSaved = 0; var selectedTower = null; var placingTowerType = null; var waveTimer = 0; var waveDelay = 300; var bugsToSpawn = 5; var bugsSpawned = 0; var gameWon = false; var towerButtons = []; var gameState = 'menu'; // 'menu' or 'playing' var mainMenu = null; var gameElements = []; // Tower costs var towerCosts = { basic: 15, rapid: 25, sniper: 40, cannon: 35, freeze: 30, poison: 45 }; // High score management var highScores = storage.highScores || []; var scoreDisplay = null; function calculateScore() { return bugsKilled * 10 + colonistsSaved * 50 + wave * 100; } function saveHighScore(score) { // Add current score highScores.push(score); // Sort in descending order highScores.sort(function (a, b) { return b - a; }); // Keep only top 5 if (highScores.length > 5) { highScores = highScores.slice(0, 5); } // Save to storage storage.highScores = highScores; updateScoreDisplay(); } function updateScoreDisplay() { if (!scoreDisplay) return; var scoreText = 'TOP 5 SCORES:\n'; for (var i = 0; i < Math.min(5, highScores.length); i++) { scoreText += i + 1 + '. ' + highScores[i] + '\n'; } // Fill empty spots for (var i = highScores.length; i < 5; i++) { scoreText += i + 1 + '. ---\n'; } scoreDisplay.setText(scoreText); } function createScoreDisplay() { scoreDisplay = new Text2('TOP 5 SCORES:\n1. ---\n2. ---\n3. ---\n4. ---\n5. ---', { size: 40, fill: 0xFFFF00 }); scoreDisplay.anchor.set(0.5, 1); scoreDisplay.x = 0; scoreDisplay.y = -20; scoreDisplay.visible = false; LK.gui.bottom.addChild(scoreDisplay); updateScoreDisplay(); } // Game path for bugs var gamePath = [{ x: 0, y: 1366 }, { x: 400, y: 1366 }, { x: 400, y: 800 }, { x: 800, y: 800 }, { x: 800, y: 1366 }, { x: 1200, y: 1366 }, { x: 1200, y: 600 }, { x: 1600, y: 600 }, { x: 1600, y: 1000 }, { x: 2048, y: 1000 }]; // Colonist path to bunker - updated to connect colonies better var colonistPath = [{ x: 500, // Start near colony1 y: 700 }, { x: 900, y: 900 }, { x: 1200, y: 900 }, { x: 1400, y: 800 }, { x: 1600, y: 1000 }, { x: 1800, y: 1400 }]; // Create colonies array and bunker variable var colonies = []; var bunker = null; // Initialize main menu mainMenu = new MainMenu(); game.addChild(mainMenu); // UI Elements var coinText = new Text2('Coins: 50', { size: 60, fill: 0xFFFF00 }); coinText.anchor.set(0, 0); coinText.visible = false; LK.gui.topRight.addChild(coinText); var waveText = new Text2('Wave: 1', { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); waveText.visible = false; LK.gui.top.addChild(waveText); var infoText = new Text2('Tap empty space to place tower ($15)', { size: 40, fill: 0x00FFFF }); infoText.anchor.set(0.5, 1); infoText.visible = false; LK.gui.bottom.addChild(infoText); var killsText = new Text2('Bugs Killed: 0', { size: 50, fill: 0xFF0000 }); killsText.anchor.set(0, 0); killsText.visible = false; LK.gui.left.addChild(killsText); var savedText = new Text2('Colonists Saved: 0', { size: 50, fill: 0x00FF00 }); savedText.anchor.set(0, 0); savedText.y = 80; savedText.visible = false; LK.gui.left.addChild(savedText); // Create score display createScoreDisplay(); // Create tower selection buttons var towerTypes = ['basic', 'rapid', 'sniper', 'cannon', 'freeze', 'poison']; var buttonSpacing = 140; var startX = 100; for (var i = 0; i < towerTypes.length; i++) { var towerButton = new TowerButton(towerTypes[i], towerCosts[towerTypes[i]]); towerButton.x = startX + i * buttonSpacing; towerButton.y = 2600; towerButton.visible = false; game.addChild(towerButton); towerButtons.push(towerButton); } function startGame() { if (gameState === 'playing') return; gameState = 'playing'; // Hide main menu if (mainMenu) { mainMenu.destroy(); mainMenu = null; } // Reset game variables bugs = []; towers = []; bullets = []; colonists = []; colonies = []; coins = 80; wave = 1; bugsKilled = 0; colonistsSaved = 0; selectedTower = null; placingTowerType = null; waveTimer = 0; bugsToSpawn = 5; bugsSpawned = 0; gameWon = false; towerButtons = []; // Initialize game elements initializeGameElements(); // Show UI elements coinText.visible = true; waveText.visible = true; infoText.visible = true; killsText.visible = true; savedText.visible = true; // Keep score display hidden during gameplay if (scoreDisplay) scoreDisplay.visible = false; for (var i = 0; i < towerButtons.length; i++) { towerButtons[i].visible = true; } // Ensure we start at level 1 and begin first wave wave = 0; // Start at 0 so first call to startNewWave makes it 1 waveText.setText('Level: 1 | Wave: 1 | Colonies: ' + colonies.length); LK.setTimeout(function () { startNewWave(); }, 3000); } function initializeGameElements() { // Add background image var backgroundImage = game.addChild(LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5 })); backgroundImage.x = 1024; backgroundImage.y = 1366; backgroundImage.width = 2048; backgroundImage.height = 2732; gameElements.push(backgroundImage); // Create initial colony var colony1 = game.addChild(LK.getAsset('colony', { anchorX: 0.5, anchorY: 0.5 })); colony1.x = 400; colony1.y = 600; colonies.push(colony1); gameElements.push(colony1); // Create bunker bunker = game.addChild(LK.getAsset('bunker', { anchorX: 0.5, anchorY: 0.5 })); bunker.x = 1800; bunker.y = 1400; gameElements.push(bunker); // Show initial instructions var instructionText = new Text2('Defend the colonies from alien bugs!\nUse robot towers to stop them!', { size: 60, fill: 0x00FFFF }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 600; game.addChild(instructionText); gameElements.push(instructionText); tween(instructionText, { alpha: 0 }, { duration: 4000, onFinish: function onFinish() { instructionText.destroy(); } }); // Play background music var bgMusic = LK.getMusic ? LK.getMusic('gameMusic') : null; if (bgMusic || LK.playMusic) { LK.playMusic('gameMusic'); } } function updateUI() { if (gameState !== 'playing') return; coinText.setText('Coins: ' + coins); killsText.setText('Bugs Killed: ' + bugsKilled); savedText.setText('Colonists Saved: ' + colonistsSaved); if (selectedTower) { var upgradeText = selectedTower.level >= selectedTower.maxLevel ? 'MAX LEVEL' : 'Upgrade ($' + selectedTower.upgradeCost + ')'; infoText.setText('Tower Lv.' + selectedTower.level + ' - ' + upgradeText + ' | Sell ($' + selectedTower.sellValue + ')'); } else if (placingTowerType) { infoText.setText('Placing ' + placingTowerType.toUpperCase() + ' tower ($' + towerCosts[placingTowerType] + ') - Tap location'); } else { infoText.setText('Select a tower type below to place'); } } function showTowerInfo() { updateUI(); } function spawnBug() { if (bugsSpawned >= bugsToSpawn) return; var bugType; // Random chance for queenbug (boss) to appear in any wave - no set limit if (Math.random() < 0.15) { // 15% chance for queenbug to spawn bugType = 'boss'; } else if (wave >= 15) { var advancedTypes = ['medium', 'large', 'fast', 'armored', 'flying']; bugType = advancedTypes[Math.floor(Math.random() * advancedTypes.length)]; } else if (wave >= 10) { var midTypes = ['small', 'medium', 'fast', 'flying']; bugType = midTypes[Math.floor(Math.random() * midTypes.length)]; } else if (wave >= 5) { var earlyTypes = ['small', 'medium', 'fast']; bugType = earlyTypes[Math.floor(Math.random() * earlyTypes.length)]; } else { var basicTypes = ['small', 'small', 'medium']; bugType = basicTypes[Math.floor(Math.random() * basicTypes.length)]; } var bug = new Bug(bugType, wave); bug.x = gamePath[0].x; bug.y = gamePath[0].y; bugs.push(bug); game.addChild(bug); bugsSpawned++; // Play bug crawling sound when bug spawns var crawlingSound = LK.getSound('bugcrawling'); if (crawlingSound) crawlingSound.play(); } function spawnNewColony() { // Only spawn if we have fewer than 8 colonies total if (colonies.length >= 8) return; // Generate random position for new colony in safe areas var attempts = 0; var newColony = null; while (attempts < 50 && !newColony) { var x = 300 + Math.random() * 1400; // Random X between 300-1700 var y = 500 + Math.random() * 800; // Random Y between 500-1300 var canPlace = true; attempts++; // Ensure we're not too close to screen edges if (x < 200 || x > 1848 || y < 200 || y > 2532) { canPlace = false; continue; } // Check distance from path for (var i = 0; i < gamePath.length; i++) { var dx = x - gamePath[i].x; var dy = y - gamePath[i].y; if (Math.sqrt(dx * dx + dy * dy) < 150) { canPlace = false; break; } } if (!canPlace) continue; // Check distance from existing colonies and bunker var structures = colonies.slice(); structures.push(bunker); for (var i = 0; i < structures.length; i++) { var dx = x - structures[i].x; var dy = y - structures[i].y; if (Math.sqrt(dx * dx + dy * dy) < 200) { canPlace = false; break; } } if (!canPlace) continue; // Check distance from existing towers for (var i = 0; i < towers.length; i++) { var dx = x - towers[i].x; var dy = y - towers[i].y; if (Math.sqrt(dx * dx + dy * dy) < 150) { canPlace = false; break; } } if (canPlace) { newColony = game.addChild(LK.getAsset('colony', { anchorX: 0.5, anchorY: 0.5 })); newColony.x = x; newColony.y = y; colonies.push(newColony); gameElements.push(newColony); // Flash effect for new colony LK.effects.flashObject(newColony, 0x00FF00, 1000); break; } } } function startNewWave() { // Only increment wave if this isn't the initial call if (wave === 1 && bugsSpawned > 0) { wave++; } else if (wave > 1) { wave++; } // Calculate current level (1-250) based on wave progression // Level 1: waves 1-2, Level 2: waves 3-4, Level 3: waves 5-6, etc. var currentLevel = Math.ceil(wave / 2); var expectedColonies = currentLevel; // Add colonies to match the current level (1 colony per level) while (colonies.length < expectedColonies && colonies.length < 8) { spawnNewColony(); } bugsToSpawn = Math.min(15, 3 + wave * 2); bugsSpawned = 0; waveTimer = 0; waveText.setText('Level: ' + currentLevel + ' | Wave: ' + wave + ' | Colonies: ' + colonies.length); // Spawn fleeing colonists and male colonists with dramatic animations if (wave <= 15 && Math.random() < 0.7) { // Spawn regular colonists fleeing from random colonies var colonistsToSpawn = Math.min(colonies.length, Math.ceil(wave / 2)); for (var i = 0; i < colonistsToSpawn; i++) { var randomColony = colonies[Math.floor(Math.random() * colonies.length)]; var colonist = new Colonist(); colonist.x = randomColony.x + (Math.random() - 0.5) * 150; colonist.y = randomColony.y + (Math.random() - 0.5) * 150; colonists.push(colonist); game.addChild(colonist); // Add fleeing animation - quick movement toward first waypoint tween(colonist, { x: colonistPath[0].x + (Math.random() - 0.5) * 50, y: colonistPath[0].y + (Math.random() - 0.5) * 50 }, { duration: 1000 + Math.random() * 1000, easing: tween.easeOut }); } // Spawn male colonists fleeing from random colonies if (Math.random() < 0.5 && colonies.length > 1) { var randomColony = colonies[Math.floor(Math.random() * colonies.length)]; var maleColonist = game.addChild(LK.getAsset('colonistmale', { anchorX: 0.5, anchorY: 0.5 })); maleColonist.x = randomColony.x + (Math.random() - 0.5) * 150; maleColonist.y = randomColony.y + (Math.random() - 0.5) * 150; maleColonist.speed = 1.2; maleColonist.pathIndex = 0; maleColonist.isSafe = false; maleColonist.isMale = true; // Add to colonists array for tracking colonists.push(maleColonist); // Add panic animation - shake then flee tween(maleColonist, { x: maleColonist.x + 20 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { tween(maleColonist, { x: maleColonist.x - 40 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { tween(maleColonist, { x: colonistPath[Math.floor(colonistPath.length / 2)].x, y: colonistPath[Math.floor(colonistPath.length / 2)].y }, { duration: 2000 + Math.random() * 1500, easing: tween.easeOut }); } }); } }); // Add custom update method for male colonist maleColonist.update = function () { if (maleColonist.isSafe) return; if (maleColonist.pathIndex < colonistPath.length) { var target = colonistPath[maleColonist.pathIndex]; var dx = target.x - maleColonist.x; var dy = target.y - maleColonist.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { maleColonist.pathIndex++; if (maleColonist.pathIndex >= colonistPath.length) { maleColonist.isSafe = true; colonistsSaved++; updateUI(); } } else { maleColonist.x += dx / distance * maleColonist.speed; maleColonist.y += dy / distance * maleColonist.speed; } } }; } } } game.down = function (x, y, obj) { if (gameState !== 'playing') return; // Handle tower upgrade/sell if tower is selected if (selectedTower && x > 1500 && y > 2400) { // Clicked in UI area if (y > 2500) { // Sell button area selectedTower.sell(); } else { // Upgrade button area selectedTower.upgrade(); } updateUI(); return; } // Handle tower placement if (placingTowerType && y < 2400) { var canPlace = true; var cost = towerCosts[placingTowerType]; // Check if we have enough coins if (coins < cost) { canPlace = false; } // Check distance from path for (var i = 0; i < gamePath.length; i++) { var dx = x - gamePath[i].x; var dy = y - gamePath[i].y; if (Math.sqrt(dx * dx + dy * dy) < 80) { canPlace = false; break; } } // Check distance from existing towers for (var i = 0; i < towers.length; i++) { var dx = x - towers[i].x; var dy = y - towers[i].y; if (Math.sqrt(dx * dx + dy * dy) < 100) { canPlace = false; break; } } // Check distance from colonies and bunker var structures = colonies.slice(); // Copy colonies array structures.push(bunker); for (var i = 0; i < structures.length; i++) { var dx = x - structures[i].x; var dy = y - structures[i].y; if (Math.sqrt(dx * dx + dy * dy) < 120) { canPlace = false; break; } } if (canPlace) { var tower = new Tower(placingTowerType); tower.x = x; tower.y = y; towers.push(tower); game.addChild(tower); coins -= cost; placingTowerType = null; updateUI(); } } else { // Clear selection selectedTower = null; placingTowerType = null; } updateUI(); }; game.update = function () { if (gameState !== 'playing') return; if (gameWon) return; // Spawn bugs waveTimer++; if (waveTimer % 45 === 0 && bugsSpawned < bugsToSpawn) { spawnBug(); } // Check if wave is complete if (bugsSpawned >= bugsToSpawn && bugs.length === 0) { var currentLevel = Math.ceil(wave / 2); if (currentLevel >= 250) { gameWon = true; var finalScore = calculateScore(); saveHighScore(finalScore); LK.setTimeout(function () { LK.showYouWin(); }, 1000); } else { // Properly advance to next wave/level LK.setTimeout(function () { startNewWave(); }, 2000); } } // Clean up bullets for (var i = bullets.length - 1; i >= 0; i--) { if (!bullets[i].parent) { bullets.splice(i, 1); } } // Update colonists that have update methods for (var i = 0; i < colonists.length; i++) { var colonist = colonists[i]; if (colonist.update && typeof colonist.update === 'function') { colonist.update(); } } // Check for bug-colonist intersections for (var i = bugs.length - 1; i >= 0; i--) { var bug = bugs[i]; for (var j = colonists.length - 1; j >= 0; j--) { var colonist = colonists[j]; if (!colonist.isSafe && colonist.intersects && colonist.intersects(bug)) { // Play colonist scream sound when killed by bug var screamSound = LK.getSound('colonistscream'); if (screamSound) screamSound.play(); // Remove the colonist colonist.destroy(); colonists.splice(j, 1); break; // One bug can only kill one colonist per frame } } } // Check for bugs reaching the end var bugsReachedEnd = 0; for (var i = bugs.length - 1; i >= 0; i--) { var bug = bugs[i]; if (bug.pathIndex >= gamePath.length) { bug.destroy(); bugs.splice(i, 1); bugsReachedEnd++; if (bug.isBoss) { bugsReachedEnd += 4; // Boss counts as 5 bugs reaching end } } } // Check lose condition if (bugsReachedEnd >= 3) { var finalScore = calculateScore(); saveHighScore(finalScore); LK.showGameOver(); } // Update UI periodically if (LK.ticks % 60 === 0) { updateUI(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bug = Container.expand(function (type, wave) {
var self = Container.call(this);
self.type = type || 'small';
self.wave = wave || 1;
self.isBoss = false;
self.isFlying = false;
self.isArmored = false;
self.slowResistance = 0;
self.pathIndex = 0;
// Set base stats based on type
switch (self.type) {
case 'small':
self.health = 15;
self.speed = 2.5;
self.reward = 5;
break;
case 'medium':
self.health = 35;
self.speed = 2;
self.reward = 7;
break;
case 'large':
self.health = 80;
self.speed = 1.5;
self.reward = 10;
break;
case 'fast':
self.health = 20;
self.speed = 4;
self.reward = 8;
break;
case 'armored':
self.health = 120;
self.speed = 1;
self.reward = 15;
self.isArmored = true;
break;
case 'flying':
self.health = 40;
self.speed = 3;
self.reward = 12;
self.isFlying = true;
break;
case 'boss':
self.health = 500;
self.speed = 0.8;
self.reward = 50;
self.isBoss = true;
self.slowResistance = 0.7;
break;
}
// Scale with wave
var waveMultiplier = 1 + (self.wave - 1) * 0.15;
self.health = Math.floor(self.health * waveMultiplier);
self.maxHealth = self.health;
self.slowEffect = 1;
self.poisonTimer = 0;
var bugGraphics = self.attachAsset(self.type + 'Bug', {
anchorX: 0.5,
anchorY: 0.5
});
// Boss scaling
if (self.isBoss) {
bugGraphics.scaleX = 1.5;
bugGraphics.scaleY = 1.5;
bugGraphics.tint = 0xFF4444;
}
// Flying effect
if (self.isFlying) {
bugGraphics.alpha = 0.8;
var originalY = bugGraphics.y;
tween(bugGraphics, {
y: originalY - 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bugGraphics, {
y: originalY + 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Create infinite loop
tween(bugGraphics, {
y: originalY - 10
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
});
}
});
}
self.takeDamage = function (damage, damageType) {
var actualDamage = damage;
if (self.isArmored && damageType !== 'piercing') {
actualDamage = Math.max(1, damage * 0.5);
}
if (damageType === 'poison') {
self.poisonTimer = 180; // 3 seconds at 60fps
actualDamage = damage * 0.3;
}
self.health -= actualDamage;
var healthPercent = self.health / self.maxHealth;
if (damageType === 'poison') {
bugGraphics.tint = 0x00FF00;
} else {
bugGraphics.tint = healthPercent < 0.5 ? 0xFF6666 : 0xFFFFFF;
}
return self.health <= 0;
};
self.applySlow = function (slowAmount, duration) {
if (self.slowResistance < 1) {
var effectiveSlowAmount = slowAmount * (1 - self.slowResistance);
self.slowEffect = Math.min(self.slowEffect, 1 - effectiveSlowAmount);
LK.setTimeout(function () {
self.slowEffect = 1;
}, duration);
}
};
self.update = function () {
// Handle poison damage
if (self.poisonTimer > 0) {
self.poisonTimer--;
if (self.poisonTimer % 30 === 0) {
// Damage every 0.5 seconds
self.takeDamage(5, 'poison');
}
if (self.poisonTimer <= 0) {
bugGraphics.tint = 0xFFFFFF;
}
}
// Check if bug is visible on screen and play appropriate sound
var isOnScreen = self.x >= -50 && self.x <= 2098 && self.y >= -50 && self.y <= 2782;
if (isOnScreen) {
// Play bug sound based on type every 2 seconds (120 frames)
if (LK.ticks % 120 === 0) {
if (self.isFlying) {
var flyingSound = LK.getSound('bugflying');
if (flyingSound) flyingSound.play();
} else {
var crawlingSound = LK.getSound('bugcrawling');
if (crawlingSound) crawlingSound.play();
}
}
}
// Move along path
if (self.pathIndex < gamePath.length) {
var target = gamePath[self.pathIndex];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 30) {
self.pathIndex++;
} else {
var actualSpeed = self.speed * self.slowEffect;
self.x += dx / distance * actualSpeed;
self.y += dy / distance * actualSpeed;
}
}
};
return self;
});
var Bullet = Container.expand(function (bulletType, tower) {
var self = Container.call(this);
self.speed = 8;
self.target = null;
self.damage = 1;
self.type = bulletType || 'basic';
self.tower = tower;
self.splashRadius = 0;
self.piercing = false;
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Customize bullet based on type
switch (self.type) {
case 'rapid':
bulletGraphics.tint = 0x00AAFF;
bulletGraphics.scaleX = 0.7;
bulletGraphics.scaleY = 0.7;
self.speed = 12;
break;
case 'sniper':
bulletGraphics.tint = 0xFF4444;
bulletGraphics.scaleX = 1.2;
bulletGraphics.scaleY = 1.2;
self.speed = 15;
self.piercing = true;
break;
case 'cannon':
bulletGraphics.tint = 0xFF8800;
bulletGraphics.scaleX = 1.5;
bulletGraphics.scaleY = 1.5;
self.speed = 6;
self.splashRadius = 100;
break;
case 'freeze':
bulletGraphics.tint = 0x88CCFF;
self.speed = 10;
break;
case 'poison':
bulletGraphics.tint = 0x00FF00;
self.speed = 9;
break;
}
self.update = function () {
if (!self.target || !self.target.parent) {
self.destroy();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.hitTarget();
self.destroy();
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.hitTarget = function () {
var damageType = self.type === 'sniper' ? 'piercing' : self.type === 'poison' ? 'poison' : 'normal';
// Apply main damage
if (self.target.takeDamage(self.damage, damageType)) {
// Play bugdeath sound immediately when bug dies
var bugDeathSound = LK.getSound('bugDeath');
if (bugDeathSound) bugDeathSound.play();
self.killBug(self.target);
}
// Special effects
if (self.type === 'freeze') {
self.target.applySlow(0.6, 2000); // 60% slow for 2 seconds
}
// Splash damage
if (self.splashRadius > 0) {
for (var i = 0; i < bugs.length; i++) {
var bug = bugs[i];
if (bug !== self.target) {
var dx = bug.x - self.target.x;
var dy = bug.y - self.target.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist <= self.splashRadius) {
if (bug.takeDamage(self.damage * 0.6, damageType)) {
// Play bugdeath sound for splash damage kills too
var bugDeathSound = LK.getSound('bugDeath');
if (bugDeathSound) bugDeathSound.play();
self.killBug(bug);
}
}
}
}
}
};
self.killBug = function (bug) {
coins += bug.reward;
bugsKilled++;
updateUI();
for (var i = bugs.length - 1; i >= 0; i--) {
if (bugs[i] === bug) {
bug.destroy();
bugs.splice(i, 1);
break;
}
}
};
return self;
});
var Colonist = Container.expand(function () {
var self = Container.call(this);
self.speed = 1;
self.pathIndex = 0;
self.isSafe = false;
var colonistGraphics = self.attachAsset('colonist', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
if (self.isSafe) return;
if (self.pathIndex < colonistPath.length) {
var target = colonistPath[self.pathIndex];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
self.pathIndex++;
if (self.pathIndex >= colonistPath.length) {
self.isSafe = true;
colonistsSaved++;
updateUI();
}
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
};
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Main menu background
var menuBg = self.attachAsset('menu', {
anchorX: 0.5,
anchorY: 0.5
});
menuBg.x = 1024;
menuBg.y = 1366;
menuBg.width = 2048;
menuBg.height = 2732;
menuBg.alpha = 0.7;
// Title text
var titleText = new Text2('COLONY DEFENSE', {
size: 120,
fill: 0xFFFF00
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Subtitle text
var subtitleText = new Text2('Defend the colonies from alien bugs!', {
size: 60,
fill: 0x00FFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 950;
self.addChild(subtitleText);
// Start button
var startButton = self.attachAsset('playbutton', {
anchorX: 0.5,
anchorY: 0.5
});
startButton.x = 1024;
startButton.y = 1400;
startButton.width = 300;
startButton.height = 150;
startButton.tint = 0x00AA00;
var startText = new Text2('START GAME', {
size: 50,
fill: 0xFFFF00
});
startText.anchor.set(0.5, 0.5);
startText.x = 1024;
startText.y = 1400;
self.addChild(startText);
// Instructions text
var instructText = new Text2('Use robot towers to stop the alien invasion!\nSave the colonists and defend your colonies!', {
size: 40,
fill: 0xFFFFFF
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1800;
self.addChild(instructText);
// High scores display for main menu
var menuHighScores = storage.highScores || [];
var scoreText = 'TOP 5 SCORES:\n';
for (var i = 0; i < Math.min(5, menuHighScores.length); i++) {
scoreText += i + 1 + '. ' + menuHighScores[i] + '\n';
}
// Fill empty spots
for (var i = menuHighScores.length; i < 5; i++) {
scoreText += i + 1 + '. ---\n';
}
var menuScoreDisplay = new Text2(scoreText, {
size: 45,
fill: 0xFFFF00
});
menuScoreDisplay.anchor.set(0.5, 0.5);
menuScoreDisplay.x = 1024;
menuScoreDisplay.y = 2200;
self.addChild(menuScoreDisplay);
// Add pulsing animation to start button
tween(startButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 1,
scaleY: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: arguments.callee.caller
});
}
});
self.down = function (x, y, obj) {
// Check if clicked on start button area
var dx = x - startButton.x;
var dy = y - startButton.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 150) {
startGame();
}
};
return self;
});
var Tower = Container.expand(function (towerType) {
var self = Container.call(this);
self.type = towerType || 'basic';
self.range = 150;
self.damage = 1;
self.fireRate = 30;
self.lastShot = 0;
self.level = 1;
self.maxLevel = 5;
self.upgradeCost = 20;
self.sellValue = 15;
// Set base stats based on tower type
switch (self.type) {
case 'basic':
self.range = 150;
self.damage = 8;
self.fireRate = 40;
self.upgradeCost = 20;
break;
case 'rapid':
self.range = 120;
self.damage = 4;
self.fireRate = 15;
self.upgradeCost = 30;
break;
case 'sniper':
self.range = 250;
self.damage = 25;
self.fireRate = 80;
self.upgradeCost = 50;
break;
case 'cannon':
self.range = 140;
self.damage = 20;
self.fireRate = 60;
self.upgradeCost = 40;
break;
case 'freeze':
self.range = 130;
self.damage = 6;
self.fireRate = 35;
self.upgradeCost = 35;
break;
case 'poison':
self.range = 140;
self.damage = 12;
self.fireRate = 45;
self.upgradeCost = 45;
break;
}
var towerGraphics = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.5
});
// Ensure proper scaling for visibility
towerGraphics.width = 80;
towerGraphics.height = 80;
towerGraphics.alpha = 1.0;
// Move tower up 10 spaces
towerGraphics.y = -10;
// Set tower color based on type
switch (self.type) {
case 'rapid':
towerGraphics.tint = 0x00AAFF;
break;
case 'sniper':
towerGraphics.tint = 0xFF4444;
break;
case 'cannon':
towerGraphics.tint = 0xFF8800;
break;
case 'freeze':
towerGraphics.tint = 0x88CCFF;
break;
case 'poison':
towerGraphics.tint = 0x00FF00;
break;
default:
towerGraphics.tint = 0x4169E1;
}
self.canShoot = function () {
return LK.ticks - self.lastShot >= self.fireRate;
};
self.findTarget = function () {
var bestTarget = null;
var bestScore = -1;
for (var i = 0; i < bugs.length; i++) {
var bug = bugs[i];
var dx = bug.x - self.x;
var dy = bug.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
// Prioritize based on progress along path and bug type
var score = bug.pathIndex * 1000 + (bug.isBoss ? 500 : 0) - distance;
if (score > bestScore) {
bestScore = score;
bestTarget = bug;
}
}
}
return bestTarget;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
var bullet = new Bullet(self.type, self);
bullet.x = self.x;
bullet.y = self.y;
bullet.target = target;
bullet.damage = self.damage;
bullets.push(bullet);
game.addChild(bullet);
self.lastShot = LK.ticks;
var shootSound = LK.getSound('shoot');
if (shootSound) shootSound.play();
// Visual muzzle flash
towerGraphics.alpha = 1.5;
tween(towerGraphics, {
alpha: 1
}, {
duration: 100
});
};
self.upgrade = function () {
if (self.level >= self.maxLevel) return false;
if (coins >= self.upgradeCost) {
coins -= self.upgradeCost;
self.level++;
// Upgrade stats
self.damage = Math.floor(self.damage * 1.3);
self.range += 15;
if (self.type === 'rapid') {
self.fireRate = Math.max(8, self.fireRate - 3);
} else {
self.fireRate = Math.max(15, self.fireRate - 5);
}
self.sellValue += Math.floor(self.upgradeCost * 0.7);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
towerGraphics.tint = 0x00FF00;
tween(towerGraphics, {
tint: self.type === 'rapid' ? 0x00AAFF : self.type === 'sniper' ? 0xFF4444 : self.type === 'cannon' ? 0xFF8800 : self.type === 'freeze' ? 0x88CCFF : self.type === 'poison' ? 0x00FF00 : 0x4169E1
}, {
duration: 500
});
updateUI();
return true;
}
return false;
};
self.sell = function () {
coins += self.sellValue;
selectedTower = null;
for (var i = towers.length - 1; i >= 0; i--) {
if (towers[i] === self) {
towers.splice(i, 1);
break;
}
}
self.destroy();
updateUI();
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
self.down = function (x, y, obj) {
selectedTower = self;
showTowerInfo();
};
return self;
});
var TowerButton = Container.expand(function (towerType, cost) {
var self = Container.call(this);
self.towerType = towerType;
self.cost = cost;
var buttonBg = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.5
});
buttonBg.width = 120;
buttonBg.height = 120;
// Set button color based on tower type
switch (towerType) {
case 'rapid':
buttonBg.tint = 0x00AAFF;
break;
case 'sniper':
buttonBg.tint = 0xFF4444;
break;
case 'cannon':
buttonBg.tint = 0xFF8800;
break;
case 'freeze':
buttonBg.tint = 0x88CCFF;
break;
case 'poison':
buttonBg.tint = 0x00FF00;
break;
default:
buttonBg.tint = 0x4169E1;
}
var costText = new Text2('$' + cost, {
size: 30,
fill: 0xFFFF00
});
costText.anchor.set(0.5, 0.5);
costText.y = 50;
self.addChild(costText);
var typeText = new Text2(towerType.toUpperCase(), {
size: 25,
fill: 0xFFFFFF
});
typeText.anchor.set(0.5, 0.5);
typeText.y = -50;
self.addChild(typeText);
self.update = function () {
var canAfford = coins >= self.cost;
self.alpha = canAfford ? 1 : 0.5;
};
self.down = function (x, y, obj) {
if (coins >= self.cost) {
placingTowerType = self.towerType;
selectedTower = null;
updateUI();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F2F
});
/****
* Game Code
****/
var bugs = [];
var towers = [];
var bullets = [];
var colonists = [];
var coins = 80;
var wave = 1;
var maxWaves = 500; // 250 levels × 2 waves per level = 500 total waves
var bugsKilled = 0;
var colonistsSaved = 0;
var selectedTower = null;
var placingTowerType = null;
var waveTimer = 0;
var waveDelay = 300;
var bugsToSpawn = 5;
var bugsSpawned = 0;
var gameWon = false;
var towerButtons = [];
var gameState = 'menu'; // 'menu' or 'playing'
var mainMenu = null;
var gameElements = [];
// Tower costs
var towerCosts = {
basic: 15,
rapid: 25,
sniper: 40,
cannon: 35,
freeze: 30,
poison: 45
};
// High score management
var highScores = storage.highScores || [];
var scoreDisplay = null;
function calculateScore() {
return bugsKilled * 10 + colonistsSaved * 50 + wave * 100;
}
function saveHighScore(score) {
// Add current score
highScores.push(score);
// Sort in descending order
highScores.sort(function (a, b) {
return b - a;
});
// Keep only top 5
if (highScores.length > 5) {
highScores = highScores.slice(0, 5);
}
// Save to storage
storage.highScores = highScores;
updateScoreDisplay();
}
function updateScoreDisplay() {
if (!scoreDisplay) return;
var scoreText = 'TOP 5 SCORES:\n';
for (var i = 0; i < Math.min(5, highScores.length); i++) {
scoreText += i + 1 + '. ' + highScores[i] + '\n';
}
// Fill empty spots
for (var i = highScores.length; i < 5; i++) {
scoreText += i + 1 + '. ---\n';
}
scoreDisplay.setText(scoreText);
}
function createScoreDisplay() {
scoreDisplay = new Text2('TOP 5 SCORES:\n1. ---\n2. ---\n3. ---\n4. ---\n5. ---', {
size: 40,
fill: 0xFFFF00
});
scoreDisplay.anchor.set(0.5, 1);
scoreDisplay.x = 0;
scoreDisplay.y = -20;
scoreDisplay.visible = false;
LK.gui.bottom.addChild(scoreDisplay);
updateScoreDisplay();
}
// Game path for bugs
var gamePath = [{
x: 0,
y: 1366
}, {
x: 400,
y: 1366
}, {
x: 400,
y: 800
}, {
x: 800,
y: 800
}, {
x: 800,
y: 1366
}, {
x: 1200,
y: 1366
}, {
x: 1200,
y: 600
}, {
x: 1600,
y: 600
}, {
x: 1600,
y: 1000
}, {
x: 2048,
y: 1000
}];
// Colonist path to bunker - updated to connect colonies better
var colonistPath = [{
x: 500,
// Start near colony1
y: 700
}, {
x: 900,
y: 900
}, {
x: 1200,
y: 900
}, {
x: 1400,
y: 800
}, {
x: 1600,
y: 1000
}, {
x: 1800,
y: 1400
}];
// Create colonies array and bunker variable
var colonies = [];
var bunker = null;
// Initialize main menu
mainMenu = new MainMenu();
game.addChild(mainMenu);
// UI Elements
var coinText = new Text2('Coins: 50', {
size: 60,
fill: 0xFFFF00
});
coinText.anchor.set(0, 0);
coinText.visible = false;
LK.gui.topRight.addChild(coinText);
var waveText = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
waveText.visible = false;
LK.gui.top.addChild(waveText);
var infoText = new Text2('Tap empty space to place tower ($15)', {
size: 40,
fill: 0x00FFFF
});
infoText.anchor.set(0.5, 1);
infoText.visible = false;
LK.gui.bottom.addChild(infoText);
var killsText = new Text2('Bugs Killed: 0', {
size: 50,
fill: 0xFF0000
});
killsText.anchor.set(0, 0);
killsText.visible = false;
LK.gui.left.addChild(killsText);
var savedText = new Text2('Colonists Saved: 0', {
size: 50,
fill: 0x00FF00
});
savedText.anchor.set(0, 0);
savedText.y = 80;
savedText.visible = false;
LK.gui.left.addChild(savedText);
// Create score display
createScoreDisplay();
// Create tower selection buttons
var towerTypes = ['basic', 'rapid', 'sniper', 'cannon', 'freeze', 'poison'];
var buttonSpacing = 140;
var startX = 100;
for (var i = 0; i < towerTypes.length; i++) {
var towerButton = new TowerButton(towerTypes[i], towerCosts[towerTypes[i]]);
towerButton.x = startX + i * buttonSpacing;
towerButton.y = 2600;
towerButton.visible = false;
game.addChild(towerButton);
towerButtons.push(towerButton);
}
function startGame() {
if (gameState === 'playing') return;
gameState = 'playing';
// Hide main menu
if (mainMenu) {
mainMenu.destroy();
mainMenu = null;
}
// Reset game variables
bugs = [];
towers = [];
bullets = [];
colonists = [];
colonies = [];
coins = 80;
wave = 1;
bugsKilled = 0;
colonistsSaved = 0;
selectedTower = null;
placingTowerType = null;
waveTimer = 0;
bugsToSpawn = 5;
bugsSpawned = 0;
gameWon = false;
towerButtons = [];
// Initialize game elements
initializeGameElements();
// Show UI elements
coinText.visible = true;
waveText.visible = true;
infoText.visible = true;
killsText.visible = true;
savedText.visible = true;
// Keep score display hidden during gameplay
if (scoreDisplay) scoreDisplay.visible = false;
for (var i = 0; i < towerButtons.length; i++) {
towerButtons[i].visible = true;
}
// Ensure we start at level 1 and begin first wave
wave = 0; // Start at 0 so first call to startNewWave makes it 1
waveText.setText('Level: 1 | Wave: 1 | Colonies: ' + colonies.length);
LK.setTimeout(function () {
startNewWave();
}, 3000);
}
function initializeGameElements() {
// Add background image
var backgroundImage = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5
}));
backgroundImage.x = 1024;
backgroundImage.y = 1366;
backgroundImage.width = 2048;
backgroundImage.height = 2732;
gameElements.push(backgroundImage);
// Create initial colony
var colony1 = game.addChild(LK.getAsset('colony', {
anchorX: 0.5,
anchorY: 0.5
}));
colony1.x = 400;
colony1.y = 600;
colonies.push(colony1);
gameElements.push(colony1);
// Create bunker
bunker = game.addChild(LK.getAsset('bunker', {
anchorX: 0.5,
anchorY: 0.5
}));
bunker.x = 1800;
bunker.y = 1400;
gameElements.push(bunker);
// Show initial instructions
var instructionText = new Text2('Defend the colonies from alien bugs!\nUse robot towers to stop them!', {
size: 60,
fill: 0x00FFFF
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 600;
game.addChild(instructionText);
gameElements.push(instructionText);
tween(instructionText, {
alpha: 0
}, {
duration: 4000,
onFinish: function onFinish() {
instructionText.destroy();
}
});
// Play background music
var bgMusic = LK.getMusic ? LK.getMusic('gameMusic') : null;
if (bgMusic || LK.playMusic) {
LK.playMusic('gameMusic');
}
}
function updateUI() {
if (gameState !== 'playing') return;
coinText.setText('Coins: ' + coins);
killsText.setText('Bugs Killed: ' + bugsKilled);
savedText.setText('Colonists Saved: ' + colonistsSaved);
if (selectedTower) {
var upgradeText = selectedTower.level >= selectedTower.maxLevel ? 'MAX LEVEL' : 'Upgrade ($' + selectedTower.upgradeCost + ')';
infoText.setText('Tower Lv.' + selectedTower.level + ' - ' + upgradeText + ' | Sell ($' + selectedTower.sellValue + ')');
} else if (placingTowerType) {
infoText.setText('Placing ' + placingTowerType.toUpperCase() + ' tower ($' + towerCosts[placingTowerType] + ') - Tap location');
} else {
infoText.setText('Select a tower type below to place');
}
}
function showTowerInfo() {
updateUI();
}
function spawnBug() {
if (bugsSpawned >= bugsToSpawn) return;
var bugType;
// Random chance for queenbug (boss) to appear in any wave - no set limit
if (Math.random() < 0.15) {
// 15% chance for queenbug to spawn
bugType = 'boss';
} else if (wave >= 15) {
var advancedTypes = ['medium', 'large', 'fast', 'armored', 'flying'];
bugType = advancedTypes[Math.floor(Math.random() * advancedTypes.length)];
} else if (wave >= 10) {
var midTypes = ['small', 'medium', 'fast', 'flying'];
bugType = midTypes[Math.floor(Math.random() * midTypes.length)];
} else if (wave >= 5) {
var earlyTypes = ['small', 'medium', 'fast'];
bugType = earlyTypes[Math.floor(Math.random() * earlyTypes.length)];
} else {
var basicTypes = ['small', 'small', 'medium'];
bugType = basicTypes[Math.floor(Math.random() * basicTypes.length)];
}
var bug = new Bug(bugType, wave);
bug.x = gamePath[0].x;
bug.y = gamePath[0].y;
bugs.push(bug);
game.addChild(bug);
bugsSpawned++;
// Play bug crawling sound when bug spawns
var crawlingSound = LK.getSound('bugcrawling');
if (crawlingSound) crawlingSound.play();
}
function spawnNewColony() {
// Only spawn if we have fewer than 8 colonies total
if (colonies.length >= 8) return;
// Generate random position for new colony in safe areas
var attempts = 0;
var newColony = null;
while (attempts < 50 && !newColony) {
var x = 300 + Math.random() * 1400; // Random X between 300-1700
var y = 500 + Math.random() * 800; // Random Y between 500-1300
var canPlace = true;
attempts++;
// Ensure we're not too close to screen edges
if (x < 200 || x > 1848 || y < 200 || y > 2532) {
canPlace = false;
continue;
}
// Check distance from path
for (var i = 0; i < gamePath.length; i++) {
var dx = x - gamePath[i].x;
var dy = y - gamePath[i].y;
if (Math.sqrt(dx * dx + dy * dy) < 150) {
canPlace = false;
break;
}
}
if (!canPlace) continue;
// Check distance from existing colonies and bunker
var structures = colonies.slice();
structures.push(bunker);
for (var i = 0; i < structures.length; i++) {
var dx = x - structures[i].x;
var dy = y - structures[i].y;
if (Math.sqrt(dx * dx + dy * dy) < 200) {
canPlace = false;
break;
}
}
if (!canPlace) continue;
// Check distance from existing towers
for (var i = 0; i < towers.length; i++) {
var dx = x - towers[i].x;
var dy = y - towers[i].y;
if (Math.sqrt(dx * dx + dy * dy) < 150) {
canPlace = false;
break;
}
}
if (canPlace) {
newColony = game.addChild(LK.getAsset('colony', {
anchorX: 0.5,
anchorY: 0.5
}));
newColony.x = x;
newColony.y = y;
colonies.push(newColony);
gameElements.push(newColony);
// Flash effect for new colony
LK.effects.flashObject(newColony, 0x00FF00, 1000);
break;
}
}
}
function startNewWave() {
// Only increment wave if this isn't the initial call
if (wave === 1 && bugsSpawned > 0) {
wave++;
} else if (wave > 1) {
wave++;
}
// Calculate current level (1-250) based on wave progression
// Level 1: waves 1-2, Level 2: waves 3-4, Level 3: waves 5-6, etc.
var currentLevel = Math.ceil(wave / 2);
var expectedColonies = currentLevel;
// Add colonies to match the current level (1 colony per level)
while (colonies.length < expectedColonies && colonies.length < 8) {
spawnNewColony();
}
bugsToSpawn = Math.min(15, 3 + wave * 2);
bugsSpawned = 0;
waveTimer = 0;
waveText.setText('Level: ' + currentLevel + ' | Wave: ' + wave + ' | Colonies: ' + colonies.length);
// Spawn fleeing colonists and male colonists with dramatic animations
if (wave <= 15 && Math.random() < 0.7) {
// Spawn regular colonists fleeing from random colonies
var colonistsToSpawn = Math.min(colonies.length, Math.ceil(wave / 2));
for (var i = 0; i < colonistsToSpawn; i++) {
var randomColony = colonies[Math.floor(Math.random() * colonies.length)];
var colonist = new Colonist();
colonist.x = randomColony.x + (Math.random() - 0.5) * 150;
colonist.y = randomColony.y + (Math.random() - 0.5) * 150;
colonists.push(colonist);
game.addChild(colonist);
// Add fleeing animation - quick movement toward first waypoint
tween(colonist, {
x: colonistPath[0].x + (Math.random() - 0.5) * 50,
y: colonistPath[0].y + (Math.random() - 0.5) * 50
}, {
duration: 1000 + Math.random() * 1000,
easing: tween.easeOut
});
}
// Spawn male colonists fleeing from random colonies
if (Math.random() < 0.5 && colonies.length > 1) {
var randomColony = colonies[Math.floor(Math.random() * colonies.length)];
var maleColonist = game.addChild(LK.getAsset('colonistmale', {
anchorX: 0.5,
anchorY: 0.5
}));
maleColonist.x = randomColony.x + (Math.random() - 0.5) * 150;
maleColonist.y = randomColony.y + (Math.random() - 0.5) * 150;
maleColonist.speed = 1.2;
maleColonist.pathIndex = 0;
maleColonist.isSafe = false;
maleColonist.isMale = true;
// Add to colonists array for tracking
colonists.push(maleColonist);
// Add panic animation - shake then flee
tween(maleColonist, {
x: maleColonist.x + 20
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(maleColonist, {
x: maleColonist.x - 40
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(maleColonist, {
x: colonistPath[Math.floor(colonistPath.length / 2)].x,
y: colonistPath[Math.floor(colonistPath.length / 2)].y
}, {
duration: 2000 + Math.random() * 1500,
easing: tween.easeOut
});
}
});
}
});
// Add custom update method for male colonist
maleColonist.update = function () {
if (maleColonist.isSafe) return;
if (maleColonist.pathIndex < colonistPath.length) {
var target = colonistPath[maleColonist.pathIndex];
var dx = target.x - maleColonist.x;
var dy = target.y - maleColonist.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 20) {
maleColonist.pathIndex++;
if (maleColonist.pathIndex >= colonistPath.length) {
maleColonist.isSafe = true;
colonistsSaved++;
updateUI();
}
} else {
maleColonist.x += dx / distance * maleColonist.speed;
maleColonist.y += dy / distance * maleColonist.speed;
}
}
};
}
}
}
game.down = function (x, y, obj) {
if (gameState !== 'playing') return;
// Handle tower upgrade/sell if tower is selected
if (selectedTower && x > 1500 && y > 2400) {
// Clicked in UI area
if (y > 2500) {
// Sell button area
selectedTower.sell();
} else {
// Upgrade button area
selectedTower.upgrade();
}
updateUI();
return;
}
// Handle tower placement
if (placingTowerType && y < 2400) {
var canPlace = true;
var cost = towerCosts[placingTowerType];
// Check if we have enough coins
if (coins < cost) {
canPlace = false;
}
// Check distance from path
for (var i = 0; i < gamePath.length; i++) {
var dx = x - gamePath[i].x;
var dy = y - gamePath[i].y;
if (Math.sqrt(dx * dx + dy * dy) < 80) {
canPlace = false;
break;
}
}
// Check distance from existing towers
for (var i = 0; i < towers.length; i++) {
var dx = x - towers[i].x;
var dy = y - towers[i].y;
if (Math.sqrt(dx * dx + dy * dy) < 100) {
canPlace = false;
break;
}
}
// Check distance from colonies and bunker
var structures = colonies.slice(); // Copy colonies array
structures.push(bunker);
for (var i = 0; i < structures.length; i++) {
var dx = x - structures[i].x;
var dy = y - structures[i].y;
if (Math.sqrt(dx * dx + dy * dy) < 120) {
canPlace = false;
break;
}
}
if (canPlace) {
var tower = new Tower(placingTowerType);
tower.x = x;
tower.y = y;
towers.push(tower);
game.addChild(tower);
coins -= cost;
placingTowerType = null;
updateUI();
}
} else {
// Clear selection
selectedTower = null;
placingTowerType = null;
}
updateUI();
};
game.update = function () {
if (gameState !== 'playing') return;
if (gameWon) return;
// Spawn bugs
waveTimer++;
if (waveTimer % 45 === 0 && bugsSpawned < bugsToSpawn) {
spawnBug();
}
// Check if wave is complete
if (bugsSpawned >= bugsToSpawn && bugs.length === 0) {
var currentLevel = Math.ceil(wave / 2);
if (currentLevel >= 250) {
gameWon = true;
var finalScore = calculateScore();
saveHighScore(finalScore);
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
} else {
// Properly advance to next wave/level
LK.setTimeout(function () {
startNewWave();
}, 2000);
}
}
// Clean up bullets
for (var i = bullets.length - 1; i >= 0; i--) {
if (!bullets[i].parent) {
bullets.splice(i, 1);
}
}
// Update colonists that have update methods
for (var i = 0; i < colonists.length; i++) {
var colonist = colonists[i];
if (colonist.update && typeof colonist.update === 'function') {
colonist.update();
}
}
// Check for bug-colonist intersections
for (var i = bugs.length - 1; i >= 0; i--) {
var bug = bugs[i];
for (var j = colonists.length - 1; j >= 0; j--) {
var colonist = colonists[j];
if (!colonist.isSafe && colonist.intersects && colonist.intersects(bug)) {
// Play colonist scream sound when killed by bug
var screamSound = LK.getSound('colonistscream');
if (screamSound) screamSound.play();
// Remove the colonist
colonist.destroy();
colonists.splice(j, 1);
break; // One bug can only kill one colonist per frame
}
}
}
// Check for bugs reaching the end
var bugsReachedEnd = 0;
for (var i = bugs.length - 1; i >= 0; i--) {
var bug = bugs[i];
if (bug.pathIndex >= gamePath.length) {
bug.destroy();
bugs.splice(i, 1);
bugsReachedEnd++;
if (bug.isBoss) {
bugsReachedEnd += 4; // Boss counts as 5 bugs reaching end
}
}
}
// Check lose condition
if (bugsReachedEnd >= 3) {
var finalScore = calculateScore();
saveHighScore(finalScore);
LK.showGameOver();
}
// Update UI periodically
if (LK.ticks % 60 === 0) {
updateUI();
}
};
Modern App Store icon, high definition, square with rounded corners, for a game titled "Klingon Defense: Outer Rim Survival" and with the description "Defend human colonies on planet Klingon from alien bug invasions using strategic robot tower placement. Earn coins by eliminating bugs and upgrade defenses to protect settlements across multiple cities.". No text on icon!
Fullscreen modern App Store art style with futuristic laser bullet, 16:9, high definition. No text captions!
Front facing icon Fullscreen modern App Store art style with futuristic bunker, 16:9, high definition. No text captions!
Front-facing icon, Fullscreen modern App Store art style with futuristic male colonist, 16:9, high definition. No text captions!
Front-facing icon, Fullscreen modern App Store art style with futuristic colony, 16:9, high definition. No text captions!
Front-facing icon, Fullscreen modern App Store art style with futuristic tower defense, 16:9, high definition. No text captions!
Front-facing icon, Fullscreen modern App Store art style with futuristic alien bug , 16:9, high definition. No text captions!
Front-facing icon, Fullscreen modern App Store art style with futuristic medium alien bug , 16:9, high definition. No text captions!
Front-facing icon, Fullscreen modern App Store art style with futuristic small flying alien bug , 16:9, high definition. No text captions!
Fullscreen modern App Store landscape banner, 16:9, high definition, red sand with blue rocks jutting out No text on banner!
Front-facing icon, Fullscreen modern App Store art style with futuristic alien queenbug , 16:9, high definition. No text captions!