User prompt
may the shovel be infinite
User prompt
On the left in the centre there is a button to restart the game.
User prompt
the text of the sniper tower starts with 1000 and every time you put it the price goes up by 100 coins.
User prompt
the text of the splash tower starts with 350 and every time you put it the price goes up by 20 coins.
User prompt
that you start at the beginning of the game with 200 coins
User prompt
that you start at the beginning of the game with 500 coins
User prompt
the text of the slow tower starts with 500 and every time you put it the price goes up by 40 coins.
User prompt
that the price of the slow tower should start at 500 coins
User prompt
that the price of the slow tower increases by 40 coins every time you place one
User prompt
that every 11 waves the fast enemy has a little more life.
User prompt
every 25 waves the enemy tank has a little more life.
User prompt
that you start the game with 200 coins at the beginning of the game.
User prompt
sniper tower to do 100 damage per bullet
User prompt
that the price of the slow tower be raised to 500 coins
User prompt
that the price of the splash tower be raised to 350 coins
User prompt
that the price of the basic tower be raised to 100 coins
User prompt
that the enemy tank appears from wave 11 onwards.
User prompt
the enemy tank has 400 life
User prompt
that the slow tower firing distance is increased by 5 blocks
User prompt
that the splash tower firing distance is increased by 5 blocks
User prompt
every 15 waves there is a wave of golden enemies
User prompt
that the fast enemies appear from wave 4 onwards.
User prompt
the life of fast enemies to be 80
User prompt
that the shovel never wears out
User prompt
basic enemies give you 2 coins when you kill them
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function (damage, target, assetType) { var self = Container.call(this); var bulletAsset = assetType || 'bullet'; var bulletGraphics = self.attachAsset(bulletAsset, { anchorX: 0.5, anchorY: 0.5 }); self.damage = damage || 10; self.target = target; self.speed = 8; 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 < 10) { self.target.takeDamage(self.damage); // If this is a slow bullet (iceball), apply slow effect if (bulletAsset === 'iceball') { self.target.slowed = true; self.target.slowDuration = 240; // 4 seconds at 60fps // Apply ice tint effect to show enemy is slowed tween(self.target, { tint: 0x87CEEB }, { duration: 200 }); } // If this is a splash bullet, create persistent water damage area if (self.isSplash) { // Apply initial splash damage to nearby enemies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var splashDx = enemy.x - self.target.x; var splashDy = enemy.y - self.target.y; var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy); if (splashDistance <= 80 && enemy !== self.target) { enemy.takeDamage(Math.floor(self.damage * 0.5)); // Half damage for splash } } // Only create persistent water damage area if flag is set (after 10 shots) if (self.createWaterDamage) { var waterDamage = new WaterDamage(self.target.x, self.target.y, Math.floor(self.damage * 0.3)); waterDamageAreas.push(waterDamage); game.addChild(waterDamage); } } self.destroy(); return; } var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed * gameSpeed; self.y += Math.sin(angle) * self.speed * gameSpeed; // Add rainbow color cycling to bullets for colorful effect var rainbowColors = [0xFF0000, 0xFF8C00, 0xFFD700, 0x00FF00, 0x00BFFF, 0x8A2BE2]; var colorIndex = Math.floor(LK.ticks / 10 % rainbowColors.length); bulletGraphics.tint = rainbowColors[colorIndex]; }; return self; }); var Enemy = Container.expand(function (enemyType) { var self = Container.call(this); self.enemyType = enemyType || 'basic'; self.pathIndex = 0; self.health = 30; self.maxHealth = 30; self.speed = 2; self.reward = 10; self.slowed = false; self.slowDuration = 0; var enemyGraphics; if (self.enemyType === 'basic') { enemyGraphics = self.attachAsset('basicEnemy', { anchorX: 0.5, anchorY: 0.5 }); // Increase health every 10 waves var healthBonus = Math.floor(wave / 10) * 10; // +10 health per 10 waves self.health = 30 + healthBonus; self.maxHealth = 30 + healthBonus; self.speed = 2; self.reward = 10; } else if (self.enemyType === 'fast') { enemyGraphics = self.attachAsset('fastEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 15; self.maxHealth = 15; self.speed = 4; self.reward = 15; } else if (self.enemyType === 'tank') { enemyGraphics = self.attachAsset('tankEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 60; self.maxHealth = 60; self.speed = 1; self.reward = 25; } else if (self.enemyType === 'gold') { enemyGraphics = self.attachAsset('goldEnemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; self.speed = 3; self.reward = 100; // High reward! } // Create health bar background var healthBarBg = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.1 }); healthBarBg.y = -enemyGraphics.height / 2 - 20; healthBarBg.tint = 0x000000; self.addChild(healthBarBg); // Create health bar foreground var healthBarFg = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.1 }); healthBarFg.y = -enemyGraphics.height / 2 - 20; healthBarFg.tint = 0x00FF00; self.addChild(healthBarFg); // Create health text to display current health as number var healthText = new Text2(self.health.toString(), { size: 30, fill: 0xFFFFFF }); healthText.anchor.set(0.5, 0.5); healthText.y = -enemyGraphics.height / 2 - 20; self.addChild(healthText); self.healthBarBg = healthBarBg; self.healthBarFg = healthBarFg; self.healthText = healthText; self.updateHealthBar = function () { var healthPercent = self.health / self.maxHealth; self.healthBarFg.scaleX = 0.4 * healthPercent; // Update health text to show current health number self.healthText.setText(Math.max(0, self.health).toString()); // Change color based on health if (healthPercent > 0.6) { self.healthBarFg.tint = 0x00FF00; // Green } else if (healthPercent > 0.3) { self.healthBarFg.tint = 0xFFFF00; // Yellow } else { self.healthBarFg.tint = 0xFF0000; // Red } }; self.takeDamage = function (damage) { self.health -= damage; self.updateHealthBar(); // Add colorful damage flash effect var damageFlashColors = [0xFF4444, 0xFF8844, 0xFFFF44, 0x44FF44, 0x4444FF, 0xFF44FF]; var flashColor = damageFlashColors[Math.floor(Math.random() * damageFlashColors.length)]; tween(enemyGraphics, { tint: flashColor, scaleX: 1.2, scaleY: 1.2 }, { duration: 120, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0xFFFFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 180 }); } }); LK.getSound('enemyHit').play(); if (self.health <= 0) { self.die(); } }; self.die = function () { // Basic enemies give 2 coins, others use their reward value var coinReward = self.enemyType === 'basic' ? 2 : self.reward; coins += coinReward; enemiesKilled++; killStreak++; LK.getSound('enemyDeath').play(); for (var i = 0; i < enemies.length; i++) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); updateUI(); }; self.update = function () { if (self.slowed) { self.slowDuration--; if (self.slowDuration <= 0) { self.slowed = false; // Remove ice tint when slow effect expires tween(self, { tint: 0xFFFFFF }, { duration: 200 }); } } if (self.pathIndex >= pathPoints.length - 1) { // Game over immediately when any enemy reaches the end showCustomGameOver(); return; } var currentSpeed = self.slowed ? self.speed * 0.5 : self.speed; currentSpeed *= gameSpeed; // Apply game speed multiplier var target = pathPoints[self.pathIndex + 1]; var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 10) { self.pathIndex++; } else { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * currentSpeed; self.y += Math.sin(angle) * currentSpeed; } }; // Initialize health bar display self.updateHealthBar(); return self; }); var Tower = Container.expand(function (towerType) { var self = Container.call(this); self.towerType = towerType || 'basic'; self.level = 1; self.range = 120; self.damage = 10; self.fireRate = 60; self.cost = 50; self.upgradeCost = 30; self.lastShot = 0; self.shotCount = 0; // Track number of shots for splash tower var towerGraphics; if (self.towerType === 'basic') { towerGraphics = self.attachAsset('basicTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 15; self.range = 620; self.fireRate = 60; self.cost = 50; } else if (self.towerType === 'splash') { towerGraphics = self.attachAsset('splashTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 15; self.range = 100; self.fireRate = 90; self.cost = 130; } else if (self.towerType === 'slow') { towerGraphics = self.attachAsset('slowTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 25; self.range = 140; self.fireRate = 30; self.cost = 200; } else if (self.towerType === 'sniper') { towerGraphics = self.attachAsset('sniperTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 60; self.range = 1000; self.fireRate = 300; self.cost = 30; } self.canShoot = function () { return LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed); }; self.findTarget = function () { var closestEnemy = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range && distance < closestDistance) { closestEnemy = enemy; closestDistance = distance; } } return closestEnemy; }; self.shoot = function (target) { if (!self.canShoot()) return; // Add colorful muzzle flash effect when shooting var flashColors = { 'basic': 0xFFD700, 'splash': 0x00AAFF, 'slow': 0x87CEEB, 'sniper': 0xFF4500 }; tween(towerGraphics, { scaleX: 1.2, scaleY: 1.2, tint: flashColors[self.towerType] || 0xFFFFFF }, { duration: 100, onFinish: function onFinish() { tween(towerGraphics, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); if (self.towerType === 'basic') { // Fire single bullet for basic tower var bullet = new Bullet(self.damage, target); bullet.x = self.x; bullet.y = self.y; bullets.push(bullet); game.addChild(bullet); } else if (self.towerType === 'splash') { // Splash tower fires waterball bullets and leaves watercharc damage var bullet = new Bullet(self.damage, target, 'waterball'); bullet.x = self.x; bullet.y = self.y; bullet.isSplash = true; // Mark as splash bullet self.shotCount++; // Increment shot counter // Only create water damage after 10 shots if (self.shotCount >= 10) { bullet.createWaterDamage = true; self.shotCount = 0; // Reset counter } bullets.push(bullet); game.addChild(bullet); } else if (self.towerType === 'slow') { // Slow tower fires iceball bullets var bullet = new Bullet(self.damage, target, 'iceball'); bullet.x = self.x; bullet.y = self.y; bullets.push(bullet); game.addChild(bullet); } else if (self.towerType === 'sniper') { // Sniper tower fires sniperball bullets var bullet = new Bullet(self.damage, target, 'sniperball'); bullet.x = self.x; bullet.y = self.y; bullets.push(bullet); game.addChild(bullet); } else { // Other tower types fire single bullet var bullet = new Bullet(self.damage, target); bullet.x = self.x; bullet.y = self.y; bullets.push(bullet); game.addChild(bullet); } self.lastShot = LK.ticks; LK.getSound('shoot').play(); }; self.upgrade = function () { if (coins >= self.upgradeCost && self.level < 3) { coins -= self.upgradeCost; self.level++; self.damage = Math.floor(self.damage * 1.5); self.range += 20; self.upgradeCost = Math.floor(self.upgradeCost * 1.5); updateUI(); } }; self.update = function () { var target = self.findTarget(); if (target) { self.shoot(target); } }; self.down = function (x, y, obj) { selectedTower = self; showTowerMenu = true; updateUI(); }; return self; }); var WaterDamage = Container.expand(function (x, y, damage) { var self = Container.call(this); var waterGraphics = self.attachAsset('watercharc', { anchorX: 0.5, anchorY: 0.5 }); self.x = x; self.y = y; self.damage = damage || 5; // Damage per tick self.radius = 80; // Damage radius self.lifetime = 300; // 5 seconds at 60fps self.damageInterval = 30; // Damage every 0.5 seconds self.lastDamageTick = 0; waterGraphics.alpha = 0.7; waterGraphics.tint = 0x00AAFF; // Animate the water area appearance waterGraphics.scaleX = 0.5; waterGraphics.scaleY = 0.5; tween(waterGraphics, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); self.update = function () { self.lifetime--; // Apply damage to enemies in range at intervals if (LK.ticks - self.lastDamageTick >= self.damageInterval) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.radius) { enemy.takeDamage(self.damage); } } self.lastDamageTick = LK.ticks; } // Fade out as lifetime decreases if (self.lifetime < 60) { // Start fading in last second waterGraphics.alpha = 0.7 * (self.lifetime / 60); } // Remove when lifetime expires if (self.lifetime <= 0) { self.destroy(); // Remove from waterDamageAreas array for (var j = waterDamageAreas.length - 1; j >= 0; j--) { if (waterDamageAreas[j] === self) { waterDamageAreas.splice(j, 1); break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x8b4513 // Brown color }); /**** * Game Code ****/ // Game state management var gameState = 'home'; // 'home', 'playing' var homeScreen = null; var gridSize = 100; var gridWidth = 20; var gridHeight = 27; var pathPoints = [{ x: 0, y: 400 }, { x: 300, y: 400 }, { x: 300, y: 800 }, { x: 700, y: 800 }, { x: 700, y: 1200 }, { x: 1100, y: 1200 }, { x: 1100, y: 1600 }, { x: 1500, y: 1600 }, { x: 1500, y: 2000 }, { x: 2048, y: 2000 }]; var gameGrid = []; var towers = []; var enemies = []; var bullets = []; var waterDamageAreas = []; var coins = 100; var lives = 20; var wave = 1; var enemiesKilled = 0; var killStreak = 0; var lastLives = 20; var enemiesSpawned = 0; var enemiesPerWave = 10; var spawnTimer = 0; var waveInProgress = false; var selectedTower = null; var showTowerMenu = false; var towerMenuButtons = []; // Create grid for (var y = 0; y < gridHeight; y++) { gameGrid[y] = []; for (var x = 0; x < gridWidth; x++) { gameGrid[y][x] = { x: x * gridSize + 50, y: y * gridSize + 50, occupied: false, isPath: false }; } } // Mark path tiles for (var i = 0; i < pathPoints.length - 1; i++) { var start = pathPoints[i]; var end = pathPoints[i + 1]; var steps = Math.max(Math.abs(end.x - start.x), Math.abs(end.y - start.y)) / gridSize; for (var step = 0; step <= steps; step++) { var x = Math.floor((start.x + (end.x - start.x) * step / steps) / gridSize); var y = Math.floor((start.y + (end.y - start.y) * step / steps) / gridSize); if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) { gameGrid[y][x].isPath = true; } } } // Create visual grid for (var y = 0; y < gridHeight; y++) { for (var x = 0; x < gridWidth; x++) { var tile; if (gameGrid[y][x].isPath) { tile = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5 }); } else { tile = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0.5 }); } tile.x = gameGrid[y][x].x; tile.y = gameGrid[y][x].y; tile.alpha = 0.3; game.addChild(tile); } } // UI Elements var coinsText = new Text2('Coins: ' + coins, { size: 60, fill: 0xFFD700 }); coinsText.anchor.set(1, 0); LK.gui.topRight.addChild(coinsText); // Add continuous rainbow animation to coins text function animateCoinsText() { var rainbowColors = [0xFFD700, 0xFF6B35, 0xF7931E, 0xFFD700, 0x00FF7F, 0x1E90FF, 0x9370DB]; var colorIndex = Math.floor(LK.ticks / 30 % rainbowColors.length); tween(coinsText, { tint: rainbowColors[colorIndex], scaleX: 1.1, scaleY: 1.1 }, { duration: 200, onFinish: function onFinish() { tween(coinsText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }); } var livesText = new Text2('Lives: ' + lives, { size: 50, fill: 0xFFFFFF }); livesText.anchor.set(0, 0); LK.gui.topRight.addChild(livesText); var waveText = new Text2('Wave: ' + wave, { size: 50, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); var startWaveButton = new Text2('🚀 START WAVE 🚀', { size: 80, fill: 0x00FF00 }); startWaveButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(startWaveButton); // Add colorful tween animation to make it more eye-catching function animateStartButton() { tween(startWaveButton, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, onFinish: function onFinish() { tween(startWaveButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 500, onFinish: animateStartButton }); } }); } animateStartButton(); // Show home screen instead of starting immediately createHomeScreen(); // Tower selection buttons var basicTowerButton = new Text2('Basic ($50)', { size: 80, fill: 0xFFFFFF }); basicTowerButton.anchor.set(0, 1); LK.gui.bottomLeft.addChild(basicTowerButton); var splashTowerButton = new Text2('Splash ($130)', { size: 80, fill: 0xFFFFFF }); splashTowerButton.anchor.set(0, 1); LK.gui.bottomLeft.addChild(splashTowerButton); var slowTowerButton = new Text2('Slow ($200)', { size: 80, fill: 0xFFFFFF }); slowTowerButton.anchor.set(0, 1); LK.gui.bottomLeft.addChild(slowTowerButton); var sniperTowerButton = new Text2('Sniper ($30)', { size: 80, fill: 0xFFFFFF }); sniperTowerButton.anchor.set(0, 1); LK.gui.bottomLeft.addChild(sniperTowerButton); // Create shovel button for tower elimination var shovelButton = new Text2('🔨', { size: 120, fill: 0xFFFFFF }); shovelButton.anchor.set(0.5, 0.5); LK.gui.right.addChild(shovelButton); // Position tower buttons basicTowerButton.y = -400; splashTowerButton.y = -300; slowTowerButton.y = -200; sniperTowerButton.y = -100; coinsText.y = 50; livesText.y = 100; var selectedTowerType = 'basic'; var shovelMode = false; var isPaused = false; var pauseMenu = null; var pauseButton = null; var restartButton = null; var exitButton = null; var musicButton = null; var musicEnabled = true; var gameOverMenu = null; function createHomeScreen() { if (homeScreen) return; // Already created homeScreen = new Container(); // Create animated background var background = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 32, scaleY: 45 }); background.tint = 0x2c3e50; background.alpha = 0.95; background.x = 1024; background.y = 1366; homeScreen.addChild(background); // Create game title with shadow effect var shadowTitle = new Text2('TOWER DEFENSE', { size: 160, fill: 0x000000 }); shadowTitle.anchor.set(0.5, 0.5); shadowTitle.x = 1029; // Slight offset for shadow shadowTitle.y = 505; homeScreen.addChild(shadowTitle); var gameTitle = new Text2('TOWER DEFENSE', { size: 160, fill: 0xe74c3c }); gameTitle.anchor.set(0.5, 0.5); gameTitle.x = 1024; gameTitle.y = 500; homeScreen.addChild(gameTitle); // Create subtitle var subtitle = new Text2('Defend Your Path!', { size: 80, fill: 0x3498db }); subtitle.anchor.set(0.5, 0.5); subtitle.x = 1024; subtitle.y = 650; homeScreen.addChild(subtitle); // Create start button var startBg = LK.getAsset('basicTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 2 }); startBg.tint = 0x27ae60; startBg.x = 1024; startBg.y = 900; homeScreen.addChild(startBg); var startButton = new Text2('🎮 START GAME', { size: 90, fill: 0xffffff }); startButton.anchor.set(0.5, 0.5); startButton.x = 1024; startButton.y = 900; homeScreen.addChild(startButton); // Create instructions var instructionsTitle = new Text2('HOW TO PLAY:', { size: 60, fill: 0xf39c12 }); instructionsTitle.anchor.set(0.5, 0.5); instructionsTitle.x = 1024; instructionsTitle.y = 1200; homeScreen.addChild(instructionsTitle); var instruction1 = new Text2('• Tap tower buttons to select type', { size: 45, fill: 0xffffff }); instruction1.anchor.set(0.5, 0.5); instruction1.x = 1024; instruction1.y = 1300; homeScreen.addChild(instruction1); var instruction2 = new Text2('• Tap empty grass to place towers', { size: 45, fill: 0xffffff }); instruction2.anchor.set(0.5, 0.5); instruction2.x = 1024; instruction2.y = 1350; homeScreen.addChild(instruction2); var instruction3 = new Text2('• Stop enemies from reaching the end!', { size: 45, fill: 0xffffff }); instruction3.anchor.set(0.5, 0.5); instruction3.x = 1024; instruction3.y = 1400; homeScreen.addChild(instruction3); var instruction4 = new Text2('• Use speed x2 button to fast forward', { size: 45, fill: 0xffffff }); instruction4.anchor.set(0.5, 0.5); instruction4.x = 1024; instruction4.y = 1450; homeScreen.addChild(instruction4); // Create decorative tower icons var basicIcon = LK.getAsset('basicTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); basicIcon.x = 300; basicIcon.y = 1600; homeScreen.addChild(basicIcon); var splashIcon = LK.getAsset('splashTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); splashIcon.x = 700; splashIcon.y = 1600; homeScreen.addChild(splashIcon); var slowIcon = LK.getAsset('slowTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); slowIcon.x = 1300; slowIcon.y = 1600; homeScreen.addChild(slowIcon); var sniperIcon = LK.getAsset('sniperTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); sniperIcon.x = 1700; sniperIcon.y = 1600; homeScreen.addChild(sniperIcon); // Add floating animation to icons tween(basicIcon, { y: basicIcon.y + 20 }, { duration: 2000, loop: true, yoyo: true }); tween(splashIcon, { y: splashIcon.y + 20 }, { duration: 2200, loop: true, yoyo: true }); tween(slowIcon, { y: slowIcon.y + 20 }, { duration: 2400, loop: true, yoyo: true }); tween(sniperIcon, { y: sniperIcon.y + 20 }, { duration: 2600, loop: true, yoyo: true }); // Add button event handler startButton.down = function () { tween(startButton, { scaleX: 0.9, scaleY: 0.9, tint: 0x2ecc71 }, { duration: 100, onFinish: function onFinish() { startGame(); } }); }; // Animate title pulsing function animateTitle() { tween(gameTitle, { scaleX: 1.1, scaleY: 1.1, tint: 0xff6b6b }, { duration: 2000, onFinish: function onFinish() { tween(gameTitle, { scaleX: 1.0, scaleY: 1.0, tint: 0xe74c3c }, { duration: 2000, onFinish: animateTitle }); } }); } animateTitle(); // Animate start button function animateStartButton() { tween(startButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1500, onFinish: function onFinish() { tween(startButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1500, onFinish: animateStartButton }); } }); } animateStartButton(); game.addChild(homeScreen); } function startGame() { gameState = 'playing'; if (homeScreen) { homeScreen.destroy(); homeScreen = null; } // Start playing background music LK.playMusic('bgmusic', { volume: 1.0 }); } function showCustomGameOver() { if (gameOverMenu) return; // Already showing isPaused = true; // Create game over menu container gameOverMenu = new Container(); // Create animated background with gradient effect var background1 = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 32, scaleY: 45 }); background1.tint = 0x1a1a2e; background1.alpha = 0.95; background1.x = 1024; background1.y = 1366; gameOverMenu.addChild(background1); // Create decorative border elements var borderTop = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 30, scaleY: 1 }); borderTop.tint = 0xe74c3c; borderTop.x = 1024; borderTop.y = 300; gameOverMenu.addChild(borderTop); var borderBottom = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 30, scaleY: 1 }); borderBottom.tint = 0xe74c3c; borderBottom.x = 1024; borderBottom.y = 2400; gameOverMenu.addChild(borderBottom); // Create large "GAME OVER" title with shadow effect var shadowText = new Text2('GAME OVER', { size: 140, fill: 0x000000 }); shadowText.anchor.set(0.5, 0.5); shadowText.x = 1029; // Slight offset for shadow shadowText.y = 605; gameOverMenu.addChild(shadowText); var titleText = new Text2('GAME OVER', { size: 140, fill: 0xe74c3c }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 600; gameOverMenu.addChild(titleText); // Create colorful stats display var statsContainer = new Container(); // Stats background var statsBg = LK.getAsset('slowTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 12, scaleY: 8 }); statsBg.tint = 0x16213e; statsBg.alpha = 0.8; statsBg.x = 1024; statsBg.y = 1000; statsContainer.addChild(statsBg); // Wave reached text var waveReachedText = new Text2('Wave Reached: ' + wave, { size: 60, fill: 0x3498db }); waveReachedText.anchor.set(0.5, 0.5); waveReachedText.x = 1024; waveReachedText.y = 850; statsContainer.addChild(waveReachedText); // Enemies killed text var killsText = new Text2('Enemies Defeated: ' + enemiesKilled, { size: 60, fill: 0x2ecc71 }); killsText.anchor.set(0.5, 0.5); killsText.x = 1024; killsText.y = 950; statsContainer.addChild(killsText); // Final score text var finalScore = wave * 100 + enemiesKilled * 10; var scoreText = new Text2('Final Score: ' + finalScore, { size: 60, fill: 0xf39c12 }); scoreText.anchor.set(0.5, 0.5); scoreText.x = 1024; scoreText.y = 1050; statsContainer.addChild(scoreText); // Best streak text var streakText = new Text2('Best Streak: ' + killStreak, { size: 60, fill: 0x9b59b6 }); streakText.anchor.set(0.5, 0.5); streakText.x = 1024; streakText.y = 1150; statsContainer.addChild(streakText); gameOverMenu.addChild(statsContainer); // Create animated restart button with glow effect var restartBg = LK.getAsset('basicTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5 }); restartBg.tint = 0x27ae60; restartBg.x = 1024; restartBg.y = 1400; gameOverMenu.addChild(restartBg); var restartGameButton = new Text2('🔄 PLAY AGAIN', { size: 70, fill: 0xffffff }); restartGameButton.anchor.set(0.5, 0.5); restartGameButton.x = 1024; restartGameButton.y = 1400; gameOverMenu.addChild(restartGameButton); // Create animated exit button var exitBg = LK.getAsset('sniperTower', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 1.5 }); exitBg.tint = 0xc0392b; exitBg.x = 1024; exitBg.y = 1600; gameOverMenu.addChild(exitBg); var exitGameButton = new Text2('❌ EXIT GAME', { size: 70, fill: 0xffffff }); exitGameButton.anchor.set(0.5, 0.5); exitGameButton.x = 1024; exitGameButton.y = 1600; gameOverMenu.addChild(exitGameButton); // Add floating decorative elements for (var i = 0; i < 8; i++) { var decoration = LK.getAsset('waterball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); decoration.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6][i % 5]; decoration.alpha = 0.6; decoration.x = 200 + i * 225; decoration.y = 400 + Math.sin(i) * 100; gameOverMenu.addChild(decoration); // Animate decorations tween(decoration, { y: decoration.y + 50, rotation: Math.PI * 2 }, { duration: 2000 + i * 200, loop: true, yoyo: true }); } // Add button event handlers restartGameButton.down = function () { tween(restartGameButton, { scaleX: 0.9, scaleY: 0.9, tint: 0x2ecc71 }, { duration: 100, onFinish: function onFinish() { LK.showGameOver(); } }); }; exitGameButton.down = function () { tween(exitGameButton, { scaleX: 0.9, scaleY: 0.9, tint: 0xe74c3c }, { duration: 100, onFinish: function onFinish() { LK.showGameOver(); } }); }; // Animate title text with pulsing effect function animateTitle() { tween(titleText, { scaleX: 1.1, scaleY: 1.1, tint: 0xff6b6b }, { duration: 1000, onFinish: function onFinish() { tween(titleText, { scaleX: 1.0, scaleY: 1.0, tint: 0xe74c3c }, { duration: 1000, onFinish: animateTitle }); } }); } animateTitle(); // Animate stats container entrance statsContainer.alpha = 0; statsContainer.scaleX = 0.5; statsContainer.scaleY = 0.5; tween(statsContainer, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 800, delay: 500 }); game.addChild(gameOverMenu); } // Create pause button in top right corner pauseButton = new Text2('⏸', { size: 60, fill: 0xFFFFFF }); pauseButton.anchor.set(0, 0); pauseButton.x = 150; // Offset from top right to avoid overlap with coins pauseButton.y = 0; LK.gui.topRight.addChild(pauseButton); // Create speed x2 button var speedButton = new Text2('x1', { size: 50, fill: 0x00FF00 }); speedButton.anchor.set(0.5, 0); speedButton.x = 0; // Center horizontally speedButton.y = 80; // Position below wave text LK.gui.top.addChild(speedButton); // Game speed state var gameSpeed = 1; function createPauseMenu() { if (pauseMenu) return; // Already created // Create semi-transparent background pauseMenu = new Container(); var background = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 30, scaleY: 40 }); background.tint = 0x000000; background.alpha = 0.7; background.x = 1024; background.y = 1366; pauseMenu.addChild(background); // Create menu title var titleText = new Text2('PAUSED', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; pauseMenu.addChild(titleText); // Create restart button restartButton = new Text2('RESTART', { size: 60, fill: 0x00FF00 }); restartButton.anchor.set(0.5, 0.5); restartButton.x = 1024; restartButton.y = 1000; pauseMenu.addChild(restartButton); // Create exit button exitButton = new Text2('EXIT', { size: 60, fill: 0xFF0000 }); exitButton.anchor.set(0.5, 0.5); exitButton.x = 1024; exitButton.y = 1200; pauseMenu.addChild(exitButton); // Create music toggle button musicButton = new Text2(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF', { size: 60, fill: 0xFFFFFF }); musicButton.tint = musicEnabled ? 0x00FFFF : 0x888888; musicButton.anchor.set(0.5, 0.5); musicButton.x = 1024; musicButton.y = 1400; pauseMenu.addChild(musicButton); // Create resume button var resumeButton = new Text2('RESUME', { size: 60, fill: 0xFFFF00 }); resumeButton.anchor.set(0.5, 0.5); resumeButton.x = 1024; resumeButton.y = 1600; pauseMenu.addChild(resumeButton); // Add event handlers restartButton.down = function () { LK.showGameOver(); // Restart the game }; exitButton.down = function () { showCustomGameOver(); // Exit to custom game over screen }; musicButton.down = function () { musicEnabled = !musicEnabled; if (musicEnabled) { LK.playMusic('bgmusic', { volume: 1.0 }); musicButton.setText('MUSIC: ON'); musicButton.tint = 0x00FFFF; } else { LK.stopMusic(); musicButton.setText('MUSIC: OFF'); musicButton.tint = 0x888888; } }; resumeButton.down = function () { togglePause(); }; game.addChild(pauseMenu); } function togglePause() { isPaused = !isPaused; if (isPaused) { createPauseMenu(); pauseMenu.visible = true; } else { if (pauseMenu) { pauseMenu.visible = false; } } } function showExitButton() { isPaused = true; if (!exitButton) { // Create exit button if it doesn't exist exitButton = new Text2('EXIT GAME', { size: 60, fill: 0xFF0000 }); exitButton.anchor.set(0.5, 0.5); exitButton.x = 1024; exitButton.y = 1366; exitButton.down = function () { showCustomGameOver(); // Exit to custom game over screen }; game.addChild(exitButton); } else { exitButton.visible = true; } } function updateUI() { var coinDisplay = 'Coins: ' + coins; coinsText.setText(coinDisplay); livesText.setText('Lives: ' + lives); waveText.setText('Wave: ' + wave); if (showTowerMenu && selectedTower) { // Show upgrade option } } function getGridPosition(x, y) { var gridX = Math.floor(x / gridSize); var gridY = Math.floor(y / gridSize); if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) { return gameGrid[gridY][gridX]; } return null; } function canPlaceTower(gridPos) { return gridPos && !gridPos.occupied && !gridPos.isPath; } function spawnEnemy() { var enemyType = 'basic'; var rand = Math.random(); // Only basic enemies in wave 1 if (wave === 1) { enemyType = 'basic'; } else { // 5% chance for gold enemy (high reward, low health) if (rand < 0.05) { enemyType = 'gold'; } else if (wave > 5) { if (rand < 0.3) enemyType = 'fast';else if (rand < 0.6) enemyType = 'tank'; } else if (wave > 2) { if (rand < 0.2) enemyType = 'fast'; } } var enemy = new Enemy(enemyType); enemy.x = pathPoints[0].x; enemy.y = pathPoints[0].y; enemies.push(enemy); game.addChild(enemy); enemiesSpawned++; } function startWave() { if (waveInProgress) return; if (waveInProgress) return; waveInProgress = true; enemiesSpawned = 0; enemiesPerWave = 10 + wave * 2; spawnTimer = 0; showTowerMenu = false; selectedTower = null; // Shovel mode can now be used continuously without resetting // Display wave start message showWaveStartMessage(); } function showWaveStartMessage() { // Create wave message container var waveMessage = new Container(); waveMessage.x = 1024; waveMessage.y = 1366; // Create background for message var messageBg = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 12, scaleY: 3 }); messageBg.tint = 0x2c3e50; messageBg.alpha = 0.9; waveMessage.addChild(messageBg); // Create wave start text var waveStartText = new Text2('WAVE ' + wave + ' STARTED!', { size: 80, fill: 0xf39c12 }); waveStartText.anchor.set(0.5, 0.5); waveMessage.addChild(waveStartText); // Create decorative elements var leftDecoration = LK.getAsset('waterball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); leftDecoration.tint = 0xe74c3c; leftDecoration.x = -300; waveMessage.addChild(leftDecoration); var rightDecoration = LK.getAsset('waterball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); rightDecoration.tint = 0x3498db; rightDecoration.x = 300; waveMessage.addChild(rightDecoration); // Add to game game.addChild(waveMessage); // Initial state for animation waveMessage.alpha = 0; waveMessage.scaleX = 0.5; waveMessage.scaleY = 0.5; waveStartText.scaleX = 0.8; waveStartText.scaleY = 0.8; // Animate message entrance tween(waveMessage, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); // Animate text pulsing tween(waveStartText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(waveStartText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300 }); } }); // Animate decorations spinning tween(leftDecoration, { rotation: Math.PI * 2 }, { duration: 1000 }); tween(rightDecoration, { rotation: -Math.PI * 2 }, { duration: 1000 }); // Remove message after 2 seconds LK.setTimeout(function () { tween(waveMessage, { alpha: 0, scaleY: 0 }, { duration: 400, onFinish: function onFinish() { waveMessage.destroy(); } }); }, 2000); } function showWaveCompletionMessage() { // Create wave completion message container var completionMessage = new Container(); completionMessage.x = 1024; completionMessage.y = 1366; // Create background for message var messageBg = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 14, scaleY: 4 }); messageBg.tint = 0x27ae60; messageBg.alpha = 0.9; completionMessage.addChild(messageBg); // Create wave completion text var completionText = new Text2('WAVE ' + (wave - 1) + ' COMPLETED!', { size: 70, fill: 0xffffff }); completionText.anchor.set(0.5, 0.5); completionMessage.addChild(completionText); // Create bonus text var bonusText = new Text2('BONUS: +' + (50 + Math.floor(coins * 0.1)) + ' COINS', { size: 50, fill: 0xf1c40f }); bonusText.anchor.set(0.5, 0.5); bonusText.y = 60; completionMessage.addChild(bonusText); // Create decorative victory elements var leftStar = LK.getAsset('waterball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); leftStar.tint = 0xf39c12; leftStar.x = -350; completionMessage.addChild(leftStar); var rightStar = LK.getAsset('waterball', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); rightStar.tint = 0x9b59b6; rightStar.x = 350; completionMessage.addChild(rightStar); // Add sparkle effects for (var i = 0; i < 6; i++) { var sparkle = LK.getAsset('watercharc', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, scaleY: 0.2 }); sparkle.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6, 0xe67e22][i]; sparkle.alpha = 0.8; sparkle.x = -200 + i * 80; sparkle.y = -80 + Math.sin(i) * 40; completionMessage.addChild(sparkle); // Animate sparkles with rotation and scaling tween(sparkle, { rotation: Math.PI * 2, scaleX: 0.4, scaleY: 0.4 }, { duration: 1500, easing: tween.easeInOut }); } // Add to game game.addChild(completionMessage); // Initial state for animation completionMessage.alpha = 0; completionMessage.scaleX = 0.3; completionMessage.scaleY = 0.3; completionText.scaleX = 0.5; completionText.scaleY = 0.5; // Animate message entrance with bounce effect tween(completionMessage, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeOut }); // Animate main text with celebration effect tween(completionText, { scaleX: 1.3, scaleY: 1.3, tint: 0x2ecc71 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(completionText, { scaleX: 1.0, scaleY: 1.0, tint: 0xffffff }, { duration: 400 }); } }); // Animate bonus text pulsing tween(bonusText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(bonusText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300 }); } }); // Animate stars spinning and scaling tween(leftStar, { rotation: Math.PI * 3, scaleX: 0.8, scaleY: 0.8 }, { duration: 2000, easing: tween.easeInOut }); tween(rightStar, { rotation: -Math.PI * 3, scaleX: 0.8, scaleY: 0.8 }, { duration: 2000, easing: tween.easeInOut }); // Remove message after 2.5 seconds LK.setTimeout(function () { tween(completionMessage, { alpha: 0, scaleY: 0 }, { duration: 500, onFinish: function onFinish() { completionMessage.destroy(); } }); }, 2500); } // Event handlers startWaveButton.down = function () { // Add touch animation - scale down then back up tween(startWaveButton, { scaleX: 0.8, scaleY: 0.8, tint: 0x00AAFF }, { duration: 100, onFinish: function onFinish() { tween(startWaveButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); startWave(); }; basicTowerButton.down = function () { // Add touch animation - scale down and tint, then back up tween(basicTowerButton, { scaleX: 0.8, scaleY: 0.8, tint: 0x00FF00 }, { duration: 100, onFinish: function onFinish() { tween(basicTowerButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); selectedTowerType = 'basic'; showTowerMenu = false; }; splashTowerButton.down = function () { // Add touch animation - scale down and tint, then back up tween(splashTowerButton, { scaleX: 0.8, scaleY: 0.8, tint: 0xe74c3c }, { duration: 100, onFinish: function onFinish() { tween(splashTowerButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); selectedTowerType = 'splash'; showTowerMenu = false; }; slowTowerButton.down = function () { // Add touch animation - scale down and tint, then back up tween(slowTowerButton, { scaleX: 0.8, scaleY: 0.8, tint: 0x9b59b6 }, { duration: 100, onFinish: function onFinish() { tween(slowTowerButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); selectedTowerType = 'slow'; showTowerMenu = false; }; sniperTowerButton.down = function () { // Add touch animation - scale down and tint, then back up tween(sniperTowerButton, { scaleX: 0.8, scaleY: 0.8, tint: 0x2ecc71 }, { duration: 100, onFinish: function onFinish() { tween(sniperTowerButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); selectedTowerType = 'sniper'; showTowerMenu = false; }; pauseButton.down = function () { // Add touch animation tween(pauseButton, { scaleX: 0.8, scaleY: 0.8, tint: 0x00AAFF }, { duration: 100, onFinish: function onFinish() { tween(pauseButton, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 150 }); } }); showExitButton(); }; speedButton.down = function () { // Toggle speed between x1 and x2 gameSpeed = gameSpeed === 1 ? 2 : 1; speedButton.setText('x' + gameSpeed); speedButton.tint = gameSpeed === 2 ? 0xFF6600 : 0x00FF00; // Add touch animation tween(speedButton, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, onFinish: function onFinish() { tween(speedButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 150 }); } }); }; shovelButton.down = function () { // Toggle shovel mode shovelMode = !shovelMode; shovelButton.tint = shovelMode ? 0xFF0000 : 0xFFFFFF; shovelButton.setText(shovelMode ? '🔨' : '🔨'); // Add touch animation tween(shovelButton, { scaleX: 0.8, scaleY: 0.8 }, { duration: 100, onFinish: function onFinish() { tween(shovelButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 150 }); } }); }; game.down = function (x, y, obj) { // Don't allow interactions during home screen if (gameState === 'home') { return; } if (showTowerMenu) { showTowerMenu = false; selectedTower = null; return; } // Prevent interactions during wave if (waveInProgress) { return; } // Handle shovel mode - eliminate towers if (shovelMode) { var gridPos = getGridPosition(x, y); if (gridPos && gridPos.occupied) { // Find and remove the tower at this position for (var i = towers.length - 1; i >= 0; i--) { var tower = towers[i]; var towerGridX = Math.floor(tower.x / gridSize); var towerGridY = Math.floor(tower.y / gridSize); var clickGridX = Math.floor(x / gridSize); var clickGridY = Math.floor(y / gridSize); if (towerGridX === clickGridX && towerGridY === clickGridY) { // Give back half the tower cost var refund = Math.floor(tower.cost * 0.5); coins += refund; // Mark grid position as unoccupied gridPos.occupied = false; // Remove tower from arrays and game tower.destroy(); towers.splice(i, 1); // Flash effect to show elimination LK.effects.flashObject(tower, 0xFF0000, 200); updateUI(); break; } } } return; } var gridPos = getGridPosition(x, y); if (canPlaceTower(gridPos)) { var tower = new Tower(selectedTowerType); if (coins >= tower.cost) { coins -= tower.cost; tower.x = gridPos.x; tower.y = gridPos.y; gridPos.occupied = true; towers.push(tower); game.addChild(tower); updateUI(); } } }; game.update = function () { // Don't update game logic if on home screen if (gameState === 'home') { return; } // Don't update game logic if paused if (isPaused) { return; } // Animate coins text with rainbow colors if (LK.ticks % 30 === 0) { animateCoinsText(); } // Spawn enemies during wave if (waveInProgress && enemiesSpawned < enemiesPerWave) { spawnTimer += gameSpeed; if (spawnTimer >= 60) { spawnEnemy(); spawnTimer = 0; } } // Reset kill streak if lives were lost if (lives < lastLives) { killStreak = 0; lastLives = lives; } // Check if wave is complete if (waveInProgress && enemiesSpawned >= enemiesPerWave && enemies.length === 0) { waveInProgress = false; wave++; // Wave completion bonus + interest on current money var waveBonus = 50; var interestBonus = Math.floor(coins * 0.1); // 10% interest coins += waveBonus + interestBonus; // Display wave completion message showWaveCompletionMessage(); updateUI(); } // Clean up bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (!bullet.parent) { bullets.splice(i, 1); } } // Clean up water damage areas for (var i = waterDamageAreas.length - 1; i >= 0; i--) { var waterArea = waterDamageAreas[i]; if (!waterArea.parent) { waterDamageAreas.splice(i, 1); } } // Check win condition if (wave > 20) { LK.showYouWin(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (damage, target, assetType) {
var self = Container.call(this);
var bulletAsset = assetType || 'bullet';
var bulletGraphics = self.attachAsset(bulletAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = damage || 10;
self.target = target;
self.speed = 8;
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 < 10) {
self.target.takeDamage(self.damage);
// If this is a slow bullet (iceball), apply slow effect
if (bulletAsset === 'iceball') {
self.target.slowed = true;
self.target.slowDuration = 240; // 4 seconds at 60fps
// Apply ice tint effect to show enemy is slowed
tween(self.target, {
tint: 0x87CEEB
}, {
duration: 200
});
}
// If this is a splash bullet, create persistent water damage area
if (self.isSplash) {
// Apply initial splash damage to nearby enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var splashDx = enemy.x - self.target.x;
var splashDy = enemy.y - self.target.y;
var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
if (splashDistance <= 80 && enemy !== self.target) {
enemy.takeDamage(Math.floor(self.damage * 0.5)); // Half damage for splash
}
}
// Only create persistent water damage area if flag is set (after 10 shots)
if (self.createWaterDamage) {
var waterDamage = new WaterDamage(self.target.x, self.target.y, Math.floor(self.damage * 0.3));
waterDamageAreas.push(waterDamage);
game.addChild(waterDamage);
}
}
self.destroy();
return;
}
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed * gameSpeed;
self.y += Math.sin(angle) * self.speed * gameSpeed;
// Add rainbow color cycling to bullets for colorful effect
var rainbowColors = [0xFF0000, 0xFF8C00, 0xFFD700, 0x00FF00, 0x00BFFF, 0x8A2BE2];
var colorIndex = Math.floor(LK.ticks / 10 % rainbowColors.length);
bulletGraphics.tint = rainbowColors[colorIndex];
};
return self;
});
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
self.enemyType = enemyType || 'basic';
self.pathIndex = 0;
self.health = 30;
self.maxHealth = 30;
self.speed = 2;
self.reward = 10;
self.slowed = false;
self.slowDuration = 0;
var enemyGraphics;
if (self.enemyType === 'basic') {
enemyGraphics = self.attachAsset('basicEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
// Increase health every 10 waves
var healthBonus = Math.floor(wave / 10) * 10; // +10 health per 10 waves
self.health = 30 + healthBonus;
self.maxHealth = 30 + healthBonus;
self.speed = 2;
self.reward = 10;
} else if (self.enemyType === 'fast') {
enemyGraphics = self.attachAsset('fastEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 15;
self.maxHealth = 15;
self.speed = 4;
self.reward = 15;
} else if (self.enemyType === 'tank') {
enemyGraphics = self.attachAsset('tankEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 60;
self.maxHealth = 60;
self.speed = 1;
self.reward = 25;
} else if (self.enemyType === 'gold') {
enemyGraphics = self.attachAsset('goldEnemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
self.speed = 3;
self.reward = 100; // High reward!
}
// Create health bar background
var healthBarBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1
});
healthBarBg.y = -enemyGraphics.height / 2 - 20;
healthBarBg.tint = 0x000000;
self.addChild(healthBarBg);
// Create health bar foreground
var healthBarFg = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.1
});
healthBarFg.y = -enemyGraphics.height / 2 - 20;
healthBarFg.tint = 0x00FF00;
self.addChild(healthBarFg);
// Create health text to display current health as number
var healthText = new Text2(self.health.toString(), {
size: 30,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.y = -enemyGraphics.height / 2 - 20;
self.addChild(healthText);
self.healthBarBg = healthBarBg;
self.healthBarFg = healthBarFg;
self.healthText = healthText;
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
self.healthBarFg.scaleX = 0.4 * healthPercent;
// Update health text to show current health number
self.healthText.setText(Math.max(0, self.health).toString());
// Change color based on health
if (healthPercent > 0.6) {
self.healthBarFg.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
self.healthBarFg.tint = 0xFFFF00; // Yellow
} else {
self.healthBarFg.tint = 0xFF0000; // Red
}
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
// Add colorful damage flash effect
var damageFlashColors = [0xFF4444, 0xFF8844, 0xFFFF44, 0x44FF44, 0x4444FF, 0xFF44FF];
var flashColor = damageFlashColors[Math.floor(Math.random() * damageFlashColors.length)];
tween(enemyGraphics, {
tint: flashColor,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0xFFFFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 180
});
}
});
LK.getSound('enemyHit').play();
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Basic enemies give 2 coins, others use their reward value
var coinReward = self.enemyType === 'basic' ? 2 : self.reward;
coins += coinReward;
enemiesKilled++;
killStreak++;
LK.getSound('enemyDeath').play();
for (var i = 0; i < enemies.length; i++) {
if (enemies[i] === self) {
enemies.splice(i, 1);
break;
}
}
self.destroy();
updateUI();
};
self.update = function () {
if (self.slowed) {
self.slowDuration--;
if (self.slowDuration <= 0) {
self.slowed = false;
// Remove ice tint when slow effect expires
tween(self, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
}
if (self.pathIndex >= pathPoints.length - 1) {
// Game over immediately when any enemy reaches the end
showCustomGameOver();
return;
}
var currentSpeed = self.slowed ? self.speed * 0.5 : self.speed;
currentSpeed *= gameSpeed; // Apply game speed multiplier
var target = pathPoints[self.pathIndex + 1];
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
} else {
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * currentSpeed;
self.y += Math.sin(angle) * currentSpeed;
}
};
// Initialize health bar display
self.updateHealthBar();
return self;
});
var Tower = Container.expand(function (towerType) {
var self = Container.call(this);
self.towerType = towerType || 'basic';
self.level = 1;
self.range = 120;
self.damage = 10;
self.fireRate = 60;
self.cost = 50;
self.upgradeCost = 30;
self.lastShot = 0;
self.shotCount = 0; // Track number of shots for splash tower
var towerGraphics;
if (self.towerType === 'basic') {
towerGraphics = self.attachAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.range = 620;
self.fireRate = 60;
self.cost = 50;
} else if (self.towerType === 'splash') {
towerGraphics = self.attachAsset('splashTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 15;
self.range = 100;
self.fireRate = 90;
self.cost = 130;
} else if (self.towerType === 'slow') {
towerGraphics = self.attachAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 25;
self.range = 140;
self.fireRate = 30;
self.cost = 200;
} else if (self.towerType === 'sniper') {
towerGraphics = self.attachAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.damage = 60;
self.range = 1000;
self.fireRate = 300;
self.cost = 30;
}
self.canShoot = function () {
return LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed);
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range && distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
if (!self.canShoot()) return;
// Add colorful muzzle flash effect when shooting
var flashColors = {
'basic': 0xFFD700,
'splash': 0x00AAFF,
'slow': 0x87CEEB,
'sniper': 0xFF4500
};
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2,
tint: flashColors[self.towerType] || 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
if (self.towerType === 'basic') {
// Fire single bullet for basic tower
var bullet = new Bullet(self.damage, target);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'splash') {
// Splash tower fires waterball bullets and leaves watercharc damage
var bullet = new Bullet(self.damage, target, 'waterball');
bullet.x = self.x;
bullet.y = self.y;
bullet.isSplash = true; // Mark as splash bullet
self.shotCount++; // Increment shot counter
// Only create water damage after 10 shots
if (self.shotCount >= 10) {
bullet.createWaterDamage = true;
self.shotCount = 0; // Reset counter
}
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'slow') {
// Slow tower fires iceball bullets
var bullet = new Bullet(self.damage, target, 'iceball');
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else if (self.towerType === 'sniper') {
// Sniper tower fires sniperball bullets
var bullet = new Bullet(self.damage, target, 'sniperball');
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
} else {
// Other tower types fire single bullet
var bullet = new Bullet(self.damage, target);
bullet.x = self.x;
bullet.y = self.y;
bullets.push(bullet);
game.addChild(bullet);
}
self.lastShot = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (coins >= self.upgradeCost && self.level < 3) {
coins -= self.upgradeCost;
self.level++;
self.damage = Math.floor(self.damage * 1.5);
self.range += 20;
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
updateUI();
}
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.shoot(target);
}
};
self.down = function (x, y, obj) {
selectedTower = self;
showTowerMenu = true;
updateUI();
};
return self;
});
var WaterDamage = Container.expand(function (x, y, damage) {
var self = Container.call(this);
var waterGraphics = self.attachAsset('watercharc', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.damage = damage || 5; // Damage per tick
self.radius = 80; // Damage radius
self.lifetime = 300; // 5 seconds at 60fps
self.damageInterval = 30; // Damage every 0.5 seconds
self.lastDamageTick = 0;
waterGraphics.alpha = 0.7;
waterGraphics.tint = 0x00AAFF;
// Animate the water area appearance
waterGraphics.scaleX = 0.5;
waterGraphics.scaleY = 0.5;
tween(waterGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
self.update = function () {
self.lifetime--;
// Apply damage to enemies in range at intervals
if (LK.ticks - self.lastDamageTick >= self.damageInterval) {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.radius) {
enemy.takeDamage(self.damage);
}
}
self.lastDamageTick = LK.ticks;
}
// Fade out as lifetime decreases
if (self.lifetime < 60) {
// Start fading in last second
waterGraphics.alpha = 0.7 * (self.lifetime / 60);
}
// Remove when lifetime expires
if (self.lifetime <= 0) {
self.destroy();
// Remove from waterDamageAreas array
for (var j = waterDamageAreas.length - 1; j >= 0; j--) {
if (waterDamageAreas[j] === self) {
waterDamageAreas.splice(j, 1);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8b4513 // Brown color
});
/****
* Game Code
****/
// Game state management
var gameState = 'home'; // 'home', 'playing'
var homeScreen = null;
var gridSize = 100;
var gridWidth = 20;
var gridHeight = 27;
var pathPoints = [{
x: 0,
y: 400
}, {
x: 300,
y: 400
}, {
x: 300,
y: 800
}, {
x: 700,
y: 800
}, {
x: 700,
y: 1200
}, {
x: 1100,
y: 1200
}, {
x: 1100,
y: 1600
}, {
x: 1500,
y: 1600
}, {
x: 1500,
y: 2000
}, {
x: 2048,
y: 2000
}];
var gameGrid = [];
var towers = [];
var enemies = [];
var bullets = [];
var waterDamageAreas = [];
var coins = 100;
var lives = 20;
var wave = 1;
var enemiesKilled = 0;
var killStreak = 0;
var lastLives = 20;
var enemiesSpawned = 0;
var enemiesPerWave = 10;
var spawnTimer = 0;
var waveInProgress = false;
var selectedTower = null;
var showTowerMenu = false;
var towerMenuButtons = [];
// Create grid
for (var y = 0; y < gridHeight; y++) {
gameGrid[y] = [];
for (var x = 0; x < gridWidth; x++) {
gameGrid[y][x] = {
x: x * gridSize + 50,
y: y * gridSize + 50,
occupied: false,
isPath: false
};
}
}
// Mark path tiles
for (var i = 0; i < pathPoints.length - 1; i++) {
var start = pathPoints[i];
var end = pathPoints[i + 1];
var steps = Math.max(Math.abs(end.x - start.x), Math.abs(end.y - start.y)) / gridSize;
for (var step = 0; step <= steps; step++) {
var x = Math.floor((start.x + (end.x - start.x) * step / steps) / gridSize);
var y = Math.floor((start.y + (end.y - start.y) * step / steps) / gridSize);
if (x >= 0 && x < gridWidth && y >= 0 && y < gridHeight) {
gameGrid[y][x].isPath = true;
}
}
}
// Create visual grid
for (var y = 0; y < gridHeight; y++) {
for (var x = 0; x < gridWidth; x++) {
var tile;
if (gameGrid[y][x].isPath) {
tile = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
tile = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5
});
}
tile.x = gameGrid[y][x].x;
tile.y = gameGrid[y][x].y;
tile.alpha = 0.3;
game.addChild(tile);
}
}
// UI Elements
var coinsText = new Text2('Coins: ' + coins, {
size: 60,
fill: 0xFFD700
});
coinsText.anchor.set(1, 0);
LK.gui.topRight.addChild(coinsText);
// Add continuous rainbow animation to coins text
function animateCoinsText() {
var rainbowColors = [0xFFD700, 0xFF6B35, 0xF7931E, 0xFFD700, 0x00FF7F, 0x1E90FF, 0x9370DB];
var colorIndex = Math.floor(LK.ticks / 30 % rainbowColors.length);
tween(coinsText, {
tint: rainbowColors[colorIndex],
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
onFinish: function onFinish() {
tween(coinsText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 200
});
}
});
}
var livesText = new Text2('Lives: ' + lives, {
size: 50,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 0);
LK.gui.topRight.addChild(livesText);
var waveText = new Text2('Wave: ' + wave, {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
var startWaveButton = new Text2('🚀 START WAVE 🚀', {
size: 80,
fill: 0x00FF00
});
startWaveButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(startWaveButton);
// Add colorful tween animation to make it more eye-catching
function animateStartButton() {
tween(startWaveButton, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onFinish() {
tween(startWaveButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
// Show home screen instead of starting immediately
createHomeScreen();
// Tower selection buttons
var basicTowerButton = new Text2('Basic ($50)', {
size: 80,
fill: 0xFFFFFF
});
basicTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(basicTowerButton);
var splashTowerButton = new Text2('Splash ($130)', {
size: 80,
fill: 0xFFFFFF
});
splashTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(splashTowerButton);
var slowTowerButton = new Text2('Slow ($200)', {
size: 80,
fill: 0xFFFFFF
});
slowTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(slowTowerButton);
var sniperTowerButton = new Text2('Sniper ($30)', {
size: 80,
fill: 0xFFFFFF
});
sniperTowerButton.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(sniperTowerButton);
// Create shovel button for tower elimination
var shovelButton = new Text2('🔨', {
size: 120,
fill: 0xFFFFFF
});
shovelButton.anchor.set(0.5, 0.5);
LK.gui.right.addChild(shovelButton);
// Position tower buttons
basicTowerButton.y = -400;
splashTowerButton.y = -300;
slowTowerButton.y = -200;
sniperTowerButton.y = -100;
coinsText.y = 50;
livesText.y = 100;
var selectedTowerType = 'basic';
var shovelMode = false;
var isPaused = false;
var pauseMenu = null;
var pauseButton = null;
var restartButton = null;
var exitButton = null;
var musicButton = null;
var musicEnabled = true;
var gameOverMenu = null;
function createHomeScreen() {
if (homeScreen) return; // Already created
homeScreen = new Container();
// Create animated background
var background = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 32,
scaleY: 45
});
background.tint = 0x2c3e50;
background.alpha = 0.95;
background.x = 1024;
background.y = 1366;
homeScreen.addChild(background);
// Create game title with shadow effect
var shadowTitle = new Text2('TOWER DEFENSE', {
size: 160,
fill: 0x000000
});
shadowTitle.anchor.set(0.5, 0.5);
shadowTitle.x = 1029; // Slight offset for shadow
shadowTitle.y = 505;
homeScreen.addChild(shadowTitle);
var gameTitle = new Text2('TOWER DEFENSE', {
size: 160,
fill: 0xe74c3c
});
gameTitle.anchor.set(0.5, 0.5);
gameTitle.x = 1024;
gameTitle.y = 500;
homeScreen.addChild(gameTitle);
// Create subtitle
var subtitle = new Text2('Defend Your Path!', {
size: 80,
fill: 0x3498db
});
subtitle.anchor.set(0.5, 0.5);
subtitle.x = 1024;
subtitle.y = 650;
homeScreen.addChild(subtitle);
// Create start button
var startBg = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 2
});
startBg.tint = 0x27ae60;
startBg.x = 1024;
startBg.y = 900;
homeScreen.addChild(startBg);
var startButton = new Text2('🎮 START GAME', {
size: 90,
fill: 0xffffff
});
startButton.anchor.set(0.5, 0.5);
startButton.x = 1024;
startButton.y = 900;
homeScreen.addChild(startButton);
// Create instructions
var instructionsTitle = new Text2('HOW TO PLAY:', {
size: 60,
fill: 0xf39c12
});
instructionsTitle.anchor.set(0.5, 0.5);
instructionsTitle.x = 1024;
instructionsTitle.y = 1200;
homeScreen.addChild(instructionsTitle);
var instruction1 = new Text2('• Tap tower buttons to select type', {
size: 45,
fill: 0xffffff
});
instruction1.anchor.set(0.5, 0.5);
instruction1.x = 1024;
instruction1.y = 1300;
homeScreen.addChild(instruction1);
var instruction2 = new Text2('• Tap empty grass to place towers', {
size: 45,
fill: 0xffffff
});
instruction2.anchor.set(0.5, 0.5);
instruction2.x = 1024;
instruction2.y = 1350;
homeScreen.addChild(instruction2);
var instruction3 = new Text2('• Stop enemies from reaching the end!', {
size: 45,
fill: 0xffffff
});
instruction3.anchor.set(0.5, 0.5);
instruction3.x = 1024;
instruction3.y = 1400;
homeScreen.addChild(instruction3);
var instruction4 = new Text2('• Use speed x2 button to fast forward', {
size: 45,
fill: 0xffffff
});
instruction4.anchor.set(0.5, 0.5);
instruction4.x = 1024;
instruction4.y = 1450;
homeScreen.addChild(instruction4);
// Create decorative tower icons
var basicIcon = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
basicIcon.x = 300;
basicIcon.y = 1600;
homeScreen.addChild(basicIcon);
var splashIcon = LK.getAsset('splashTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
splashIcon.x = 700;
splashIcon.y = 1600;
homeScreen.addChild(splashIcon);
var slowIcon = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
slowIcon.x = 1300;
slowIcon.y = 1600;
homeScreen.addChild(slowIcon);
var sniperIcon = LK.getAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
sniperIcon.x = 1700;
sniperIcon.y = 1600;
homeScreen.addChild(sniperIcon);
// Add floating animation to icons
tween(basicIcon, {
y: basicIcon.y + 20
}, {
duration: 2000,
loop: true,
yoyo: true
});
tween(splashIcon, {
y: splashIcon.y + 20
}, {
duration: 2200,
loop: true,
yoyo: true
});
tween(slowIcon, {
y: slowIcon.y + 20
}, {
duration: 2400,
loop: true,
yoyo: true
});
tween(sniperIcon, {
y: sniperIcon.y + 20
}, {
duration: 2600,
loop: true,
yoyo: true
});
// Add button event handler
startButton.down = function () {
tween(startButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
startGame();
}
});
};
// Animate title pulsing
function animateTitle() {
tween(gameTitle, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xff6b6b
}, {
duration: 2000,
onFinish: function onFinish() {
tween(gameTitle, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 2000,
onFinish: animateTitle
});
}
});
}
animateTitle();
// Animate start button
function animateStartButton() {
tween(startButton, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1500,
onFinish: function onFinish() {
tween(startButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1500,
onFinish: animateStartButton
});
}
});
}
animateStartButton();
game.addChild(homeScreen);
}
function startGame() {
gameState = 'playing';
if (homeScreen) {
homeScreen.destroy();
homeScreen = null;
}
// Start playing background music
LK.playMusic('bgmusic', {
volume: 1.0
});
}
function showCustomGameOver() {
if (gameOverMenu) return; // Already showing
isPaused = true;
// Create game over menu container
gameOverMenu = new Container();
// Create animated background with gradient effect
var background1 = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 32,
scaleY: 45
});
background1.tint = 0x1a1a2e;
background1.alpha = 0.95;
background1.x = 1024;
background1.y = 1366;
gameOverMenu.addChild(background1);
// Create decorative border elements
var borderTop = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 1
});
borderTop.tint = 0xe74c3c;
borderTop.x = 1024;
borderTop.y = 300;
gameOverMenu.addChild(borderTop);
var borderBottom = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 1
});
borderBottom.tint = 0xe74c3c;
borderBottom.x = 1024;
borderBottom.y = 2400;
gameOverMenu.addChild(borderBottom);
// Create large "GAME OVER" title with shadow effect
var shadowText = new Text2('GAME OVER', {
size: 140,
fill: 0x000000
});
shadowText.anchor.set(0.5, 0.5);
shadowText.x = 1029; // Slight offset for shadow
shadowText.y = 605;
gameOverMenu.addChild(shadowText);
var titleText = new Text2('GAME OVER', {
size: 140,
fill: 0xe74c3c
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
gameOverMenu.addChild(titleText);
// Create colorful stats display
var statsContainer = new Container();
// Stats background
var statsBg = LK.getAsset('slowTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 8
});
statsBg.tint = 0x16213e;
statsBg.alpha = 0.8;
statsBg.x = 1024;
statsBg.y = 1000;
statsContainer.addChild(statsBg);
// Wave reached text
var waveReachedText = new Text2('Wave Reached: ' + wave, {
size: 60,
fill: 0x3498db
});
waveReachedText.anchor.set(0.5, 0.5);
waveReachedText.x = 1024;
waveReachedText.y = 850;
statsContainer.addChild(waveReachedText);
// Enemies killed text
var killsText = new Text2('Enemies Defeated: ' + enemiesKilled, {
size: 60,
fill: 0x2ecc71
});
killsText.anchor.set(0.5, 0.5);
killsText.x = 1024;
killsText.y = 950;
statsContainer.addChild(killsText);
// Final score text
var finalScore = wave * 100 + enemiesKilled * 10;
var scoreText = new Text2('Final Score: ' + finalScore, {
size: 60,
fill: 0xf39c12
});
scoreText.anchor.set(0.5, 0.5);
scoreText.x = 1024;
scoreText.y = 1050;
statsContainer.addChild(scoreText);
// Best streak text
var streakText = new Text2('Best Streak: ' + killStreak, {
size: 60,
fill: 0x9b59b6
});
streakText.anchor.set(0.5, 0.5);
streakText.x = 1024;
streakText.y = 1150;
statsContainer.addChild(streakText);
gameOverMenu.addChild(statsContainer);
// Create animated restart button with glow effect
var restartBg = LK.getAsset('basicTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
restartBg.tint = 0x27ae60;
restartBg.x = 1024;
restartBg.y = 1400;
gameOverMenu.addChild(restartBg);
var restartGameButton = new Text2('🔄 PLAY AGAIN', {
size: 70,
fill: 0xffffff
});
restartGameButton.anchor.set(0.5, 0.5);
restartGameButton.x = 1024;
restartGameButton.y = 1400;
gameOverMenu.addChild(restartGameButton);
// Create animated exit button
var exitBg = LK.getAsset('sniperTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1.5
});
exitBg.tint = 0xc0392b;
exitBg.x = 1024;
exitBg.y = 1600;
gameOverMenu.addChild(exitBg);
var exitGameButton = new Text2('❌ EXIT GAME', {
size: 70,
fill: 0xffffff
});
exitGameButton.anchor.set(0.5, 0.5);
exitGameButton.x = 1024;
exitGameButton.y = 1600;
gameOverMenu.addChild(exitGameButton);
// Add floating decorative elements
for (var i = 0; i < 8; i++) {
var decoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
decoration.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6][i % 5];
decoration.alpha = 0.6;
decoration.x = 200 + i * 225;
decoration.y = 400 + Math.sin(i) * 100;
gameOverMenu.addChild(decoration);
// Animate decorations
tween(decoration, {
y: decoration.y + 50,
rotation: Math.PI * 2
}, {
duration: 2000 + i * 200,
loop: true,
yoyo: true
});
}
// Add button event handlers
restartGameButton.down = function () {
tween(restartGameButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
LK.showGameOver();
}
});
};
exitGameButton.down = function () {
tween(exitGameButton, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0xe74c3c
}, {
duration: 100,
onFinish: function onFinish() {
LK.showGameOver();
}
});
};
// Animate title text with pulsing effect
function animateTitle() {
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xff6b6b
}, {
duration: 1000,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 1000,
onFinish: animateTitle
});
}
});
}
animateTitle();
// Animate stats container entrance
statsContainer.alpha = 0;
statsContainer.scaleX = 0.5;
statsContainer.scaleY = 0.5;
tween(statsContainer, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
delay: 500
});
game.addChild(gameOverMenu);
}
// Create pause button in top right corner
pauseButton = new Text2('⏸', {
size: 60,
fill: 0xFFFFFF
});
pauseButton.anchor.set(0, 0);
pauseButton.x = 150; // Offset from top right to avoid overlap with coins
pauseButton.y = 0;
LK.gui.topRight.addChild(pauseButton);
// Create speed x2 button
var speedButton = new Text2('x1', {
size: 50,
fill: 0x00FF00
});
speedButton.anchor.set(0.5, 0);
speedButton.x = 0; // Center horizontally
speedButton.y = 80; // Position below wave text
LK.gui.top.addChild(speedButton);
// Game speed state
var gameSpeed = 1;
function createPauseMenu() {
if (pauseMenu) return; // Already created
// Create semi-transparent background
pauseMenu = new Container();
var background = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 30,
scaleY: 40
});
background.tint = 0x000000;
background.alpha = 0.7;
background.x = 1024;
background.y = 1366;
pauseMenu.addChild(background);
// Create menu title
var titleText = new Text2('PAUSED', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
pauseMenu.addChild(titleText);
// Create restart button
restartButton = new Text2('RESTART', {
size: 60,
fill: 0x00FF00
});
restartButton.anchor.set(0.5, 0.5);
restartButton.x = 1024;
restartButton.y = 1000;
pauseMenu.addChild(restartButton);
// Create exit button
exitButton = new Text2('EXIT', {
size: 60,
fill: 0xFF0000
});
exitButton.anchor.set(0.5, 0.5);
exitButton.x = 1024;
exitButton.y = 1200;
pauseMenu.addChild(exitButton);
// Create music toggle button
musicButton = new Text2(musicEnabled ? 'MUSIC: ON' : 'MUSIC: OFF', {
size: 60,
fill: 0xFFFFFF
});
musicButton.tint = musicEnabled ? 0x00FFFF : 0x888888;
musicButton.anchor.set(0.5, 0.5);
musicButton.x = 1024;
musicButton.y = 1400;
pauseMenu.addChild(musicButton);
// Create resume button
var resumeButton = new Text2('RESUME', {
size: 60,
fill: 0xFFFF00
});
resumeButton.anchor.set(0.5, 0.5);
resumeButton.x = 1024;
resumeButton.y = 1600;
pauseMenu.addChild(resumeButton);
// Add event handlers
restartButton.down = function () {
LK.showGameOver(); // Restart the game
};
exitButton.down = function () {
showCustomGameOver(); // Exit to custom game over screen
};
musicButton.down = function () {
musicEnabled = !musicEnabled;
if (musicEnabled) {
LK.playMusic('bgmusic', {
volume: 1.0
});
musicButton.setText('MUSIC: ON');
musicButton.tint = 0x00FFFF;
} else {
LK.stopMusic();
musicButton.setText('MUSIC: OFF');
musicButton.tint = 0x888888;
}
};
resumeButton.down = function () {
togglePause();
};
game.addChild(pauseMenu);
}
function togglePause() {
isPaused = !isPaused;
if (isPaused) {
createPauseMenu();
pauseMenu.visible = true;
} else {
if (pauseMenu) {
pauseMenu.visible = false;
}
}
}
function showExitButton() {
isPaused = true;
if (!exitButton) {
// Create exit button if it doesn't exist
exitButton = new Text2('EXIT GAME', {
size: 60,
fill: 0xFF0000
});
exitButton.anchor.set(0.5, 0.5);
exitButton.x = 1024;
exitButton.y = 1366;
exitButton.down = function () {
showCustomGameOver(); // Exit to custom game over screen
};
game.addChild(exitButton);
} else {
exitButton.visible = true;
}
}
function updateUI() {
var coinDisplay = 'Coins: ' + coins;
coinsText.setText(coinDisplay);
livesText.setText('Lives: ' + lives);
waveText.setText('Wave: ' + wave);
if (showTowerMenu && selectedTower) {
// Show upgrade option
}
}
function getGridPosition(x, y) {
var gridX = Math.floor(x / gridSize);
var gridY = Math.floor(y / gridSize);
if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
return gameGrid[gridY][gridX];
}
return null;
}
function canPlaceTower(gridPos) {
return gridPos && !gridPos.occupied && !gridPos.isPath;
}
function spawnEnemy() {
var enemyType = 'basic';
var rand = Math.random();
// Only basic enemies in wave 1
if (wave === 1) {
enemyType = 'basic';
} else {
// 5% chance for gold enemy (high reward, low health)
if (rand < 0.05) {
enemyType = 'gold';
} else if (wave > 5) {
if (rand < 0.3) enemyType = 'fast';else if (rand < 0.6) enemyType = 'tank';
} else if (wave > 2) {
if (rand < 0.2) enemyType = 'fast';
}
}
var enemy = new Enemy(enemyType);
enemy.x = pathPoints[0].x;
enemy.y = pathPoints[0].y;
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function startWave() {
if (waveInProgress) return;
if (waveInProgress) return;
waveInProgress = true;
enemiesSpawned = 0;
enemiesPerWave = 10 + wave * 2;
spawnTimer = 0;
showTowerMenu = false;
selectedTower = null;
// Shovel mode can now be used continuously without resetting
// Display wave start message
showWaveStartMessage();
}
function showWaveStartMessage() {
// Create wave message container
var waveMessage = new Container();
waveMessage.x = 1024;
waveMessage.y = 1366;
// Create background for message
var messageBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 12,
scaleY: 3
});
messageBg.tint = 0x2c3e50;
messageBg.alpha = 0.9;
waveMessage.addChild(messageBg);
// Create wave start text
var waveStartText = new Text2('WAVE ' + wave + ' STARTED!', {
size: 80,
fill: 0xf39c12
});
waveStartText.anchor.set(0.5, 0.5);
waveMessage.addChild(waveStartText);
// Create decorative elements
var leftDecoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
leftDecoration.tint = 0xe74c3c;
leftDecoration.x = -300;
waveMessage.addChild(leftDecoration);
var rightDecoration = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
rightDecoration.tint = 0x3498db;
rightDecoration.x = 300;
waveMessage.addChild(rightDecoration);
// Add to game
game.addChild(waveMessage);
// Initial state for animation
waveMessage.alpha = 0;
waveMessage.scaleX = 0.5;
waveMessage.scaleY = 0.5;
waveStartText.scaleX = 0.8;
waveStartText.scaleY = 0.8;
// Animate message entrance
tween(waveMessage, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Animate text pulsing
tween(waveStartText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(waveStartText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
// Animate decorations spinning
tween(leftDecoration, {
rotation: Math.PI * 2
}, {
duration: 1000
});
tween(rightDecoration, {
rotation: -Math.PI * 2
}, {
duration: 1000
});
// Remove message after 2 seconds
LK.setTimeout(function () {
tween(waveMessage, {
alpha: 0,
scaleY: 0
}, {
duration: 400,
onFinish: function onFinish() {
waveMessage.destroy();
}
});
}, 2000);
}
function showWaveCompletionMessage() {
// Create wave completion message container
var completionMessage = new Container();
completionMessage.x = 1024;
completionMessage.y = 1366;
// Create background for message
var messageBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 14,
scaleY: 4
});
messageBg.tint = 0x27ae60;
messageBg.alpha = 0.9;
completionMessage.addChild(messageBg);
// Create wave completion text
var completionText = new Text2('WAVE ' + (wave - 1) + ' COMPLETED!', {
size: 70,
fill: 0xffffff
});
completionText.anchor.set(0.5, 0.5);
completionMessage.addChild(completionText);
// Create bonus text
var bonusText = new Text2('BONUS: +' + (50 + Math.floor(coins * 0.1)) + ' COINS', {
size: 50,
fill: 0xf1c40f
});
bonusText.anchor.set(0.5, 0.5);
bonusText.y = 60;
completionMessage.addChild(bonusText);
// Create decorative victory elements
var leftStar = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
leftStar.tint = 0xf39c12;
leftStar.x = -350;
completionMessage.addChild(leftStar);
var rightStar = LK.getAsset('waterball', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4
});
rightStar.tint = 0x9b59b6;
rightStar.x = 350;
completionMessage.addChild(rightStar);
// Add sparkle effects
for (var i = 0; i < 6; i++) {
var sparkle = LK.getAsset('watercharc', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2
});
sparkle.tint = [0xe74c3c, 0x3498db, 0x2ecc71, 0xf39c12, 0x9b59b6, 0xe67e22][i];
sparkle.alpha = 0.8;
sparkle.x = -200 + i * 80;
sparkle.y = -80 + Math.sin(i) * 40;
completionMessage.addChild(sparkle);
// Animate sparkles with rotation and scaling
tween(sparkle, {
rotation: Math.PI * 2,
scaleX: 0.4,
scaleY: 0.4
}, {
duration: 1500,
easing: tween.easeInOut
});
}
// Add to game
game.addChild(completionMessage);
// Initial state for animation
completionMessage.alpha = 0;
completionMessage.scaleX = 0.3;
completionMessage.scaleY = 0.3;
completionText.scaleX = 0.5;
completionText.scaleY = 0.5;
// Animate message entrance with bounce effect
tween(completionMessage, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut
});
// Animate main text with celebration effect
tween(completionText, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x2ecc71
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(completionText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xffffff
}, {
duration: 400
});
}
});
// Animate bonus text pulsing
tween(bonusText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(bonusText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
});
// Animate stars spinning and scaling
tween(leftStar, {
rotation: Math.PI * 3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut
});
tween(rightStar, {
rotation: -Math.PI * 3,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 2000,
easing: tween.easeInOut
});
// Remove message after 2.5 seconds
LK.setTimeout(function () {
tween(completionMessage, {
alpha: 0,
scaleY: 0
}, {
duration: 500,
onFinish: function onFinish() {
completionMessage.destroy();
}
});
}, 2500);
}
// Event handlers
startWaveButton.down = function () {
// Add touch animation - scale down then back up
tween(startWaveButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(startWaveButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
startWave();
};
basicTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(basicTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00FF00
}, {
duration: 100,
onFinish: function onFinish() {
tween(basicTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'basic';
showTowerMenu = false;
};
splashTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(splashTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0xe74c3c
}, {
duration: 100,
onFinish: function onFinish() {
tween(splashTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'splash';
showTowerMenu = false;
};
slowTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(slowTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x9b59b6
}, {
duration: 100,
onFinish: function onFinish() {
tween(slowTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'slow';
showTowerMenu = false;
};
sniperTowerButton.down = function () {
// Add touch animation - scale down and tint, then back up
tween(sniperTowerButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x2ecc71
}, {
duration: 100,
onFinish: function onFinish() {
tween(sniperTowerButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
selectedTowerType = 'sniper';
showTowerMenu = false;
};
pauseButton.down = function () {
// Add touch animation
tween(pauseButton, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x00AAFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(pauseButton, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
showExitButton();
};
speedButton.down = function () {
// Toggle speed between x1 and x2
gameSpeed = gameSpeed === 1 ? 2 : 1;
speedButton.setText('x' + gameSpeed);
speedButton.tint = gameSpeed === 2 ? 0xFF6600 : 0x00FF00;
// Add touch animation
tween(speedButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(speedButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150
});
}
});
};
shovelButton.down = function () {
// Toggle shovel mode
shovelMode = !shovelMode;
shovelButton.tint = shovelMode ? 0xFF0000 : 0xFFFFFF;
shovelButton.setText(shovelMode ? '🔨' : '🔨');
// Add touch animation
tween(shovelButton, {
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 100,
onFinish: function onFinish() {
tween(shovelButton, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 150
});
}
});
};
game.down = function (x, y, obj) {
// Don't allow interactions during home screen
if (gameState === 'home') {
return;
}
if (showTowerMenu) {
showTowerMenu = false;
selectedTower = null;
return;
}
// Prevent interactions during wave
if (waveInProgress) {
return;
}
// Handle shovel mode - eliminate towers
if (shovelMode) {
var gridPos = getGridPosition(x, y);
if (gridPos && gridPos.occupied) {
// Find and remove the tower at this position
for (var i = towers.length - 1; i >= 0; i--) {
var tower = towers[i];
var towerGridX = Math.floor(tower.x / gridSize);
var towerGridY = Math.floor(tower.y / gridSize);
var clickGridX = Math.floor(x / gridSize);
var clickGridY = Math.floor(y / gridSize);
if (towerGridX === clickGridX && towerGridY === clickGridY) {
// Give back half the tower cost
var refund = Math.floor(tower.cost * 0.5);
coins += refund;
// Mark grid position as unoccupied
gridPos.occupied = false;
// Remove tower from arrays and game
tower.destroy();
towers.splice(i, 1);
// Flash effect to show elimination
LK.effects.flashObject(tower, 0xFF0000, 200);
updateUI();
break;
}
}
}
return;
}
var gridPos = getGridPosition(x, y);
if (canPlaceTower(gridPos)) {
var tower = new Tower(selectedTowerType);
if (coins >= tower.cost) {
coins -= tower.cost;
tower.x = gridPos.x;
tower.y = gridPos.y;
gridPos.occupied = true;
towers.push(tower);
game.addChild(tower);
updateUI();
}
}
};
game.update = function () {
// Don't update game logic if on home screen
if (gameState === 'home') {
return;
}
// Don't update game logic if paused
if (isPaused) {
return;
}
// Animate coins text with rainbow colors
if (LK.ticks % 30 === 0) {
animateCoinsText();
}
// Spawn enemies during wave
if (waveInProgress && enemiesSpawned < enemiesPerWave) {
spawnTimer += gameSpeed;
if (spawnTimer >= 60) {
spawnEnemy();
spawnTimer = 0;
}
}
// Reset kill streak if lives were lost
if (lives < lastLives) {
killStreak = 0;
lastLives = lives;
}
// Check if wave is complete
if (waveInProgress && enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
waveInProgress = false;
wave++;
// Wave completion bonus + interest on current money
var waveBonus = 50;
var interestBonus = Math.floor(coins * 0.1); // 10% interest
coins += waveBonus + interestBonus;
// Display wave completion message
showWaveCompletionMessage();
updateUI();
}
// Clean up bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet.parent) {
bullets.splice(i, 1);
}
}
// Clean up water damage areas
for (var i = waterDamageAreas.length - 1; i >= 0; i--) {
var waterArea = waterDamageAreas[i];
if (!waterArea.parent) {
waterDamageAreas.splice(i, 1);
}
}
// Check win condition
if (wave > 20) {
LK.showYouWin();
}
};
a tower of a tower defence. In-Game asset. 2d. High contrast. No shadows
slime. In-Game asset. 2d. High contrast. No shadows
splash tower of tower defense. In-Game asset. 2d. High contrast. No shadows
water bullet. In-Game asset. 2d. High contrast. No shadows
puddle of water. In-Game asset. 2d. High contrast. No shadows
slow tower of tower defense. In-Game asset. 2d. High contrast. No shadows
ice ball. In-Game asset. 2d. High contrast. No shadows
the sniper tower of tower defense. In-Game asset. 2d. High contrast. No shadows
sniper bullet. In-Game asset. 2d. High contrast. No shadows
floor. In-Game asset. 2d. High contrast. No shadows
fast enemy. In-Game asset. 2d. High contrast. No shadows
tank. In-Game asset. 2d. High contrast. No shadows