User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '10')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
MENÜYÜ KALDIR
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '9')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '9')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '13')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '13')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '0')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '14')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '10')' in or related to this line: 'if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {' Line Number: 3242
User prompt
menü yap menüde istersek düşman olalım istersek normal olalım istersekte hileli olalım
User prompt
güçlü towere buf
User prompt
oyun 31 de bitsin ve 23. wavede yardım gelsin 99999999999 hasar veren bir tower bedava 1 adet
User prompt
30. seviyede 90000 altınımız olsun ve 90000 goldluk ultimate boss god toweri alabilelim
User prompt
başta destek gelsin bedava 1 güçlü kule versinler 567 hasar versin super tower inf hasar versin
User prompt
super tower 9999999999999 vursun
User prompt
21.wavede gold 4000 olacak
User prompt
oyun 22. wavede bitsin 21. wavede altınımız 4k olsun ve boss gelsin boss ultra olsun sizi yeneceğim diye bir animasyon başlar ve animasyon bitince boss fight başlasın ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
21 wavede boss gelsin ultra ve boss gelince paramız altın 4000 olsun çok güçlü olsun ve animasyonlu ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yeni animasyonlar ekle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught ReferenceError: levelTints is not defined' in or related to this line: 'LK.effects.flashObject(self, levelTints[self.level - 1], 1200);' Line Number: 988 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
oyuna waveyi atla ekle
User prompt
alabilelim super toweri istediğimiz zaman
User prompt
super kuleyi 4000 altına alabilelim çok güçlü olsun onu 5 kez geliştirebilelim ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: self.healSelf is not a function' in or related to this line: 'self.healSelf();' Line Number: 385
User prompt
10. waveden sonra ultra bosslar gelsin ve 20. wawede bir animasyon başlasın roketle yeni bir kule gelsin ve kule super kule olsun 5 aşaması olsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 25; self.target = null; self.hasHit = false; self.update = function () { if (!self.target || self.hasHit) 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) { if (self.isPiercing && self.hitTargets.indexOf(self.target) === -1) { // Piercing bullet hits target but continues self.target.takeDamage(self.damage); self.hitTargets.push(self.target); // Find next target var nextTarget = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead || enemy.reachedBase || self.hitTargets.indexOf(enemy) !== -1) continue; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < closestDistance && dist <= 200) { closestDistance = dist; nextTarget = enemy; } } if (nextTarget) { self.target = nextTarget; } else { self.hasHit = true; } } else if (self.isSplash) { // Splash damage to all enemies in radius for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead || enemy.reachedBase) continue; var splashDx = enemy.x - self.x; var splashDy = enemy.y - self.y; var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy); if (splashDistance <= self.splashRadius) { enemy.takeDamage(self.damage); } } self.hasHit = true; } else if (self.isMagic) { // Magic damage with slow effect self.target.takeDamage(self.damage); self.target.applySlow(self.slowEffect, self.slowDuration); self.hasHit = true; } else { // Regular damage self.target.takeDamage(self.damage); self.hasHit = true; } LK.getSound('enemyHit').play(); if (self.hasHit) return; } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 100; self.health = self.maxHealth; self.speed = 2; self.reward = 10; self.pathIndex = 0; self.isDead = false; self.reachedBase = false; self.originalSpeed = self.speed; self.slowEndTime = 0; self.isSlowed = false; // Add health bar var healthBarBg = LK.getAsset('pathTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.1 }); healthBarBg.y = -40; healthBarBg.tint = 0x333333; self.addChild(healthBarBg); var healthBar = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.1 }); healthBar.y = -40; healthBar.tint = 0x00FF00; self.addChild(healthBar); self.updateHealthBar = function () { var healthPercent = self.health / self.maxHealth; healthBar.scaleX = 0.6 * healthPercent; if (healthPercent > 0.6) { healthBar.tint = 0x00FF00; // Green } else if (healthPercent > 0.3) { healthBar.tint = 0xFFFF00; // Yellow } else { healthBar.tint = 0xFF0000; // Red } }; self.applySlow = function (slowFactor, duration) { self.speed = self.originalSpeed * slowFactor; self.slowEndTime = LK.ticks + duration; self.isSlowed = true; enemyGraphics.tint = 0x3498db; // Blue tint for slowed enemies }; self.takeDamage = function (damage) { self.health -= damage; self.updateHealthBar(); if (self.health <= 0 && !self.isDead) { self.isDead = true; LK.getSound('enemyDeath').play(); currency += self.reward; updateUI(); // Enhanced death animation with explosion effect tween(enemyGraphics, { scaleX: 1.5, scaleY: 1.5, tint: 0xff6b6b }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(enemyGraphics, { alpha: 0, scaleX: 0.1, scaleY: 0.1, rotation: Math.PI * 2 }, { duration: 400, easing: tween.easeIn }); } }); } }; self.update = function () { if (self.isDead || self.reachedBase) return; // Check if slow effect should end if (self.isSlowed && LK.ticks >= self.slowEndTime) { self.speed = self.originalSpeed; self.isSlowed = false; enemyGraphics.tint = 0xFFFFFF; // Reset tint } if (self.pathIndex < enemyPath.length) { var target = enemyPath[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 >= enemyPath.length) { self.reachedBase = true; lives--; updateUI(); return; } } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; return self; }); var UltraBoss = Enemy.expand(function () { var self = Enemy.call(this); // Replace graphics with ultra boss enemy self.removeChildAt(0); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5 }); enemyGraphics.tint = 0x4B0082; // Dark violet for ultra boss self.maxHealth = 2500; self.health = self.maxHealth; self.speed = 0.8; self.originalSpeed = self.speed; self.reward = 200; self.enemyType = 'ultraboss'; self.isUltraBoss = true; self.lastSpecialAttack = 0; self.specialAttackCooldown = 240; // 4 seconds at 60fps self.healAmount = 100; self.shieldActive = false; self.shieldEndTime = 0; self.teleportCooldown = 0; // Ultra boss shield ability - damage immunity self.activateShield = function () { self.shieldActive = true; self.shieldEndTime = LK.ticks + 180; // 3 seconds // Shield visual effect tween(enemyGraphics, { tint: 0x00FFFF, scaleX: 3, scaleY: 3 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { enemyGraphics.tint = 0x87CEEB; // Light blue while shielded } }); LK.effects.flashObject(self, 0x00FFFF, 500); }; // Ultra boss teleport ability self.teleportToPath = function () { var randomIndex = Math.min(self.pathIndex + 3, enemyPath.length - 1); var targetPos = enemyPath[randomIndex]; // Teleport effect tween(self, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 200, onFinish: function onFinish() { self.x = targetPos.x; self.y = targetPos.y; self.pathIndex = randomIndex; tween(self, { alpha: 1, scaleX: 2.5, scaleY: 2.5 }, { duration: 300, easing: tween.bounceOut }); } }); LK.effects.flashScreen(0x9932CC, 300); }; // Ultra boss area damage ability self.areaDamage = function () { // Damage all nearby towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var dx = tower.x - self.x; var dy = tower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 250) { // Disable tower temporarily and reduce level tower.stunned = true; tower.stunEndTime = LK.ticks + 300; // 5 seconds if (tower.level > 1) { tower.level--; tower.damage = Math.max(10, tower.damage - 15); } // Visual effect tween(tower, { tint: 0x8B0000, alpha: 0.3 }, { duration: 300 }); } } // Ultra boss area damage visual effect LK.effects.flashScreen(0x8B0000, 800); }; self.takeDamage = function (damage, damageType) { if (self.shieldActive) { // No damage while shield is active return; } var actualDamage = damage * 0.6; // Ultra boss takes 40% less damage self.health -= actualDamage; self.updateHealthBar(); if (self.health <= 0 && !self.isDead) { self.isDead = true; LK.getSound('enemyDeath').play(); currency += self.reward; diamonds += 10; // Ultra boss drops more diamonds updateUI(); // Epic ultra boss death animation tween(enemyGraphics, { scaleX: 4, scaleY: 4, tint: 0x9932CC, rotation: Math.PI * 6 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { tween(enemyGraphics, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 800, easing: tween.easeIn }); } }); LK.effects.flashScreen(0x9932CC, 1500); } }; self.update = function () { if (self.isDead || self.reachedBase) return; // Check if shield should end if (self.shieldActive && LK.ticks >= self.shieldEndTime) { self.shieldActive = false; enemyGraphics.tint = 0x4B0082; // Reset ultra boss tint } // Check if slow effect should end if (self.isSlowed && LK.ticks >= self.slowEndTime) { self.speed = self.originalSpeed; self.isSlowed = false; enemyGraphics.tint = self.shieldActive ? 0x87CEEB : 0x4B0082; } // Ultra boss special attacks if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) { var specialType = Math.floor(Math.random() * 4); if (specialType === 0) { self.healSelf(); } else if (specialType === 1) { self.activateShield(); } else if (specialType === 2 && self.teleportCooldown <= 0) { self.teleportToPath(); self.teleportCooldown = 600; // 10 second cooldown } else { self.areaDamage(); } self.lastSpecialAttack = LK.ticks; } if (self.teleportCooldown > 0) { self.teleportCooldown--; } if (self.pathIndex < enemyPath.length) { var target = enemyPath[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 >= enemyPath.length) { self.reachedBase = true; lives -= 10; // Ultra boss deals massive damage to base updateUI(); return; } } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; return self; }); var MagicWarrior = Enemy.expand(function () { var self = Enemy.call(this); // Replace graphics with magic warrior self.removeChildAt(0); var enemyGraphics = self.attachAsset('magicWarrior', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 120; self.health = self.maxHealth; self.speed = 2.2; self.originalSpeed = self.speed; self.reward = 20; self.enemyType = 'magic'; self.magicResistance = 0.5; // Takes 50% less damage from magic towers self.takeDamage = function (damage, damageType) { var actualDamage = damage; if (damageType === 'magic') { actualDamage = damage * self.magicResistance; } self.health -= actualDamage; self.updateHealthBar(); if (self.health <= 0 && !self.isDead) { self.isDead = true; LK.getSound('enemyDeath').play(); currency += self.reward; updateUI(); tween(enemyGraphics, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500 }); } }; return self; }); var HeavyWarrior = Enemy.expand(function () { var self = Enemy.call(this); // Replace graphics with heavy warrior self.removeChildAt(0); var enemyGraphics = self.attachAsset('heavyWarrior', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 200; self.health = self.maxHealth; self.speed = 1.0; self.originalSpeed = self.speed; self.reward = 25; self.enemyType = 'heavy'; return self; }); var FastWarrior = Enemy.expand(function () { var self = Enemy.call(this); // Replace graphics with fast warrior self.removeChildAt(0); var enemyGraphics = self.attachAsset('fastWarrior', { anchorX: 0.5, anchorY: 0.5 }); self.maxHealth = 60; self.health = self.maxHealth; self.speed = 3.5; self.originalSpeed = self.speed; self.reward = 15; self.enemyType = 'fast'; return self; }); var Boss = Enemy.expand(function () { var self = Enemy.call(this); // Replace graphics with boss enemy self.removeChildAt(0); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); enemyGraphics.tint = 0x8B0000; // Dark red for boss self.maxHealth = 1000; self.health = self.maxHealth; self.speed = 1.0; self.originalSpeed = self.speed; self.reward = 100; self.enemyType = 'boss'; self.isBoss = true; self.lastSpecialAttack = 0; self.specialAttackCooldown = 300; // 5 seconds at 60fps self.healAmount = 50; self.stun = false; self.stunEndTime = 0; // Boss special ability - healing self.healSelf = function () { self.health = Math.min(self.maxHealth, self.health + self.healAmount); self.updateHealthBar(); // Healing visual effect tween(enemyGraphics, { tint: 0x00FF00, scaleX: 2.5, scaleY: 2.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(enemyGraphics, { tint: 0x8B0000, scaleX: 2, scaleY: 2 }, { duration: 300, easing: tween.easeIn }); } }); LK.effects.flashObject(self, 0x00FF00, 500); }; // Boss stun ability - temporarily disable nearby towers self.stunTowers = function () { for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var dx = tower.x - self.x; var dy = tower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 200) { // Disable tower temporarily tower.stunned = true; tower.stunEndTime = LK.ticks + 180; // 3 seconds // Visual effect tween(tower, { tint: 0x666666, alpha: 0.5 }, { duration: 200 }); } } // Boss stun visual effect LK.effects.flashScreen(0xFF0000, 500); }; self.takeDamage = function (damage, damageType) { var actualDamage = damage * 0.8; // Boss takes 20% less damage self.health -= actualDamage; self.updateHealthBar(); if (self.health <= 0 && !self.isDead) { self.isDead = true; LK.getSound('enemyDeath').play(); currency += self.reward; diamonds += 5; // Boss drops diamonds updateUI(); // Epic boss death animation tween(enemyGraphics, { scaleX: 3, scaleY: 3, tint: 0xFFD700, rotation: Math.PI * 4 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { tween(enemyGraphics, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 500, easing: tween.easeIn }); } }); LK.effects.flashScreen(0xFFD700, 1000); } }; self.update = function () { if (self.isDead || self.reachedBase) return; // Check if slow effect should end if (self.isSlowed && LK.ticks >= self.slowEndTime) { self.speed = self.originalSpeed; self.isSlowed = false; enemyGraphics.tint = 0x8B0000; // Reset boss tint } // Boss special attacks if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) { var specialType = Math.floor(Math.random() * 2); if (specialType === 0) { self.healSelf(); } else { self.stunTowers(); } self.lastSpecialAttack = LK.ticks; } if (self.pathIndex < enemyPath.length) { var target = enemyPath[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 >= enemyPath.length) { self.reachedBase = true; lives -= 5; // Boss deals more damage to base updateUI(); return; } } var moveX = dx / distance * self.speed; var moveY = dy / distance * self.speed; self.x += moveX; self.y += moveY; } }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); var towerGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 150; self.damage = 25; self.fireRate = 60; self.lastFire = 0; self.cost = 50; self.level = 1; self.maxLevel = 3; self.showingRange = false; self.upgradeCost = 75; var rangeIndicator = self.attachAsset('towerRange', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.visible = false; self.showRange = function () { rangeIndicator.visible = true; self.showingRange = true; }; self.hideRange = function () { rangeIndicator.visible = false; self.showingRange = false; }; self.findTarget = function () { var closestEnemy = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead || enemy.reachedBase) continue; 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) { closestDistance = distance; closestEnemy = enemy; } } return closestEnemy; }; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.target = target; bullet.damage = self.damage; // Bullet spawn animation bullet.alpha = 0; bullet.scaleX = 0.5; bullet.scaleY = 0.5; bullets.push(bullet); game.addChild(bullet); // Animate bullet appearance tween(bullet, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOut }); // Tower fire animation var originalScale = towerGraphics.scaleX; tween(towerGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(towerGraphics, { scaleX: originalScale, scaleY: originalScale }, { duration: 100, easing: tween.easeIn }); } }); self.lastFire = LK.ticks; LK.getSound('shoot').play(); }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 15; self.range += 25; self.fireRate = Math.max(30, self.fireRate - 10); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); // Update range indicator rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; // Enhanced visual upgrade animation tween(towerGraphics, { scaleX: 1.4, scaleY: 1.4, tint: 0x00ff00 }, { duration: 300, easing: tween.bounceOut, onFinish: function onFinish() { var finalTint = self.level === 2 ? 0x3A7BD5 : 0x2E5BBA; tween(towerGraphics, { scaleX: 1, scaleY: 1, tint: finalTint }, { duration: 200, easing: tween.easeOut }); } }); // Upgrade sparkle effect LK.effects.flashObject(self, 0xffd700, 800); updateUI(); return true; }; self.down = function (x, y, obj) { // Always start dragging on click draggingTower = self; selectedTower = self; // Use x, y parameters directly instead of trying to convert coordinates dragOffset.x = x - self.x; dragOffset.y = y - self.y; originalTowerPosition.x = self.x; originalTowerPosition.y = self.y; // Find original grid position var gridPos = getGridPosition(self.x, self.y); if (gridPos) { originalGridPosition.x = gridPos.x; originalGridPosition.y = gridPos.y; grid[gridPos.x][gridPos.y].occupied = false; } // Add drag visual effect tween(self, { alpha: 0.7, scaleX: 1.1, scaleY: 1.1 }, { duration: 200 }); hideAllRanges(); self.showRange(); }; self.update = function () { // Check if tower is stunned by boss if (self.stunned && LK.ticks >= self.stunEndTime) { self.stunned = false; // Reset visual effects tween(self, { tint: 0xFFFFFF, alpha: 1 }, { duration: 300 }); } if (self.stunned) return; // Skip firing when stunned var target = self.findTarget(); if (target) { self.fire(target); } }; return self; }); var SuperTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with super tower self.removeChildAt(0); var towerGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); towerGraphics.tint = 0xFFD700; // Gold color for super tower self.range = 300; self.damage = 100; self.fireRate = 20; self.cost = 0; // Free tower from rocket self.level = 1; self.maxLevel = 5; // 5 upgrade levels self.upgradeCost = 200; self.towerType = 'super'; self.multiShot = 3; // Fires 3 bullets at once self.piercing = true; // Bullets can hit multiple enemies // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0xFFD700; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; // Multi-shot bullets for (var shot = 0; shot < self.multiShot; shot++) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.target = target; bullet.damage = self.damage; bullet.isPiercing = self.piercing; bullet.hitTargets = []; // Track hit targets for piercing // Spread bullets slightly var angleOffset = (shot - Math.floor(self.multiShot / 2)) * 0.2; bullet.angleOffset = angleOffset; // Super bullet visual bullet.children[0].tint = 0xFFD700; bullet.children[0].scaleX = 1.5; bullet.children[0].scaleY = 1.5; bullets.push(bullet); game.addChild(bullet); } // Super tower fire animation tween(towerGraphics, { scaleX: 2, scaleY: 2, tint: 0xFFFFFF }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(towerGraphics, { scaleX: 1.5, scaleY: 1.5, tint: 0xFFD700 }, { duration: 150, easing: tween.easeIn }); } }); self.lastFire = LK.ticks; LK.getSound('shoot').play(); }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 50; self.range += 50; self.fireRate = Math.max(10, self.fireRate - 3); self.multiShot = Math.min(5, self.multiShot + 1); self.upgradeCost = Math.floor(self.upgradeCost * 1.8); // Update range indicator rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; // Epic upgrade animation tween(towerGraphics, { scaleX: 2.5, scaleY: 2.5, tint: 0xFFFFFF }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { var levelTint = [0xFFD700, 0xFFA500, 0xFF8C00, 0xFF6347, 0xFF0000][self.level - 1]; tween(towerGraphics, { scaleX: 1.5, scaleY: 1.5, tint: levelTint }, { duration: 300, easing: tween.easeOut }); } }); LK.effects.flashObject(self, 0xFFD700, 1000); updateUI(); return true; }; return self; }); var MagicTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with magic tower self.removeChildAt(0); var towerGraphics = self.attachAsset('magicTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 130; self.damage = 20; self.fireRate = 50; self.cost = 80; self.upgradeCost = 120; self.towerType = 'magic'; self.slowEffect = 0.5; self.slowDuration = 120; // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0xe67e22; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; // Create slowing bullet var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.target = target; bullet.damage = self.damage; bullet.slowEffect = self.slowEffect; bullet.slowDuration = self.slowDuration; bullet.isMagic = true; bullets.push(bullet); game.addChild(bullet); self.lastFire = LK.ticks; LK.getSound('shoot').play(); }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 12; self.range += 25; self.slowEffect = Math.max(0.2, self.slowEffect - 0.1); self.slowDuration += 30; self.fireRate = Math.max(30, self.fireRate - 10); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; towerGraphics.tint = self.level === 2 ? 0xD35400 : 0xA04000; updateUI(); return true; }; return self; }); var LightningTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with lightning tower self.removeChildAt(0); var towerGraphics = self.attachAsset('lightningTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 140; self.damage = 35; self.fireRate = 80; self.cost = 120; self.upgradeCost = 180; self.towerType = 'lightning'; self.chainCount = 3; self.chainRadius = 100; // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0xf1c40f; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; // Lightning chain effect var chainTargets = [target]; var currentTarget = target; // Find chain targets for (var c = 0; c < self.chainCount - 1; c++) { var nextTarget = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead || enemy.reachedBase) continue; if (chainTargets.indexOf(enemy) !== -1) continue; var dx = enemy.x - currentTarget.x; var dy = enemy.y - currentTarget.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.chainRadius && distance < closestDistance) { closestDistance = distance; nextTarget = enemy; } } if (nextTarget) { chainTargets.push(nextTarget); currentTarget = nextTarget; } } // Apply damage to all chain targets for (var i = 0; i < chainTargets.length; i++) { var chainTarget = chainTargets[i]; var chainDamage = self.damage * (1 - i * 0.2); // Decreasing damage chainTarget.takeDamage(chainDamage); // Lightning effect animation tween(chainTarget, { tint: 0xf1c40f }, { duration: 100, onFinish: function onFinish() { tween(chainTarget, { tint: 0xFFFFFF }, { duration: 200 }); } }); } // Tower fire animation tween(towerGraphics, { tint: 0xf1c40f, scaleX: 1.3, scaleY: 1.3 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(towerGraphics, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); self.lastFire = LK.ticks; LK.getSound('lightning').play(); }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 18; self.range += 20; self.chainCount++; self.chainRadius += 20; self.fireRate = Math.max(50, self.fireRate - 10); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; towerGraphics.tint = self.level === 2 ? 0xd4ac0d : 0xb7950b; updateUI(); return true; }; return self; }); var LaserTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with laser tower self.removeChildAt(0); var towerGraphics = self.attachAsset('laserTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 160; self.damage = 8; self.fireRate = 10; self.cost = 110; self.upgradeCost = 160; self.towerType = 'laser'; self.currentTarget = null; self.beamDuration = 0; self.maxBeamDuration = 300; // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0xe74c3c; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; // Continuous laser beam if (self.currentTarget === target) { self.beamDuration++; // Increasing damage over time var damageMultiplier = 1 + self.beamDuration / 100; target.takeDamage(self.damage * damageMultiplier); } else { self.currentTarget = target; self.beamDuration = 0; target.takeDamage(self.damage); } // Laser beam visual effect tween(target, { tint: 0xe74c3c }, { duration: 50, onFinish: function onFinish() { tween(target, { tint: 0xFFFFFF }, { duration: 100 }); } }); // Tower rotation towards target var angle = Math.atan2(target.y - self.y, target.x - self.x); tween(towerGraphics, { rotation: angle }, { duration: 100, easing: tween.easeOut }); self.lastFire = LK.ticks; if (self.beamDuration % 30 === 0) { LK.getSound('laser').play(); } }; self.findTarget = function () { // Prefer to keep current target if still in range if (self.currentTarget && !self.currentTarget.isDead && !self.currentTarget.reachedBase) { var dx = self.currentTarget.x - self.x; var dy = self.currentTarget.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.range) { return self.currentTarget; } } // Find new target if current is lost self.currentTarget = null; self.beamDuration = 0; var closestEnemy = null; var closestDistance = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead || enemy.reachedBase) continue; 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) { closestDistance = distance; closestEnemy = enemy; } } return closestEnemy; }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 5; self.range += 25; self.maxBeamDuration += 100; self.fireRate = Math.max(5, self.fireRate - 2); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; towerGraphics.tint = self.level === 2 ? 0xc0392b : 0xa93226; updateUI(); return true; }; return self; }); var FreezeTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with freeze tower self.removeChildAt(0); var towerGraphics = self.attachAsset('freezeTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 120; self.damage = 10; self.fireRate = 120; self.cost = 90; self.upgradeCost = 140; self.towerType = 'freeze'; self.freezeRadius = 80; self.freezeDuration = 180; self.freezeEffect = 0.1; // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0x7fb3d3; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; // Area freeze effect for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead || enemy.reachedBase) continue; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.freezeRadius) { enemy.takeDamage(self.damage); enemy.applySlow(self.freezeEffect, self.freezeDuration); // Freeze effect animation tween(enemy, { tint: 0x7fb3d3 }, { duration: 200, onFinish: function onFinish() { tween(enemy, { tint: 0x3498db }, { duration: self.freezeDuration * 16 }); } }); } } // Tower fire animation with pulse effect tween(towerGraphics, { scaleX: 1.4, scaleY: 1.4, tint: 0x3498db }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(towerGraphics, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300, easing: tween.bounceOut }); } }); self.lastFire = LK.ticks; LK.getSound('freeze').play(); }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 8; self.range += 15; self.freezeRadius += 20; self.freezeDuration += 60; self.freezeEffect = Math.max(0.05, self.freezeEffect - 0.025); self.fireRate = Math.max(80, self.fireRate - 15); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; towerGraphics.tint = self.level === 2 ? 0x5dade2 : 0x3498db; updateUI(); return true; }; return self; }); var CannonTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with cannon tower self.removeChildAt(0); var towerGraphics = self.attachAsset('cannonTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 120; self.damage = 40; self.fireRate = 90; self.cost = 100; self.upgradeCost = 150; self.towerType = 'cannon'; self.splashRadius = 80; // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0x34495e; self.fire = function (target) { if (LK.ticks - self.lastFire < self.fireRate) return; // Create splash damage bullet var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.target = target; bullet.damage = self.damage; bullet.splashRadius = self.splashRadius; bullet.isSplash = true; bullets.push(bullet); game.addChild(bullet); self.lastFire = LK.ticks; LK.getSound('shoot').play(); }; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 20; self.range += 20; self.splashRadius += 20; self.fireRate = Math.max(60, self.fireRate - 15); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; towerGraphics.tint = self.level === 2 ? 0x2C3E50 : 0x1B2631; updateUI(); return true; }; return self; }); var ArcherTower = Tower.expand(function () { var self = Tower.call(this); // Replace graphics with archer tower self.removeChildAt(0); var towerGraphics = self.attachAsset('archerTower', { anchorX: 0.5, anchorY: 0.5 }); self.range = 200; self.damage = 15; self.fireRate = 40; self.cost = 75; self.upgradeCost = 100; self.towerType = 'archer'; // Update range indicator var rangeIndicator = self.children[self.children.length - 1]; rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; rangeIndicator.tint = 0x8e44ad; self.upgrade = function () { if (self.level >= self.maxLevel || currency < self.upgradeCost) return false; currency -= self.upgradeCost; self.level++; self.damage += 10; self.range += 30; self.fireRate = Math.max(25, self.fireRate - 5); self.upgradeCost = Math.floor(self.upgradeCost * 1.5); rangeIndicator.width = self.range * 2; rangeIndicator.height = self.range * 2; towerGraphics.tint = self.level === 2 ? 0x7D3C98 : 0x6C3483; updateUI(); return true; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var gridSize = 100; var mapWidth = 20; var mapHeight = 25; var currency = 390; var diamonds = 0; var lives = 20; var wave = 1; var enemiesInWave = 5; var enemiesSpawned = 0; var waveActive = false; var gameOver = false; var towers = []; var enemies = []; var bullets = []; var grid = []; var enemyPath = []; var selectedTower = null; var placingTower = false; var selectedTowerType = 'basic'; var draggingTower = null; var dragOffset = { x: 0, y: 0 }; var originalTowerPosition = { x: 0, y: 0 }; var originalGridPosition = { x: 0, y: 0 }; var rocketDelivered = false; var superTowerUnlocked = false; // Create UI elements var currencyTxt = new Text2('Gold: 390', { size: 60, fill: 0xFFD700 }); currencyTxt.anchor.set(0, 0); currencyTxt.x = 120; currencyTxt.y = 20; LK.gui.topLeft.addChild(currencyTxt); var diamondsTxt = new Text2('Diamonds: 0', { size: 60, fill: 0x00FFFF }); diamondsTxt.anchor.set(0, 0); diamondsTxt.x = 120; diamondsTxt.y = 90; LK.gui.topLeft.addChild(diamondsTxt); var livesTxt = new Text2('Lives: 20', { size: 60, fill: 0xFF0000 }); livesTxt.anchor.set(0.5, 0); LK.gui.top.addChild(livesTxt); var waveTxt = new Text2('Wave: 1', { size: 60, fill: 0xFFFFFF }); waveTxt.anchor.set(1, 0); LK.gui.topRight.addChild(waveTxt); var startWaveBtn = new Text2('Start Wave', { size: 80, fill: 0x00FF00 }); startWaveBtn.anchor.set(0.5, 1); startWaveBtn.y = -20; LK.gui.bottom.addChild(startWaveBtn); var buyTowerBtn = new Text2('Basic Tower (50g)', { size: 60, fill: 0x4A90E2 }); buyTowerBtn.anchor.set(0, 1); buyTowerBtn.x = 20; buyTowerBtn.y = -20; LK.gui.bottomLeft.addChild(buyTowerBtn); var buyArcherBtn = new Text2('Archer Tower (75g)', { size: 60, fill: 0x8e44ad }); buyArcherBtn.anchor.set(0, 1); buyArcherBtn.x = 20; buyArcherBtn.y = -80; LK.gui.bottomLeft.addChild(buyArcherBtn); var buyCannonBtn = new Text2('Cannon Tower (100g)', { size: 60, fill: 0x34495e }); buyCannonBtn.anchor.set(0, 1); buyCannonBtn.x = 20; buyCannonBtn.y = -140; LK.gui.bottomLeft.addChild(buyCannonBtn); var buyMagicBtn = new Text2('Magic Tower (80g)', { size: 60, fill: 0xe67e22 }); buyMagicBtn.anchor.set(0, 1); buyMagicBtn.x = 20; buyMagicBtn.y = -200; LK.gui.bottomLeft.addChild(buyMagicBtn); var buyLightningBtn = new Text2('Lightning Tower (120g)', { size: 60, fill: 0xf1c40f }); buyLightningBtn.anchor.set(0, 1); buyLightningBtn.x = 20; buyLightningBtn.y = -260; LK.gui.bottomLeft.addChild(buyLightningBtn); var buyFreezeBtn = new Text2('Freeze Tower (90g)', { size: 60, fill: 0x7fb3d3 }); buyFreezeBtn.anchor.set(0, 1); buyFreezeBtn.x = 20; buyFreezeBtn.y = -320; LK.gui.bottomLeft.addChild(buyFreezeBtn); var buyLaserBtn = new Text2('Laser Tower (110g)', { size: 60, fill: 0xe74c3c }); buyLaserBtn.anchor.set(0, 1); buyLaserBtn.x = 20; buyLaserBtn.y = -380; LK.gui.bottomLeft.addChild(buyLaserBtn); var buySuperBtn = new Text2('SUPER TOWER (FREE)', { size: 60, fill: 0xFFD700 }); buySuperBtn.anchor.set(0, 1); buySuperBtn.x = 20; buySuperBtn.y = -440; buySuperBtn.visible = false; LK.gui.bottomLeft.addChild(buySuperBtn); var upgradeTowerBtn = new Text2('Upgrade Tower', { size: 70, fill: 0xFF6B35 }); upgradeTowerBtn.anchor.set(0, 1); upgradeTowerBtn.x = 20; upgradeTowerBtn.y = -100; upgradeTowerBtn.visible = false; LK.gui.bottomLeft.addChild(upgradeTowerBtn); var enhanceAllBtn = new Text2('Enhance All Towers (10♦)', { size: 60, fill: 0x00FFFF }); enhanceAllBtn.anchor.set(1, 1); enhanceAllBtn.x = -20; enhanceAllBtn.y = -20; LK.gui.bottomRight.addChild(enhanceAllBtn); var superDamageBtn = new Text2('Super Damage (15♦)', { size: 60, fill: 0xFF6B35 }); superDamageBtn.anchor.set(1, 1); superDamageBtn.x = -20; superDamageBtn.y = -80; LK.gui.bottomRight.addChild(superDamageBtn); var rapidFireBtn = new Text2('Rapid Fire (12♦)', { size: 60, fill: 0xFFFF00 }); rapidFireBtn.anchor.set(1, 1); rapidFireBtn.x = -20; rapidFireBtn.y = -140; LK.gui.bottomRight.addChild(rapidFireBtn); function updateUI() { currencyTxt.setText('Gold: ' + currency); diamondsTxt.setText('Diamonds: ' + diamonds); livesTxt.setText('Lives: ' + lives); waveTxt.setText('Wave: ' + wave); if (currency >= 50) { buyTowerBtn.tint = 0x4A90E2; } else { buyTowerBtn.tint = 0x666666; } if (currency >= 75) { buyArcherBtn.tint = 0x8e44ad; } else { buyArcherBtn.tint = 0x666666; } if (currency >= 100) { buyCannonBtn.tint = 0x34495e; } else { buyCannonBtn.tint = 0x666666; } if (currency >= 80) { buyMagicBtn.tint = 0xe67e22; } else { buyMagicBtn.tint = 0x666666; } if (currency >= 120) { buyLightningBtn.tint = 0xf1c40f; } else { buyLightningBtn.tint = 0x666666; } if (currency >= 90) { buyFreezeBtn.tint = 0x7fb3d3; } else { buyFreezeBtn.tint = 0x666666; } if (currency >= 110) { buyLaserBtn.tint = 0xe74c3c; } else { buyLaserBtn.tint = 0x666666; } // Update upgrade button visibility and state if (selectedTower && selectedTower.level < selectedTower.maxLevel) { upgradeTowerBtn.visible = true; upgradeTowerBtn.setText('Upgrade Lv' + selectedTower.level + ' (' + selectedTower.upgradeCost + 'g)'); if (currency >= selectedTower.upgradeCost) { upgradeTowerBtn.tint = 0xFF6B35; } else { upgradeTowerBtn.tint = 0x666666; } } else { upgradeTowerBtn.visible = false; } // Update diamond enhancement buttons if (diamonds >= 10) { enhanceAllBtn.tint = 0x00FFFF; } else { enhanceAllBtn.tint = 0x666666; } if (diamonds >= 15) { superDamageBtn.tint = 0xFF6B35; } else { superDamageBtn.tint = 0x666666; } if (diamonds >= 12) { rapidFireBtn.tint = 0xFFFF00; } else { rapidFireBtn.tint = 0x666666; } // Update super tower button visibility if (superTowerUnlocked) { buySuperBtn.visible = true; buySuperBtn.tint = 0xFFD700; } else { buySuperBtn.visible = false; } } function hideAllRanges() { for (var i = 0; i < towers.length; i++) { towers[i].hideRange(); } } function initializeGrid() { for (var x = 0; x < mapWidth; x++) { grid[x] = []; for (var y = 0; y < mapHeight; y++) { grid[x][y] = { x: x * gridSize + gridSize / 2, y: y * gridSize + gridSize / 2, occupied: false, isPath: false }; } } } function createPath() { enemyPath = []; var pathCoords = [{ x: 0, y: 12 }, { x: 1, y: 12 }, { x: 2, y: 12 }, { x: 3, y: 12 }, { x: 4, y: 12 }, { x: 5, y: 12 }, { x: 6, y: 12 }, { x: 7, y: 12 }, { x: 8, y: 12 }, { x: 8, y: 11 }, { x: 8, y: 10 }, { x: 8, y: 9 }, { x: 8, y: 8 }, { x: 9, y: 8 }, { x: 10, y: 8 }, { x: 11, y: 8 }, { x: 12, y: 8 }, { x: 12, y: 9 }, { x: 12, y: 10 }, { x: 12, y: 11 }, { x: 12, y: 12 }, { x: 13, y: 12 }, { x: 14, y: 12 }, { x: 15, y: 12 }, { x: 16, y: 12 }, { x: 17, y: 12 }, { x: 18, y: 12 }, { x: 19, y: 12 }]; for (var i = 0; i < pathCoords.length; i++) { var coord = pathCoords[i]; if (coord.x < mapWidth && coord.y < mapHeight) { grid[coord.x][coord.y].isPath = true; enemyPath.push({ x: coord.x * gridSize + gridSize / 2, y: coord.y * gridSize + gridSize / 2 }); } } } function drawMap() { for (var x = 0; x < mapWidth; x++) { for (var y = 0; y < mapHeight; y++) { var tile; if (grid[x][y].isPath) { tile = LK.getAsset('pathTile', { x: x * gridSize, y: y * gridSize }); } else { tile = LK.getAsset('grassTile', { x: x * gridSize, y: y * gridSize }); } game.addChild(tile); } } } function drawBase() { var base = LK.getAsset('base', { anchorX: 0.5, anchorY: 0.5, x: (mapWidth - 1) * gridSize + gridSize / 2, y: 12 * gridSize + gridSize / 2 }); game.addChild(base); } function spawnEnemy() { if (enemiesSpawned >= enemiesInWave) return; var enemy; // Spawn ultra boss after wave 10 and every 5 waves after that if (wave > 10 && wave % 5 === 0 && enemiesSpawned === enemiesInWave - 1) { enemy = new UltraBoss(); } else if (wave % 10 === 0 && enemiesSpawned === enemiesInWave - 1) { // Spawn regular boss on wave 10 and every 10 waves enemy = new Boss(); } else { var enemyType = Math.floor(Math.random() * 4); if (enemyType === 0) { enemy = new Enemy(); } else if (enemyType === 1) { enemy = new FastWarrior(); } else if (enemyType === 2) { enemy = new HeavyWarrior(); } else { enemy = new MagicWarrior(); } } enemy.x = gridSize / 2; enemy.y = 12 * gridSize + gridSize / 2; // Scale stats with wave enemy.health = enemy.maxHealth + (wave - 1) * 20; enemy.maxHealth = enemy.health; enemy.speed = enemy.originalSpeed + (wave - 1) * 0.2; enemy.originalSpeed = enemy.speed; enemy.reward = enemy.reward + (wave - 1) * 2; // Spawn animation enemy.alpha = 0; enemy.scaleX = 0.1; enemy.scaleY = 0.1; enemies.push(enemy); game.addChild(enemy); // Enhanced enemy entrance animation with rotation var spawnAnimationType = Math.floor(Math.random() * 3); if (spawnAnimationType === 0) { // Bounce entrance tween(enemy, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.bounceOut }); } else if (spawnAnimationType === 1) { // Spiral entrance enemy.rotation = Math.PI * 4; tween(enemy, { alpha: 1, scaleX: 1, scaleY: 1, rotation: 0 }, { duration: 600, easing: tween.easeOut }); } else { // Elastic entrance tween(enemy, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 700, easing: tween.elasticOut }); } enemiesSpawned++; } function getGridPosition(x, y) { var gridX = Math.floor(x / gridSize); var gridY = Math.floor(y / gridSize); if (gridX >= 0 && gridX < mapWidth && gridY >= 0 && gridY < mapHeight) { return { x: gridX, y: gridY }; } return null; } function canPlaceTower(gridX, gridY) { return !grid[gridX][gridY].occupied && !grid[gridX][gridY].isPath; } function placeTower(gridX, gridY) { var tower; var cost; if (selectedTowerType === 'basic') { cost = 50; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new Tower(); } else if (selectedTowerType === 'archer') { cost = 75; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new ArcherTower(); } else if (selectedTowerType === 'cannon') { cost = 100; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new CannonTower(); } else if (selectedTowerType === 'magic') { cost = 80; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new MagicTower(); } else if (selectedTowerType === 'lightning') { cost = 120; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new LightningTower(); } else if (selectedTowerType === 'freeze') { cost = 90; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new FreezeTower(); } else if (selectedTowerType === 'laser') { cost = 110; if (!canPlaceTower(gridX, gridY) || currency < cost) return false; tower = new LaserTower(); } else if (selectedTowerType === 'super') { cost = 0; // Super tower is free if (!canPlaceTower(gridX, gridY)) return false; tower = new SuperTower(); } tower.x = gridX * gridSize + gridSize / 2; tower.y = gridY * gridSize + gridSize / 2; // Placement animation tower.alpha = 0; tower.scaleX = 0.1; tower.scaleY = 0.1; towers.push(tower); game.addChild(tower); // Animate tower placement tween(tower, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(tower, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); grid[gridX][gridY].occupied = true; currency -= cost; placingTower = false; updateUI(); return true; } function startWave() { if (waveActive) return; waveActive = true; enemiesSpawned = 0; enemiesInWave = 5 + wave * 2; startWaveBtn.setText('Wave Active'); startWaveBtn.tint = 0x666666; // Special rocket animation for wave 20 if (wave === 20 && !rocketDelivered) { deliverSuperTowerRocket(); rocketDelivered = true; } // Wave start animation tween(startWaveBtn, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.bounceOut, onFinish: function onFinish() { tween(startWaveBtn, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); // Screen flash effect for wave start LK.effects.flashScreen(0x00FF00, 300); } function deliverSuperTowerRocket() { // Create rocket var rocket = LK.getAsset('tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 2, x: 2048 / 2, y: -100 }); rocket.tint = 0xFF4500; rocket.rotation = Math.PI; game.addChild(rocket); // Rocket descent animation tween(rocket, { y: 2732 / 2, rotation: 0 }, { duration: 2000, easing: tween.easeIn, onFinish: function onFinish() { // Explosion effect LK.effects.flashScreen(0xFFD700, 1000); // Remove rocket and show super tower unlock message rocket.destroy(); superTowerUnlocked = true; // Create super tower unlock notification var unlockMsg = new Text2('SUPER TOWER UNLOCKED!', { size: 120, fill: 0xFFD700 }); unlockMsg.anchor.set(0.5, 0.5); unlockMsg.x = 2048 / 2; unlockMsg.y = 2732 / 2; unlockMsg.alpha = 0; game.addChild(unlockMsg); // Animate unlock message tween(unlockMsg, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.bounceOut, onFinish: function onFinish() { tween(unlockMsg, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { unlockMsg.destroy(); } }); } }); updateUI(); } }); } function checkWaveComplete() { if (!waveActive) return; var allEnemiesGone = true; for (var i = 0; i < enemies.length; i++) { if (!enemies[i].isDead && !enemies[i].reachedBase) { allEnemiesGone = false; break; } } if (enemiesSpawned >= enemiesInWave && allEnemiesGone) { waveActive = false; wave++; currency += 25; diamonds += 5; // Award 5 diamonds per wave startWaveBtn.setText('Start Wave'); startWaveBtn.tint = 0x00FF00; updateUI(); // Clean up dead enemies for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i].isDead || enemies[i].reachedBase) { enemies[i].destroy(); enemies.splice(i, 1); } } } } // Initialize game initializeGrid(); createPath(); drawMap(); drawBase(); updateUI(); // Event handlers startWaveBtn.down = function () { startWave(); }; buyTowerBtn.down = function () { if (currency >= 50) { selectedTowerType = 'basic'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; buyArcherBtn.down = function () { if (currency >= 75) { selectedTowerType = 'archer'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; buyCannonBtn.down = function () { if (currency >= 100) { selectedTowerType = 'cannon'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; buyMagicBtn.down = function () { if (currency >= 80) { selectedTowerType = 'magic'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; buyLightningBtn.down = function () { if (currency >= 120) { selectedTowerType = 'lightning'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; buyFreezeBtn.down = function () { if (currency >= 90) { selectedTowerType = 'freeze'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; buyLaserBtn.down = function () { if (currency >= 110) { selectedTowerType = 'laser'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; upgradeTowerBtn.down = function () { if (selectedTower && selectedTower.upgrade()) { updateUI(); } }; enhanceAllBtn.down = function () { if (diamonds >= 10) { diamonds -= 10; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; tower.damage = Math.floor(tower.damage * 1.5); tower.range = Math.floor(tower.range * 1.2); // Visual enhancement effect tween(tower, { scaleX: 1.3, scaleY: 1.3, tint: 0x00FFFF }, { duration: 400, easing: tween.bounceOut, onFinish: function onFinish() { tween(tower, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } LK.effects.flashScreen(0x00FFFF, 500); updateUI(); } }; superDamageBtn.down = function () { if (diamonds >= 15) { diamonds -= 15; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; tower.damage = Math.floor(tower.damage * 2.5); // Super damage visual effect tween(tower, { scaleX: 1.5, scaleY: 1.5, tint: 0xFF6B35 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { tween(tower, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 400 }); } }); } LK.effects.flashScreen(0xFF6B35, 800); updateUI(); } }; rapidFireBtn.down = function () { if (diamonds >= 12) { diamonds -= 12; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; tower.fireRate = Math.max(10, Math.floor(tower.fireRate * 0.5)); // Rapid fire visual effect tween(tower, { tint: 0xFFFF00 }, { duration: 200, onFinish: function onFinish() { tween(tower, { tint: 0xFFFFFF }, { duration: 200 }); } }); } LK.effects.flashScreen(0xFFFF00, 600); updateUI(); } }; buySuperBtn.down = function () { if (superTowerUnlocked) { selectedTowerType = 'super'; placingTower = true; selectedTower = null; hideAllRanges(); updateUI(); } }; game.move = function (x, y, obj) { if (draggingTower) { draggingTower.x = x - dragOffset.x; draggingTower.y = y - dragOffset.y; // Visual feedback for valid/invalid placement var gridPos = getGridPosition(draggingTower.x, draggingTower.y); if (gridPos && canPlaceTower(gridPos.x, gridPos.y)) { draggingTower.tint = 0x00FF00; // Green for valid } else { draggingTower.tint = 0xFF0000; // Red for invalid } } }; game.down = function (x, y, obj) { if (placingTower) { var gridPos = getGridPosition(x, y); if (gridPos) { placeTower(gridPos.x, gridPos.y); } } else { // Clear tower selection if clicking on empty space var gridPos = getGridPosition(x, y); if (gridPos && !grid[gridPos.x][gridPos.y].occupied) { selectedTower = null; hideAllRanges(); updateUI(); } } }; game.up = function (x, y, obj) { if (draggingTower) { var gridPos = getGridPosition(draggingTower.x, draggingTower.y); var canPlace = gridPos && canPlaceTower(gridPos.x, gridPos.y); if (canPlace) { // Successfully place tower at new position draggingTower.x = gridPos.x * gridSize + gridSize / 2; draggingTower.y = gridPos.y * gridSize + gridSize / 2; grid[gridPos.x][gridPos.y].occupied = true; // Smooth placement animation tween(draggingTower, { alpha: 1, scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300, easing: tween.bounceOut }); } else { // Return to original position tween(draggingTower, { x: originalTowerPosition.x, y: originalTowerPosition.y, alpha: 1, scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 400, easing: tween.elasticOut }); // Restore original grid position grid[originalGridPosition.x][originalGridPosition.y].occupied = true; } draggingTower = null; } }; // Main game loop game.update = function () { if (lives <= 0 && !gameOver) { gameOver = true; LK.showGameOver(); return; } if (wave > 20) { LK.showYouWin(); return; } // Spawn enemies during wave with decreasing interval var spawnInterval = Math.max(45, 90 - wave * 2); if (waveActive && LK.ticks % spawnInterval === 0) { spawnEnemy(); } // Clean up bullets that hit targets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.hasHit || !bullet.target || bullet.target.isDead) { bullet.destroy(); bullets.splice(i, 1); } } checkWaveComplete(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 25;
self.target = null;
self.hasHit = false;
self.update = function () {
if (!self.target || self.hasHit) 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) {
if (self.isPiercing && self.hitTargets.indexOf(self.target) === -1) {
// Piercing bullet hits target but continues
self.target.takeDamage(self.damage);
self.hitTargets.push(self.target);
// Find next target
var nextTarget = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead || enemy.reachedBase || self.hitTargets.indexOf(enemy) !== -1) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < closestDistance && dist <= 200) {
closestDistance = dist;
nextTarget = enemy;
}
}
if (nextTarget) {
self.target = nextTarget;
} else {
self.hasHit = true;
}
} else if (self.isSplash) {
// Splash damage to all enemies in radius
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead || enemy.reachedBase) continue;
var splashDx = enemy.x - self.x;
var splashDy = enemy.y - self.y;
var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy);
if (splashDistance <= self.splashRadius) {
enemy.takeDamage(self.damage);
}
}
self.hasHit = true;
} else if (self.isMagic) {
// Magic damage with slow effect
self.target.takeDamage(self.damage);
self.target.applySlow(self.slowEffect, self.slowDuration);
self.hasHit = true;
} else {
// Regular damage
self.target.takeDamage(self.damage);
self.hasHit = true;
}
LK.getSound('enemyHit').play();
if (self.hasHit) return;
}
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 100;
self.health = self.maxHealth;
self.speed = 2;
self.reward = 10;
self.pathIndex = 0;
self.isDead = false;
self.reachedBase = false;
self.originalSpeed = self.speed;
self.slowEndTime = 0;
self.isSlowed = false;
// Add health bar
var healthBarBg = LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1
});
healthBarBg.y = -40;
healthBarBg.tint = 0x333333;
self.addChild(healthBarBg);
var healthBar = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.1
});
healthBar.y = -40;
healthBar.tint = 0x00FF00;
self.addChild(healthBar);
self.updateHealthBar = function () {
var healthPercent = self.health / self.maxHealth;
healthBar.scaleX = 0.6 * healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00FF00; // Green
} else if (healthPercent > 0.3) {
healthBar.tint = 0xFFFF00; // Yellow
} else {
healthBar.tint = 0xFF0000; // Red
}
};
self.applySlow = function (slowFactor, duration) {
self.speed = self.originalSpeed * slowFactor;
self.slowEndTime = LK.ticks + duration;
self.isSlowed = true;
enemyGraphics.tint = 0x3498db; // Blue tint for slowed enemies
};
self.takeDamage = function (damage) {
self.health -= damage;
self.updateHealthBar();
if (self.health <= 0 && !self.isDead) {
self.isDead = true;
LK.getSound('enemyDeath').play();
currency += self.reward;
updateUI();
// Enhanced death animation with explosion effect
tween(enemyGraphics, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xff6b6b
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1,
rotation: Math.PI * 2
}, {
duration: 400,
easing: tween.easeIn
});
}
});
}
};
self.update = function () {
if (self.isDead || self.reachedBase) return;
// Check if slow effect should end
if (self.isSlowed && LK.ticks >= self.slowEndTime) {
self.speed = self.originalSpeed;
self.isSlowed = false;
enemyGraphics.tint = 0xFFFFFF; // Reset tint
}
if (self.pathIndex < enemyPath.length) {
var target = enemyPath[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 >= enemyPath.length) {
self.reachedBase = true;
lives--;
updateUI();
return;
}
}
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
}
};
return self;
});
var UltraBoss = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace graphics with ultra boss enemy
self.removeChildAt(0);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
enemyGraphics.tint = 0x4B0082; // Dark violet for ultra boss
self.maxHealth = 2500;
self.health = self.maxHealth;
self.speed = 0.8;
self.originalSpeed = self.speed;
self.reward = 200;
self.enemyType = 'ultraboss';
self.isUltraBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 240; // 4 seconds at 60fps
self.healAmount = 100;
self.shieldActive = false;
self.shieldEndTime = 0;
self.teleportCooldown = 0;
// Ultra boss shield ability - damage immunity
self.activateShield = function () {
self.shieldActive = true;
self.shieldEndTime = LK.ticks + 180; // 3 seconds
// Shield visual effect
tween(enemyGraphics, {
tint: 0x00FFFF,
scaleX: 3,
scaleY: 3
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
enemyGraphics.tint = 0x87CEEB; // Light blue while shielded
}
});
LK.effects.flashObject(self, 0x00FFFF, 500);
};
// Ultra boss teleport ability
self.teleportToPath = function () {
var randomIndex = Math.min(self.pathIndex + 3, enemyPath.length - 1);
var targetPos = enemyPath[randomIndex];
// Teleport effect
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 200,
onFinish: function onFinish() {
self.x = targetPos.x;
self.y = targetPos.y;
self.pathIndex = randomIndex;
tween(self, {
alpha: 1,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
LK.effects.flashScreen(0x9932CC, 300);
};
// Ultra boss area damage ability
self.areaDamage = function () {
// Damage all nearby towers
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 250) {
// Disable tower temporarily and reduce level
tower.stunned = true;
tower.stunEndTime = LK.ticks + 300; // 5 seconds
if (tower.level > 1) {
tower.level--;
tower.damage = Math.max(10, tower.damage - 15);
}
// Visual effect
tween(tower, {
tint: 0x8B0000,
alpha: 0.3
}, {
duration: 300
});
}
}
// Ultra boss area damage visual effect
LK.effects.flashScreen(0x8B0000, 800);
};
self.takeDamage = function (damage, damageType) {
if (self.shieldActive) {
// No damage while shield is active
return;
}
var actualDamage = damage * 0.6; // Ultra boss takes 40% less damage
self.health -= actualDamage;
self.updateHealthBar();
if (self.health <= 0 && !self.isDead) {
self.isDead = true;
LK.getSound('enemyDeath').play();
currency += self.reward;
diamonds += 10; // Ultra boss drops more diamonds
updateUI();
// Epic ultra boss death animation
tween(enemyGraphics, {
scaleX: 4,
scaleY: 4,
tint: 0x9932CC,
rotation: Math.PI * 6
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeIn
});
}
});
LK.effects.flashScreen(0x9932CC, 1500);
}
};
self.update = function () {
if (self.isDead || self.reachedBase) return;
// Check if shield should end
if (self.shieldActive && LK.ticks >= self.shieldEndTime) {
self.shieldActive = false;
enemyGraphics.tint = 0x4B0082; // Reset ultra boss tint
}
// Check if slow effect should end
if (self.isSlowed && LK.ticks >= self.slowEndTime) {
self.speed = self.originalSpeed;
self.isSlowed = false;
enemyGraphics.tint = self.shieldActive ? 0x87CEEB : 0x4B0082;
}
// Ultra boss special attacks
if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
var specialType = Math.floor(Math.random() * 4);
if (specialType === 0) {
self.healSelf();
} else if (specialType === 1) {
self.activateShield();
} else if (specialType === 2 && self.teleportCooldown <= 0) {
self.teleportToPath();
self.teleportCooldown = 600; // 10 second cooldown
} else {
self.areaDamage();
}
self.lastSpecialAttack = LK.ticks;
}
if (self.teleportCooldown > 0) {
self.teleportCooldown--;
}
if (self.pathIndex < enemyPath.length) {
var target = enemyPath[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 >= enemyPath.length) {
self.reachedBase = true;
lives -= 10; // Ultra boss deals massive damage to base
updateUI();
return;
}
}
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
}
};
return self;
});
var MagicWarrior = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace graphics with magic warrior
self.removeChildAt(0);
var enemyGraphics = self.attachAsset('magicWarrior', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 120;
self.health = self.maxHealth;
self.speed = 2.2;
self.originalSpeed = self.speed;
self.reward = 20;
self.enemyType = 'magic';
self.magicResistance = 0.5; // Takes 50% less damage from magic towers
self.takeDamage = function (damage, damageType) {
var actualDamage = damage;
if (damageType === 'magic') {
actualDamage = damage * self.magicResistance;
}
self.health -= actualDamage;
self.updateHealthBar();
if (self.health <= 0 && !self.isDead) {
self.isDead = true;
LK.getSound('enemyDeath').play();
currency += self.reward;
updateUI();
tween(enemyGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500
});
}
};
return self;
});
var HeavyWarrior = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace graphics with heavy warrior
self.removeChildAt(0);
var enemyGraphics = self.attachAsset('heavyWarrior', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 200;
self.health = self.maxHealth;
self.speed = 1.0;
self.originalSpeed = self.speed;
self.reward = 25;
self.enemyType = 'heavy';
return self;
});
var FastWarrior = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace graphics with fast warrior
self.removeChildAt(0);
var enemyGraphics = self.attachAsset('fastWarrior', {
anchorX: 0.5,
anchorY: 0.5
});
self.maxHealth = 60;
self.health = self.maxHealth;
self.speed = 3.5;
self.originalSpeed = self.speed;
self.reward = 15;
self.enemyType = 'fast';
return self;
});
var Boss = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace graphics with boss enemy
self.removeChildAt(0);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
enemyGraphics.tint = 0x8B0000; // Dark red for boss
self.maxHealth = 1000;
self.health = self.maxHealth;
self.speed = 1.0;
self.originalSpeed = self.speed;
self.reward = 100;
self.enemyType = 'boss';
self.isBoss = true;
self.lastSpecialAttack = 0;
self.specialAttackCooldown = 300; // 5 seconds at 60fps
self.healAmount = 50;
self.stun = false;
self.stunEndTime = 0;
// Boss special ability - healing
self.healSelf = function () {
self.health = Math.min(self.maxHealth, self.health + self.healAmount);
self.updateHealthBar();
// Healing visual effect
tween(enemyGraphics, {
tint: 0x00FF00,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyGraphics, {
tint: 0x8B0000,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.effects.flashObject(self, 0x00FF00, 500);
};
// Boss stun ability - temporarily disable nearby towers
self.stunTowers = function () {
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
var dx = tower.x - self.x;
var dy = tower.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 200) {
// Disable tower temporarily
tower.stunned = true;
tower.stunEndTime = LK.ticks + 180; // 3 seconds
// Visual effect
tween(tower, {
tint: 0x666666,
alpha: 0.5
}, {
duration: 200
});
}
}
// Boss stun visual effect
LK.effects.flashScreen(0xFF0000, 500);
};
self.takeDamage = function (damage, damageType) {
var actualDamage = damage * 0.8; // Boss takes 20% less damage
self.health -= actualDamage;
self.updateHealthBar();
if (self.health <= 0 && !self.isDead) {
self.isDead = true;
LK.getSound('enemyDeath').play();
currency += self.reward;
diamonds += 5; // Boss drops diamonds
updateUI();
// Epic boss death animation
tween(enemyGraphics, {
scaleX: 3,
scaleY: 3,
tint: 0xFFD700,
rotation: Math.PI * 4
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(enemyGraphics, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeIn
});
}
});
LK.effects.flashScreen(0xFFD700, 1000);
}
};
self.update = function () {
if (self.isDead || self.reachedBase) return;
// Check if slow effect should end
if (self.isSlowed && LK.ticks >= self.slowEndTime) {
self.speed = self.originalSpeed;
self.isSlowed = false;
enemyGraphics.tint = 0x8B0000; // Reset boss tint
}
// Boss special attacks
if (LK.ticks - self.lastSpecialAttack >= self.specialAttackCooldown) {
var specialType = Math.floor(Math.random() * 2);
if (specialType === 0) {
self.healSelf();
} else {
self.stunTowers();
}
self.lastSpecialAttack = LK.ticks;
}
if (self.pathIndex < enemyPath.length) {
var target = enemyPath[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 >= enemyPath.length) {
self.reachedBase = true;
lives -= 5; // Boss deals more damage to base
updateUI();
return;
}
}
var moveX = dx / distance * self.speed;
var moveY = dy / distance * self.speed;
self.x += moveX;
self.y += moveY;
}
};
return self;
});
var Tower = Container.expand(function () {
var self = Container.call(this);
var towerGraphics = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 150;
self.damage = 25;
self.fireRate = 60;
self.lastFire = 0;
self.cost = 50;
self.level = 1;
self.maxLevel = 3;
self.showingRange = false;
self.upgradeCost = 75;
var rangeIndicator = self.attachAsset('towerRange', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
});
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.visible = false;
self.showRange = function () {
rangeIndicator.visible = true;
self.showingRange = true;
};
self.hideRange = function () {
rangeIndicator.visible = false;
self.showingRange = false;
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead || enemy.reachedBase) continue;
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) {
closestDistance = distance;
closestEnemy = enemy;
}
}
return closestEnemy;
};
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = target;
bullet.damage = self.damage;
// Bullet spawn animation
bullet.alpha = 0;
bullet.scaleX = 0.5;
bullet.scaleY = 0.5;
bullets.push(bullet);
game.addChild(bullet);
// Animate bullet appearance
tween(bullet, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOut
});
// Tower fire animation
var originalScale = towerGraphics.scaleX;
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: originalScale,
scaleY: originalScale
}, {
duration: 100,
easing: tween.easeIn
});
}
});
self.lastFire = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 15;
self.range += 25;
self.fireRate = Math.max(30, self.fireRate - 10);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
// Update range indicator
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
// Enhanced visual upgrade animation
tween(towerGraphics, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0x00ff00
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
var finalTint = self.level === 2 ? 0x3A7BD5 : 0x2E5BBA;
tween(towerGraphics, {
scaleX: 1,
scaleY: 1,
tint: finalTint
}, {
duration: 200,
easing: tween.easeOut
});
}
});
// Upgrade sparkle effect
LK.effects.flashObject(self, 0xffd700, 800);
updateUI();
return true;
};
self.down = function (x, y, obj) {
// Always start dragging on click
draggingTower = self;
selectedTower = self;
// Use x, y parameters directly instead of trying to convert coordinates
dragOffset.x = x - self.x;
dragOffset.y = y - self.y;
originalTowerPosition.x = self.x;
originalTowerPosition.y = self.y;
// Find original grid position
var gridPos = getGridPosition(self.x, self.y);
if (gridPos) {
originalGridPosition.x = gridPos.x;
originalGridPosition.y = gridPos.y;
grid[gridPos.x][gridPos.y].occupied = false;
}
// Add drag visual effect
tween(self, {
alpha: 0.7,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
hideAllRanges();
self.showRange();
};
self.update = function () {
// Check if tower is stunned by boss
if (self.stunned && LK.ticks >= self.stunEndTime) {
self.stunned = false;
// Reset visual effects
tween(self, {
tint: 0xFFFFFF,
alpha: 1
}, {
duration: 300
});
}
if (self.stunned) return; // Skip firing when stunned
var target = self.findTarget();
if (target) {
self.fire(target);
}
};
return self;
});
var SuperTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with super tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
towerGraphics.tint = 0xFFD700; // Gold color for super tower
self.range = 300;
self.damage = 100;
self.fireRate = 20;
self.cost = 0; // Free tower from rocket
self.level = 1;
self.maxLevel = 5; // 5 upgrade levels
self.upgradeCost = 200;
self.towerType = 'super';
self.multiShot = 3; // Fires 3 bullets at once
self.piercing = true; // Bullets can hit multiple enemies
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0xFFD700;
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
// Multi-shot bullets
for (var shot = 0; shot < self.multiShot; shot++) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = target;
bullet.damage = self.damage;
bullet.isPiercing = self.piercing;
bullet.hitTargets = []; // Track hit targets for piercing
// Spread bullets slightly
var angleOffset = (shot - Math.floor(self.multiShot / 2)) * 0.2;
bullet.angleOffset = angleOffset;
// Super bullet visual
bullet.children[0].tint = 0xFFD700;
bullet.children[0].scaleX = 1.5;
bullet.children[0].scaleY = 1.5;
bullets.push(bullet);
game.addChild(bullet);
}
// Super tower fire animation
tween(towerGraphics, {
scaleX: 2,
scaleY: 2,
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFD700
}, {
duration: 150,
easing: tween.easeIn
});
}
});
self.lastFire = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 50;
self.range += 50;
self.fireRate = Math.max(10, self.fireRate - 3);
self.multiShot = Math.min(5, self.multiShot + 1);
self.upgradeCost = Math.floor(self.upgradeCost * 1.8);
// Update range indicator
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
// Epic upgrade animation
tween(towerGraphics, {
scaleX: 2.5,
scaleY: 2.5,
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
var levelTint = [0xFFD700, 0xFFA500, 0xFF8C00, 0xFF6347, 0xFF0000][self.level - 1];
tween(towerGraphics, {
scaleX: 1.5,
scaleY: 1.5,
tint: levelTint
}, {
duration: 300,
easing: tween.easeOut
});
}
});
LK.effects.flashObject(self, 0xFFD700, 1000);
updateUI();
return true;
};
return self;
});
var MagicTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with magic tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('magicTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 130;
self.damage = 20;
self.fireRate = 50;
self.cost = 80;
self.upgradeCost = 120;
self.towerType = 'magic';
self.slowEffect = 0.5;
self.slowDuration = 120;
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0xe67e22;
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
// Create slowing bullet
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = target;
bullet.damage = self.damage;
bullet.slowEffect = self.slowEffect;
bullet.slowDuration = self.slowDuration;
bullet.isMagic = true;
bullets.push(bullet);
game.addChild(bullet);
self.lastFire = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 12;
self.range += 25;
self.slowEffect = Math.max(0.2, self.slowEffect - 0.1);
self.slowDuration += 30;
self.fireRate = Math.max(30, self.fireRate - 10);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
towerGraphics.tint = self.level === 2 ? 0xD35400 : 0xA04000;
updateUI();
return true;
};
return self;
});
var LightningTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with lightning tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('lightningTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 140;
self.damage = 35;
self.fireRate = 80;
self.cost = 120;
self.upgradeCost = 180;
self.towerType = 'lightning';
self.chainCount = 3;
self.chainRadius = 100;
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0xf1c40f;
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
// Lightning chain effect
var chainTargets = [target];
var currentTarget = target;
// Find chain targets
for (var c = 0; c < self.chainCount - 1; c++) {
var nextTarget = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead || enemy.reachedBase) continue;
if (chainTargets.indexOf(enemy) !== -1) continue;
var dx = enemy.x - currentTarget.x;
var dy = enemy.y - currentTarget.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.chainRadius && distance < closestDistance) {
closestDistance = distance;
nextTarget = enemy;
}
}
if (nextTarget) {
chainTargets.push(nextTarget);
currentTarget = nextTarget;
}
}
// Apply damage to all chain targets
for (var i = 0; i < chainTargets.length; i++) {
var chainTarget = chainTargets[i];
var chainDamage = self.damage * (1 - i * 0.2); // Decreasing damage
chainTarget.takeDamage(chainDamage);
// Lightning effect animation
tween(chainTarget, {
tint: 0xf1c40f
}, {
duration: 100,
onFinish: function onFinish() {
tween(chainTarget, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
// Tower fire animation
tween(towerGraphics, {
tint: 0xf1c40f,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
self.lastFire = LK.ticks;
LK.getSound('lightning').play();
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 18;
self.range += 20;
self.chainCount++;
self.chainRadius += 20;
self.fireRate = Math.max(50, self.fireRate - 10);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
towerGraphics.tint = self.level === 2 ? 0xd4ac0d : 0xb7950b;
updateUI();
return true;
};
return self;
});
var LaserTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with laser tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('laserTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 160;
self.damage = 8;
self.fireRate = 10;
self.cost = 110;
self.upgradeCost = 160;
self.towerType = 'laser';
self.currentTarget = null;
self.beamDuration = 0;
self.maxBeamDuration = 300;
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0xe74c3c;
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
// Continuous laser beam
if (self.currentTarget === target) {
self.beamDuration++;
// Increasing damage over time
var damageMultiplier = 1 + self.beamDuration / 100;
target.takeDamage(self.damage * damageMultiplier);
} else {
self.currentTarget = target;
self.beamDuration = 0;
target.takeDamage(self.damage);
}
// Laser beam visual effect
tween(target, {
tint: 0xe74c3c
}, {
duration: 50,
onFinish: function onFinish() {
tween(target, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
// Tower rotation towards target
var angle = Math.atan2(target.y - self.y, target.x - self.x);
tween(towerGraphics, {
rotation: angle
}, {
duration: 100,
easing: tween.easeOut
});
self.lastFire = LK.ticks;
if (self.beamDuration % 30 === 0) {
LK.getSound('laser').play();
}
};
self.findTarget = function () {
// Prefer to keep current target if still in range
if (self.currentTarget && !self.currentTarget.isDead && !self.currentTarget.reachedBase) {
var dx = self.currentTarget.x - self.x;
var dy = self.currentTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.range) {
return self.currentTarget;
}
}
// Find new target if current is lost
self.currentTarget = null;
self.beamDuration = 0;
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead || enemy.reachedBase) continue;
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) {
closestDistance = distance;
closestEnemy = enemy;
}
}
return closestEnemy;
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 5;
self.range += 25;
self.maxBeamDuration += 100;
self.fireRate = Math.max(5, self.fireRate - 2);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
towerGraphics.tint = self.level === 2 ? 0xc0392b : 0xa93226;
updateUI();
return true;
};
return self;
});
var FreezeTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with freeze tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('freezeTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 120;
self.damage = 10;
self.fireRate = 120;
self.cost = 90;
self.upgradeCost = 140;
self.towerType = 'freeze';
self.freezeRadius = 80;
self.freezeDuration = 180;
self.freezeEffect = 0.1;
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0x7fb3d3;
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
// Area freeze effect
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead || enemy.reachedBase) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.freezeRadius) {
enemy.takeDamage(self.damage);
enemy.applySlow(self.freezeEffect, self.freezeDuration);
// Freeze effect animation
tween(enemy, {
tint: 0x7fb3d3
}, {
duration: 200,
onFinish: function onFinish() {
tween(enemy, {
tint: 0x3498db
}, {
duration: self.freezeDuration * 16
});
}
});
}
}
// Tower fire animation with pulse effect
tween(towerGraphics, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0x3498db
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.bounceOut
});
}
});
self.lastFire = LK.ticks;
LK.getSound('freeze').play();
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 8;
self.range += 15;
self.freezeRadius += 20;
self.freezeDuration += 60;
self.freezeEffect = Math.max(0.05, self.freezeEffect - 0.025);
self.fireRate = Math.max(80, self.fireRate - 15);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
towerGraphics.tint = self.level === 2 ? 0x5dade2 : 0x3498db;
updateUI();
return true;
};
return self;
});
var CannonTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with cannon tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('cannonTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 120;
self.damage = 40;
self.fireRate = 90;
self.cost = 100;
self.upgradeCost = 150;
self.towerType = 'cannon';
self.splashRadius = 80;
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0x34495e;
self.fire = function (target) {
if (LK.ticks - self.lastFire < self.fireRate) return;
// Create splash damage bullet
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.target = target;
bullet.damage = self.damage;
bullet.splashRadius = self.splashRadius;
bullet.isSplash = true;
bullets.push(bullet);
game.addChild(bullet);
self.lastFire = LK.ticks;
LK.getSound('shoot').play();
};
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 20;
self.range += 20;
self.splashRadius += 20;
self.fireRate = Math.max(60, self.fireRate - 15);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
towerGraphics.tint = self.level === 2 ? 0x2C3E50 : 0x1B2631;
updateUI();
return true;
};
return self;
});
var ArcherTower = Tower.expand(function () {
var self = Tower.call(this);
// Replace graphics with archer tower
self.removeChildAt(0);
var towerGraphics = self.attachAsset('archerTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.range = 200;
self.damage = 15;
self.fireRate = 40;
self.cost = 75;
self.upgradeCost = 100;
self.towerType = 'archer';
// Update range indicator
var rangeIndicator = self.children[self.children.length - 1];
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
rangeIndicator.tint = 0x8e44ad;
self.upgrade = function () {
if (self.level >= self.maxLevel || currency < self.upgradeCost) return false;
currency -= self.upgradeCost;
self.level++;
self.damage += 10;
self.range += 30;
self.fireRate = Math.max(25, self.fireRate - 5);
self.upgradeCost = Math.floor(self.upgradeCost * 1.5);
rangeIndicator.width = self.range * 2;
rangeIndicator.height = self.range * 2;
towerGraphics.tint = self.level === 2 ? 0x7D3C98 : 0x6C3483;
updateUI();
return true;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var gridSize = 100;
var mapWidth = 20;
var mapHeight = 25;
var currency = 390;
var diamonds = 0;
var lives = 20;
var wave = 1;
var enemiesInWave = 5;
var enemiesSpawned = 0;
var waveActive = false;
var gameOver = false;
var towers = [];
var enemies = [];
var bullets = [];
var grid = [];
var enemyPath = [];
var selectedTower = null;
var placingTower = false;
var selectedTowerType = 'basic';
var draggingTower = null;
var dragOffset = {
x: 0,
y: 0
};
var originalTowerPosition = {
x: 0,
y: 0
};
var originalGridPosition = {
x: 0,
y: 0
};
var rocketDelivered = false;
var superTowerUnlocked = false;
// Create UI elements
var currencyTxt = new Text2('Gold: 390', {
size: 60,
fill: 0xFFD700
});
currencyTxt.anchor.set(0, 0);
currencyTxt.x = 120;
currencyTxt.y = 20;
LK.gui.topLeft.addChild(currencyTxt);
var diamondsTxt = new Text2('Diamonds: 0', {
size: 60,
fill: 0x00FFFF
});
diamondsTxt.anchor.set(0, 0);
diamondsTxt.x = 120;
diamondsTxt.y = 90;
LK.gui.topLeft.addChild(diamondsTxt);
var livesTxt = new Text2('Lives: 20', {
size: 60,
fill: 0xFF0000
});
livesTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(livesTxt);
var waveTxt = new Text2('Wave: 1', {
size: 60,
fill: 0xFFFFFF
});
waveTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(waveTxt);
var startWaveBtn = new Text2('Start Wave', {
size: 80,
fill: 0x00FF00
});
startWaveBtn.anchor.set(0.5, 1);
startWaveBtn.y = -20;
LK.gui.bottom.addChild(startWaveBtn);
var buyTowerBtn = new Text2('Basic Tower (50g)', {
size: 60,
fill: 0x4A90E2
});
buyTowerBtn.anchor.set(0, 1);
buyTowerBtn.x = 20;
buyTowerBtn.y = -20;
LK.gui.bottomLeft.addChild(buyTowerBtn);
var buyArcherBtn = new Text2('Archer Tower (75g)', {
size: 60,
fill: 0x8e44ad
});
buyArcherBtn.anchor.set(0, 1);
buyArcherBtn.x = 20;
buyArcherBtn.y = -80;
LK.gui.bottomLeft.addChild(buyArcherBtn);
var buyCannonBtn = new Text2('Cannon Tower (100g)', {
size: 60,
fill: 0x34495e
});
buyCannonBtn.anchor.set(0, 1);
buyCannonBtn.x = 20;
buyCannonBtn.y = -140;
LK.gui.bottomLeft.addChild(buyCannonBtn);
var buyMagicBtn = new Text2('Magic Tower (80g)', {
size: 60,
fill: 0xe67e22
});
buyMagicBtn.anchor.set(0, 1);
buyMagicBtn.x = 20;
buyMagicBtn.y = -200;
LK.gui.bottomLeft.addChild(buyMagicBtn);
var buyLightningBtn = new Text2('Lightning Tower (120g)', {
size: 60,
fill: 0xf1c40f
});
buyLightningBtn.anchor.set(0, 1);
buyLightningBtn.x = 20;
buyLightningBtn.y = -260;
LK.gui.bottomLeft.addChild(buyLightningBtn);
var buyFreezeBtn = new Text2('Freeze Tower (90g)', {
size: 60,
fill: 0x7fb3d3
});
buyFreezeBtn.anchor.set(0, 1);
buyFreezeBtn.x = 20;
buyFreezeBtn.y = -320;
LK.gui.bottomLeft.addChild(buyFreezeBtn);
var buyLaserBtn = new Text2('Laser Tower (110g)', {
size: 60,
fill: 0xe74c3c
});
buyLaserBtn.anchor.set(0, 1);
buyLaserBtn.x = 20;
buyLaserBtn.y = -380;
LK.gui.bottomLeft.addChild(buyLaserBtn);
var buySuperBtn = new Text2('SUPER TOWER (FREE)', {
size: 60,
fill: 0xFFD700
});
buySuperBtn.anchor.set(0, 1);
buySuperBtn.x = 20;
buySuperBtn.y = -440;
buySuperBtn.visible = false;
LK.gui.bottomLeft.addChild(buySuperBtn);
var upgradeTowerBtn = new Text2('Upgrade Tower', {
size: 70,
fill: 0xFF6B35
});
upgradeTowerBtn.anchor.set(0, 1);
upgradeTowerBtn.x = 20;
upgradeTowerBtn.y = -100;
upgradeTowerBtn.visible = false;
LK.gui.bottomLeft.addChild(upgradeTowerBtn);
var enhanceAllBtn = new Text2('Enhance All Towers (10♦)', {
size: 60,
fill: 0x00FFFF
});
enhanceAllBtn.anchor.set(1, 1);
enhanceAllBtn.x = -20;
enhanceAllBtn.y = -20;
LK.gui.bottomRight.addChild(enhanceAllBtn);
var superDamageBtn = new Text2('Super Damage (15♦)', {
size: 60,
fill: 0xFF6B35
});
superDamageBtn.anchor.set(1, 1);
superDamageBtn.x = -20;
superDamageBtn.y = -80;
LK.gui.bottomRight.addChild(superDamageBtn);
var rapidFireBtn = new Text2('Rapid Fire (12♦)', {
size: 60,
fill: 0xFFFF00
});
rapidFireBtn.anchor.set(1, 1);
rapidFireBtn.x = -20;
rapidFireBtn.y = -140;
LK.gui.bottomRight.addChild(rapidFireBtn);
function updateUI() {
currencyTxt.setText('Gold: ' + currency);
diamondsTxt.setText('Diamonds: ' + diamonds);
livesTxt.setText('Lives: ' + lives);
waveTxt.setText('Wave: ' + wave);
if (currency >= 50) {
buyTowerBtn.tint = 0x4A90E2;
} else {
buyTowerBtn.tint = 0x666666;
}
if (currency >= 75) {
buyArcherBtn.tint = 0x8e44ad;
} else {
buyArcherBtn.tint = 0x666666;
}
if (currency >= 100) {
buyCannonBtn.tint = 0x34495e;
} else {
buyCannonBtn.tint = 0x666666;
}
if (currency >= 80) {
buyMagicBtn.tint = 0xe67e22;
} else {
buyMagicBtn.tint = 0x666666;
}
if (currency >= 120) {
buyLightningBtn.tint = 0xf1c40f;
} else {
buyLightningBtn.tint = 0x666666;
}
if (currency >= 90) {
buyFreezeBtn.tint = 0x7fb3d3;
} else {
buyFreezeBtn.tint = 0x666666;
}
if (currency >= 110) {
buyLaserBtn.tint = 0xe74c3c;
} else {
buyLaserBtn.tint = 0x666666;
}
// Update upgrade button visibility and state
if (selectedTower && selectedTower.level < selectedTower.maxLevel) {
upgradeTowerBtn.visible = true;
upgradeTowerBtn.setText('Upgrade Lv' + selectedTower.level + ' (' + selectedTower.upgradeCost + 'g)');
if (currency >= selectedTower.upgradeCost) {
upgradeTowerBtn.tint = 0xFF6B35;
} else {
upgradeTowerBtn.tint = 0x666666;
}
} else {
upgradeTowerBtn.visible = false;
}
// Update diamond enhancement buttons
if (diamonds >= 10) {
enhanceAllBtn.tint = 0x00FFFF;
} else {
enhanceAllBtn.tint = 0x666666;
}
if (diamonds >= 15) {
superDamageBtn.tint = 0xFF6B35;
} else {
superDamageBtn.tint = 0x666666;
}
if (diamonds >= 12) {
rapidFireBtn.tint = 0xFFFF00;
} else {
rapidFireBtn.tint = 0x666666;
}
// Update super tower button visibility
if (superTowerUnlocked) {
buySuperBtn.visible = true;
buySuperBtn.tint = 0xFFD700;
} else {
buySuperBtn.visible = false;
}
}
function hideAllRanges() {
for (var i = 0; i < towers.length; i++) {
towers[i].hideRange();
}
}
function initializeGrid() {
for (var x = 0; x < mapWidth; x++) {
grid[x] = [];
for (var y = 0; y < mapHeight; y++) {
grid[x][y] = {
x: x * gridSize + gridSize / 2,
y: y * gridSize + gridSize / 2,
occupied: false,
isPath: false
};
}
}
}
function createPath() {
enemyPath = [];
var pathCoords = [{
x: 0,
y: 12
}, {
x: 1,
y: 12
}, {
x: 2,
y: 12
}, {
x: 3,
y: 12
}, {
x: 4,
y: 12
}, {
x: 5,
y: 12
}, {
x: 6,
y: 12
}, {
x: 7,
y: 12
}, {
x: 8,
y: 12
}, {
x: 8,
y: 11
}, {
x: 8,
y: 10
}, {
x: 8,
y: 9
}, {
x: 8,
y: 8
}, {
x: 9,
y: 8
}, {
x: 10,
y: 8
}, {
x: 11,
y: 8
}, {
x: 12,
y: 8
}, {
x: 12,
y: 9
}, {
x: 12,
y: 10
}, {
x: 12,
y: 11
}, {
x: 12,
y: 12
}, {
x: 13,
y: 12
}, {
x: 14,
y: 12
}, {
x: 15,
y: 12
}, {
x: 16,
y: 12
}, {
x: 17,
y: 12
}, {
x: 18,
y: 12
}, {
x: 19,
y: 12
}];
for (var i = 0; i < pathCoords.length; i++) {
var coord = pathCoords[i];
if (coord.x < mapWidth && coord.y < mapHeight) {
grid[coord.x][coord.y].isPath = true;
enemyPath.push({
x: coord.x * gridSize + gridSize / 2,
y: coord.y * gridSize + gridSize / 2
});
}
}
}
function drawMap() {
for (var x = 0; x < mapWidth; x++) {
for (var y = 0; y < mapHeight; y++) {
var tile;
if (grid[x][y].isPath) {
tile = LK.getAsset('pathTile', {
x: x * gridSize,
y: y * gridSize
});
} else {
tile = LK.getAsset('grassTile', {
x: x * gridSize,
y: y * gridSize
});
}
game.addChild(tile);
}
}
}
function drawBase() {
var base = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
x: (mapWidth - 1) * gridSize + gridSize / 2,
y: 12 * gridSize + gridSize / 2
});
game.addChild(base);
}
function spawnEnemy() {
if (enemiesSpawned >= enemiesInWave) return;
var enemy;
// Spawn ultra boss after wave 10 and every 5 waves after that
if (wave > 10 && wave % 5 === 0 && enemiesSpawned === enemiesInWave - 1) {
enemy = new UltraBoss();
} else if (wave % 10 === 0 && enemiesSpawned === enemiesInWave - 1) {
// Spawn regular boss on wave 10 and every 10 waves
enemy = new Boss();
} else {
var enemyType = Math.floor(Math.random() * 4);
if (enemyType === 0) {
enemy = new Enemy();
} else if (enemyType === 1) {
enemy = new FastWarrior();
} else if (enemyType === 2) {
enemy = new HeavyWarrior();
} else {
enemy = new MagicWarrior();
}
}
enemy.x = gridSize / 2;
enemy.y = 12 * gridSize + gridSize / 2;
// Scale stats with wave
enemy.health = enemy.maxHealth + (wave - 1) * 20;
enemy.maxHealth = enemy.health;
enemy.speed = enemy.originalSpeed + (wave - 1) * 0.2;
enemy.originalSpeed = enemy.speed;
enemy.reward = enemy.reward + (wave - 1) * 2;
// Spawn animation
enemy.alpha = 0;
enemy.scaleX = 0.1;
enemy.scaleY = 0.1;
enemies.push(enemy);
game.addChild(enemy);
// Enhanced enemy entrance animation with rotation
var spawnAnimationType = Math.floor(Math.random() * 3);
if (spawnAnimationType === 0) {
// Bounce entrance
tween(enemy, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.bounceOut
});
} else if (spawnAnimationType === 1) {
// Spiral entrance
enemy.rotation = Math.PI * 4;
tween(enemy, {
alpha: 1,
scaleX: 1,
scaleY: 1,
rotation: 0
}, {
duration: 600,
easing: tween.easeOut
});
} else {
// Elastic entrance
tween(enemy, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 700,
easing: tween.elasticOut
});
}
enemiesSpawned++;
}
function getGridPosition(x, y) {
var gridX = Math.floor(x / gridSize);
var gridY = Math.floor(y / gridSize);
if (gridX >= 0 && gridX < mapWidth && gridY >= 0 && gridY < mapHeight) {
return {
x: gridX,
y: gridY
};
}
return null;
}
function canPlaceTower(gridX, gridY) {
return !grid[gridX][gridY].occupied && !grid[gridX][gridY].isPath;
}
function placeTower(gridX, gridY) {
var tower;
var cost;
if (selectedTowerType === 'basic') {
cost = 50;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new Tower();
} else if (selectedTowerType === 'archer') {
cost = 75;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new ArcherTower();
} else if (selectedTowerType === 'cannon') {
cost = 100;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new CannonTower();
} else if (selectedTowerType === 'magic') {
cost = 80;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new MagicTower();
} else if (selectedTowerType === 'lightning') {
cost = 120;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new LightningTower();
} else if (selectedTowerType === 'freeze') {
cost = 90;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new FreezeTower();
} else if (selectedTowerType === 'laser') {
cost = 110;
if (!canPlaceTower(gridX, gridY) || currency < cost) return false;
tower = new LaserTower();
} else if (selectedTowerType === 'super') {
cost = 0; // Super tower is free
if (!canPlaceTower(gridX, gridY)) return false;
tower = new SuperTower();
}
tower.x = gridX * gridSize + gridSize / 2;
tower.y = gridY * gridSize + gridSize / 2;
// Placement animation
tower.alpha = 0;
tower.scaleX = 0.1;
tower.scaleY = 0.1;
towers.push(tower);
game.addChild(tower);
// Animate tower placement
tween(tower, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
grid[gridX][gridY].occupied = true;
currency -= cost;
placingTower = false;
updateUI();
return true;
}
function startWave() {
if (waveActive) return;
waveActive = true;
enemiesSpawned = 0;
enemiesInWave = 5 + wave * 2;
startWaveBtn.setText('Wave Active');
startWaveBtn.tint = 0x666666;
// Special rocket animation for wave 20
if (wave === 20 && !rocketDelivered) {
deliverSuperTowerRocket();
rocketDelivered = true;
}
// Wave start animation
tween(startWaveBtn, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(startWaveBtn, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
// Screen flash effect for wave start
LK.effects.flashScreen(0x00FF00, 300);
}
function deliverSuperTowerRocket() {
// Create rocket
var rocket = LK.getAsset('tower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 2,
x: 2048 / 2,
y: -100
});
rocket.tint = 0xFF4500;
rocket.rotation = Math.PI;
game.addChild(rocket);
// Rocket descent animation
tween(rocket, {
y: 2732 / 2,
rotation: 0
}, {
duration: 2000,
easing: tween.easeIn,
onFinish: function onFinish() {
// Explosion effect
LK.effects.flashScreen(0xFFD700, 1000);
// Remove rocket and show super tower unlock message
rocket.destroy();
superTowerUnlocked = true;
// Create super tower unlock notification
var unlockMsg = new Text2('SUPER TOWER UNLOCKED!', {
size: 120,
fill: 0xFFD700
});
unlockMsg.anchor.set(0.5, 0.5);
unlockMsg.x = 2048 / 2;
unlockMsg.y = 2732 / 2;
unlockMsg.alpha = 0;
game.addChild(unlockMsg);
// Animate unlock message
tween(unlockMsg, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(unlockMsg, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
unlockMsg.destroy();
}
});
}
});
updateUI();
}
});
}
function checkWaveComplete() {
if (!waveActive) return;
var allEnemiesGone = true;
for (var i = 0; i < enemies.length; i++) {
if (!enemies[i].isDead && !enemies[i].reachedBase) {
allEnemiesGone = false;
break;
}
}
if (enemiesSpawned >= enemiesInWave && allEnemiesGone) {
waveActive = false;
wave++;
currency += 25;
diamonds += 5; // Award 5 diamonds per wave
startWaveBtn.setText('Start Wave');
startWaveBtn.tint = 0x00FF00;
updateUI();
// Clean up dead enemies
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i].isDead || enemies[i].reachedBase) {
enemies[i].destroy();
enemies.splice(i, 1);
}
}
}
}
// Initialize game
initializeGrid();
createPath();
drawMap();
drawBase();
updateUI();
// Event handlers
startWaveBtn.down = function () {
startWave();
};
buyTowerBtn.down = function () {
if (currency >= 50) {
selectedTowerType = 'basic';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
buyArcherBtn.down = function () {
if (currency >= 75) {
selectedTowerType = 'archer';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
buyCannonBtn.down = function () {
if (currency >= 100) {
selectedTowerType = 'cannon';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
buyMagicBtn.down = function () {
if (currency >= 80) {
selectedTowerType = 'magic';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
buyLightningBtn.down = function () {
if (currency >= 120) {
selectedTowerType = 'lightning';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
buyFreezeBtn.down = function () {
if (currency >= 90) {
selectedTowerType = 'freeze';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
buyLaserBtn.down = function () {
if (currency >= 110) {
selectedTowerType = 'laser';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
upgradeTowerBtn.down = function () {
if (selectedTower && selectedTower.upgrade()) {
updateUI();
}
};
enhanceAllBtn.down = function () {
if (diamonds >= 10) {
diamonds -= 10;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
tower.damage = Math.floor(tower.damage * 1.5);
tower.range = Math.floor(tower.range * 1.2);
// Visual enhancement effect
tween(tower, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FFFF
}, {
duration: 400,
easing: tween.bounceOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
LK.effects.flashScreen(0x00FFFF, 500);
updateUI();
}
};
superDamageBtn.down = function () {
if (diamonds >= 15) {
diamonds -= 15;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
tower.damage = Math.floor(tower.damage * 2.5);
// Super damage visual effect
tween(tower, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF6B35
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
tween(tower, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 400
});
}
});
}
LK.effects.flashScreen(0xFF6B35, 800);
updateUI();
}
};
rapidFireBtn.down = function () {
if (diamonds >= 12) {
diamonds -= 12;
for (var i = 0; i < towers.length; i++) {
var tower = towers[i];
tower.fireRate = Math.max(10, Math.floor(tower.fireRate * 0.5));
// Rapid fire visual effect
tween(tower, {
tint: 0xFFFF00
}, {
duration: 200,
onFinish: function onFinish() {
tween(tower, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
LK.effects.flashScreen(0xFFFF00, 600);
updateUI();
}
};
buySuperBtn.down = function () {
if (superTowerUnlocked) {
selectedTowerType = 'super';
placingTower = true;
selectedTower = null;
hideAllRanges();
updateUI();
}
};
game.move = function (x, y, obj) {
if (draggingTower) {
draggingTower.x = x - dragOffset.x;
draggingTower.y = y - dragOffset.y;
// Visual feedback for valid/invalid placement
var gridPos = getGridPosition(draggingTower.x, draggingTower.y);
if (gridPos && canPlaceTower(gridPos.x, gridPos.y)) {
draggingTower.tint = 0x00FF00; // Green for valid
} else {
draggingTower.tint = 0xFF0000; // Red for invalid
}
}
};
game.down = function (x, y, obj) {
if (placingTower) {
var gridPos = getGridPosition(x, y);
if (gridPos) {
placeTower(gridPos.x, gridPos.y);
}
} else {
// Clear tower selection if clicking on empty space
var gridPos = getGridPosition(x, y);
if (gridPos && !grid[gridPos.x][gridPos.y].occupied) {
selectedTower = null;
hideAllRanges();
updateUI();
}
}
};
game.up = function (x, y, obj) {
if (draggingTower) {
var gridPos = getGridPosition(draggingTower.x, draggingTower.y);
var canPlace = gridPos && canPlaceTower(gridPos.x, gridPos.y);
if (canPlace) {
// Successfully place tower at new position
draggingTower.x = gridPos.x * gridSize + gridSize / 2;
draggingTower.y = gridPos.y * gridSize + gridSize / 2;
grid[gridPos.x][gridPos.y].occupied = true;
// Smooth placement animation
tween(draggingTower, {
alpha: 1,
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.bounceOut
});
} else {
// Return to original position
tween(draggingTower, {
x: originalTowerPosition.x,
y: originalTowerPosition.y,
alpha: 1,
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 400,
easing: tween.elasticOut
});
// Restore original grid position
grid[originalGridPosition.x][originalGridPosition.y].occupied = true;
}
draggingTower = null;
}
};
// Main game loop
game.update = function () {
if (lives <= 0 && !gameOver) {
gameOver = true;
LK.showGameOver();
return;
}
if (wave > 20) {
LK.showYouWin();
return;
}
// Spawn enemies during wave with decreasing interval
var spawnInterval = Math.max(45, 90 - wave * 2);
if (waveActive && LK.ticks % spawnInterval === 0) {
spawnEnemy();
}
// Clean up bullets that hit targets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.hasHit || !bullet.target || bullet.target.isDead) {
bullet.destroy();
bullets.splice(i, 1);
}
}
checkWaveComplete();
};