User prompt
güçlendirmeleri kaydetme ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
güçlendirmeleri büyüt ve sağ üst köşeye koy
User prompt
hayır savaş alanına koyulan karakterlere tıklanınca güçlendirmeler çıksın ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
karakter güçlendirme ekle ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
karakter koyma tuşları kare olsun arkaso
User prompt
karakter koyma tuşları birbirine deymesin
Code edit (1 edits merged)
Please save this source code
User prompt
Battle Command: War Simulator
Initial prompt
war simulator
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Base = Container.expand(function () { var self = Container.call(this); var baseGraphics = self.attachAsset('playerBase', { anchorX: 0.5, anchorY: 0.5 }); self.health = 500; self.maxHealth = 500; self.isDead = false; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; self.isDead = true; } }; return self; }); 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.targetX = 0; self.targetY = 0; self.isPlayerBullet = true; self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { self.x = self.targetX; self.y = self.targetY; self.hasReachedTarget = true; } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); self.health = 100; self.maxHealth = 100; self.speed = 1; self.damage = 30; self.isDead = false; self.hasReachedBase = false; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.health = 0; self.isDead = true; } }; self.update = function () { if (self.isDead || self.hasReachedBase) return; var dx = playerBase.x - self.x; var dy = playerBase.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { self.hasReachedBase = true; playerBase.takeDamage(self.damage); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var TheBigBoss = Enemy.expand(function () { var self = Enemy.call(this); var bigBossGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); bigBossGraphics.tint = 0xFF0000; // Red color for the big boss bigBossGraphics.scaleX = 3.0; // Massive size bigBossGraphics.scaleY = 3.0; self.health = 1500 + waveNumber * 200; self.maxHealth = self.health; self.speed = 0.2; // Very slow but menacing self.damage = 250; self.attackRange = 500; self.attackCooldown = 0; self.attackDelay = 60; // 1 second self.megaAttackCooldown = 0; self.megaAttackDelay = 300; // 5 seconds self.spawnCooldown = 0; self.spawnDelay = 600; // 10 seconds self.isBoss = true; self.isHardModeOnly = true; self.update = function () { if (self.isDead || self.hasReachedBase) return; // Regular movement towards base var dx = playerBase.x - self.x; var dy = playerBase.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 120) { self.hasReachedBase = true; playerBase.takeDamage(self.damage); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Continuous attack - shoots at nearest unit if (self.attackCooldown <= 0) { var nearestUnit = null; var shortestDistance = self.attackRange; for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; if (unit.isDead) continue; var udx = unit.x - self.x; var udy = unit.y - self.y; var udistance = Math.sqrt(udx * udx + udy * udy); if (udistance < shortestDistance) { shortestDistance = udistance; nearestUnit = unit; } } if (nearestUnit) { var bigBossBullet = new Bullet(); bigBossBullet.x = self.x; bigBossBullet.y = self.y; bigBossBullet.targetX = nearestUnit.x; bigBossBullet.targetY = nearestUnit.y; bigBossBullet.damage = 120; bigBossBullet.isPlayerBullet = false; bigBossBullet.speed = 8; bullets.push(bigBossBullet); game.addChild(bigBossBullet); self.attackCooldown = self.attackDelay; } } if (self.attackCooldown > 0) { self.attackCooldown--; } // Mega attack - damages all units on screen if (self.megaAttackCooldown <= 0) { for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; if (!unit.isDead) { unit.takeDamage(80); createExplosion(unit.x, unit.y); } } // Screen flash effect LK.effects.flashScreen(0xFF0000, 500); self.megaAttackCooldown = self.megaAttackDelay; } if (self.megaAttackCooldown > 0) { self.megaAttackCooldown--; } // Spawn fast zombies if (self.spawnCooldown <= 0) { for (var j = 0; j < 3; j++) { var zombie = new FastZombie(); zombie.x = self.x + (Math.random() - 0.5) * 200; zombie.y = self.y + (Math.random() - 0.5) * 200; enemies.push(zombie); game.addChild(zombie); } self.spawnCooldown = self.spawnDelay; } if (self.spawnCooldown > 0) { self.spawnCooldown--; } }; return self; }); var MegaBoss = Enemy.expand(function () { var self = Enemy.call(this); var megaBossGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); megaBossGraphics.tint = 0x4B0082; megaBossGraphics.scaleX = 2.0; megaBossGraphics.scaleY = 2.0; self.health = 800 + waveNumber * 100; self.maxHealth = self.health; self.speed = 0.3; self.damage = 150; self.attackRange = 400; self.attackCooldown = 0; self.attackDelay = 90; self.multiShotCooldown = 0; self.multiShotDelay = 240; // 4 seconds self.healCooldown = 0; self.healDelay = 600; // 10 seconds self.isBoss = true; self.update = function () { if (self.isDead || self.hasReachedBase) return; // Regular movement towards base var dx = playerBase.x - self.x; var dy = playerBase.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { self.hasReachedBase = true; playerBase.takeDamage(self.damage); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Multi-shot attack if (self.multiShotCooldown <= 0) { // Shoot at up to 3 different targets var targetsHit = 0; for (var i = 0; i < playerUnits.length && targetsHit < 3; i++) { var unit = playerUnits[i]; if (unit.isDead) continue; var udx = unit.x - self.x; var udy = unit.y - self.y; var udistance = Math.sqrt(udx * udx + udy * udy); if (udistance <= self.attackRange) { var bossBullet = new Bullet(); bossBullet.x = self.x; bossBullet.y = self.y; bossBullet.targetX = unit.x; bossBullet.targetY = unit.y; bossBullet.damage = 80; bossBullet.isPlayerBullet = false; bossBullet.speed = 7; bullets.push(bossBullet); game.addChild(bossBullet); targetsHit++; } } if (targetsHit > 0) { self.multiShotCooldown = self.multiShotDelay; } } if (self.multiShotCooldown > 0) { self.multiShotCooldown--; } // Self heal ability if (self.healCooldown <= 0 && self.health < self.maxHealth * 0.7) { self.health = Math.min(self.maxHealth, self.health + 200); self.healCooldown = self.healDelay; // Visual healing effect var healEffect = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.7, scaleX: 0.5, scaleY: 0.5 }); healEffect.tint = 0x00FF00; game.addChild(healEffect); tween(healEffect, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { healEffect.destroy(); } }); } if (self.healCooldown > 0) { self.healCooldown--; } }; return self; }); var FastZombie = Enemy.expand(function () { var self = Enemy.call(this); var zombieGraphics = self.attachAsset('enemyInfantry', { anchorX: 0.5, anchorY: 0.5 }); zombieGraphics.tint = 0x800080; // Purple color for zombie zombieGraphics.scaleX = 0.8; zombieGraphics.scaleY = 0.8; self.health = 20; self.maxHealth = 20; self.speed = 3.5; // Very fast self.damage = 12; self.isHardModeOnly = true; return self; }); var EnemyTank = Enemy.expand(function () { var self = Enemy.call(this); var tankGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); self.health = 120; self.maxHealth = 120; self.speed = 0.8; self.damage = 40; return self; }); var EnemySwarm = Enemy.expand(function () { var self = Enemy.call(this); var swarmGraphics = self.attachAsset('enemyInfantry', { anchorX: 0.5, anchorY: 0.5 }); swarmGraphics.tint = 0xFFFF00; swarmGraphics.scaleX = 0.7; swarmGraphics.scaleY = 0.7; self.health = 25; self.maxHealth = 25; self.speed = 2.0; self.damage = 10; return self; }); var EnemyShielder = Enemy.expand(function () { var self = Enemy.call(this); var shielderGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); shielderGraphics.tint = 0x4169E1; shielderGraphics.scaleX = 1.1; shielderGraphics.scaleY = 1.1; self.health = 180; self.maxHealth = 180; self.speed = 0.7; self.damage = 35; self.shieldActive = true; self.takeDamage = function (damage) { // Shielder takes reduced damage when shield is active var actualDamage = self.shieldActive ? damage * 0.5 : damage; self.health -= actualDamage; if (self.health <= 0) { self.health = 0; self.isDead = true; } // Shield breaks at half health if (self.health <= self.maxHealth * 0.5) { self.shieldActive = false; shielderGraphics.tint = 0xFF4500; } }; return self; }); var EnemyInfantry = Enemy.expand(function () { var self = Enemy.call(this); var infantryGraphics = self.attachAsset('enemyInfantry', { anchorX: 0.5, anchorY: 0.5 }); self.health = 50; self.maxHealth = 50; self.speed = 1.5; self.damage = 25; return self; }); var EnemyHeavy = Enemy.expand(function () { var self = Enemy.call(this); var heavyGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); heavyGraphics.tint = 0x4B0082; heavyGraphics.scaleX = 1.2; heavyGraphics.scaleY = 1.2; self.health = 200; self.maxHealth = 200; self.speed = 0.6; self.damage = 60; return self; }); var EnemyHealer = Enemy.expand(function () { var self = Enemy.call(this); var healerGraphics = self.attachAsset('enemyInfantry', { anchorX: 0.5, anchorY: 0.5 }); healerGraphics.tint = 0x9932CC; healerGraphics.scaleX = 0.8; healerGraphics.scaleY = 0.8; self.health = 60; self.maxHealth = 60; self.speed = 1.2; self.damage = 15; self.healRange = 200; self.healCooldown = 0; self.healDelay = 180; // 3 seconds at 60fps self.healPower = 30; self.update = function () { if (self.isDead || self.hasReachedBase) return; // Heal nearby enemies if (self.healCooldown <= 0) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy !== self && !enemy.isDead && enemy.health < enemy.maxHealth) { var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.healRange) { enemy.health = Math.min(enemy.maxHealth, enemy.health + self.healPower); self.healCooldown = self.healDelay; // Visual healing effect var healEffect = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: enemy.x, y: enemy.y, alpha: 0.7, scaleX: 0.3, scaleY: 0.3 }); healEffect.tint = 0x00FF00; game.addChild(healEffect); tween(healEffect, { scaleX: 1, scaleY: 1, alpha: 0 }, { duration: 800, onFinish: function onFinish() { healEffect.destroy(); } }); break; } } } } if (self.healCooldown > 0) { self.healCooldown--; } // Move towards base var dx = playerBase.x - self.x; var dy = playerBase.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { self.hasReachedBase = true; playerBase.takeDamage(self.damage); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; return self; }); var EnemyFast = Enemy.expand(function () { var self = Enemy.call(this); var fastGraphics = self.attachAsset('enemyInfantry', { anchorX: 0.5, anchorY: 0.5 }); fastGraphics.tint = 0x00FF00; self.health = 30; self.maxHealth = 30; self.speed = 2.5; self.damage = 15; return self; }); var EnemyExploder = Enemy.expand(function () { var self = Enemy.call(this); var exploderGraphics = self.attachAsset('enemyInfantry', { anchorX: 0.5, anchorY: 0.5 }); exploderGraphics.tint = 0xFF1493; exploderGraphics.scaleX = 0.9; exploderGraphics.scaleY = 0.9; self.health = 40; self.maxHealth = 40; self.speed = 1.8; self.damage = 20; self.explosionRadius = 150; self.explodeOnDeath = function () { // Damage nearby player units when exploder dies for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; var dx = unit.x - self.x; var dy = unit.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.explosionRadius) { unit.takeDamage(50); } } // Visual explosion effect createExplosion(self.x, self.y); }; return self; }); var EnemyBoss = Enemy.expand(function () { var self = Enemy.call(this); var bossGraphics = self.attachAsset('enemyTank', { anchorX: 0.5, anchorY: 0.5 }); bossGraphics.tint = 0x8B0000; bossGraphics.scaleX = 1.5; bossGraphics.scaleY = 1.5; self.health = 400 + waveNumber * 50; self.maxHealth = self.health; self.speed = 0.4; self.damage = 100; self.attackRange = 300; self.attackCooldown = 0; self.attackDelay = 120; // 2 seconds self.specialAttackCooldown = 0; self.specialAttackDelay = 300; // 5 seconds self.isBoss = true; self.update = function () { if (self.isDead || self.hasReachedBase) return; // Regular movement towards base var dx = playerBase.x - self.x; var dy = playerBase.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { self.hasReachedBase = true; playerBase.takeDamage(self.damage); } else { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } // Boss ranged attack if (self.attackCooldown <= 0) { // Find nearest player unit to attack var nearestUnit = null; var shortestDistance = self.attackRange; for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; if (unit.isDead) continue; var udx = unit.x - self.x; var udy = unit.y - self.y; var udistance = Math.sqrt(udx * udx + udy * udy); if (udistance < shortestDistance) { shortestDistance = udistance; nearestUnit = unit; } } if (nearestUnit) { // Create boss bullet var bossBullet = new Bullet(); bossBullet.x = self.x; bossBullet.y = self.y; bossBullet.targetX = nearestUnit.x; bossBullet.targetY = nearestUnit.y; bossBullet.damage = 60; bossBullet.isPlayerBullet = false; bossBullet.speed = 6; bullets.push(bossBullet); game.addChild(bossBullet); self.attackCooldown = self.attackDelay; } } if (self.attackCooldown > 0) { self.attackCooldown--; } // Special area attack if (self.specialAttackCooldown <= 0) { // Damage all player units within range for (var i = 0; i < playerUnits.length; i++) { var unit = playerUnits[i]; var udx = unit.x - self.x; var udy = unit.y - self.y; var udistance = Math.sqrt(udx * udx + udy * udy); if (udistance <= 400) { unit.takeDamage(40); createExplosion(unit.x, unit.y); } } self.specialAttackCooldown = self.specialAttackDelay; } if (self.specialAttackCooldown > 0) { self.specialAttackCooldown--; } }; return self; }); var PlayerUnit = Container.expand(function () { var self = Container.call(this); self.health = 100; self.maxHealth = 100; self.attackRange = 200; self.attackCooldown = 0; self.attackDelay = 60; self.damage = 25; self.unitType = 'tank'; self.lastTarget = null; self.rangeIndicator = null; self.targetIndicator = null; self.showingAttackInfo = false; self.takeDamage = function (damage) { // In hard mode, all units except sword have 1 health if (isHardMode && self.unitType !== 'sword') { self.health = 1; self.maxHealth = 1; } // Only sword warriors can die, all other units are immortal if (self.unitType !== 'sword') { self.health -= damage; if (self.health <= 0) { self.health = 1; // Keep at least 1 health to prevent death } } else { // Sword warriors can still die normally self.health -= damage; if (self.health <= 0) { self.health = 0; self.isDead = true; } } }; self.findNearestEnemy = function () { var nearestEnemy = null; var shortestDistance = self.attackRange; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead) continue; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { shortestDistance = distance; nearestEnemy = enemy; } } return nearestEnemy; }; self.attack = function (target) { if (self.attackCooldown <= 0) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; bullet.targetX = target.x; bullet.targetY = target.y; bullet.damage = self.damage; bullet.isPlayerBullet = true; bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); self.attackCooldown = self.attackDelay; // Update target indicator position if visible if (self.showingAttackInfo && self.targetIndicator) { self.targetIndicator.x = target.x; self.targetIndicator.y = target.y; } } }; self.showAttackInfo = function () { if (self.showingAttackInfo) return; // Create range indicator self.rangeIndicator = LK.getAsset('attackRange', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, scaleX: self.attackRange / 5, scaleY: self.attackRange / 5 }); self.rangeIndicator.x = self.x; self.rangeIndicator.y = self.y; game.addChild(self.rangeIndicator); // Create target indicator if there's a current target var currentTarget = self.findNearestEnemy(); if (currentTarget) { self.targetIndicator = LK.getAsset('targetIndicator', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.targetIndicator.x = currentTarget.x; self.targetIndicator.y = currentTarget.y; game.addChild(self.targetIndicator); } self.showingAttackInfo = true; }; self.hideAttackInfo = function () { if (!self.showingAttackInfo) return; if (self.rangeIndicator) { self.rangeIndicator.destroy(); self.rangeIndicator = null; } if (self.targetIndicator) { self.targetIndicator.destroy(); self.targetIndicator = null; } self.showingAttackInfo = false; }; self.down = function (x, y, obj) { // Hide attack info for all other units for (var i = 0; i < playerUnits.length; i++) { if (playerUnits[i] !== self) { playerUnits[i].hideAttackInfo(); } } // Just show attack info without popup self.showAttackInfo(); }; self.update = function () { if (self.isDead) return; if (self.attackCooldown > 0) { self.attackCooldown--; } var target = self.findNearestEnemy(); if (target) { self.attack(target); } }; return self; }); var Tank = PlayerUnit.expand(function () { var self = PlayerUnit.call(this); var tankGraphics = self.attachAsset('playerTank', { anchorX: 0.5, anchorY: 0.5 }); self.health = 150 + (upgrades.health - 1) * 30; self.maxHealth = self.health; self.attackRange = 250 + (upgrades.range - 1) * 25; self.attackDelay = 45; self.damage = 40 + (upgrades.damage - 1) * 10; self.unitType = 'tank'; self.cost = 100; return self; }); var SwordWarrior = PlayerUnit.expand(function () { var self = PlayerUnit.call(this); var swordGraphics = self.attachAsset('swordWarrior', { anchorX: 0.5, anchorY: 0.5 }); self.health = 120 + (upgrades.health - 1) * 25; self.maxHealth = self.health; self.attackRange = 80; // Short melee range self.attackDelay = 40; self.damage = 50 + (upgrades.damage - 1) * 12; self.unitType = 'sword'; self.cost = 90; self.speed = 2.5; // Fast movement to reach enemies self.currentTarget = null; self.isMovingToTarget = false; self.killCount = 0; // Track how many enemies killed self.maxKills = 4; // Die after 4 kills self.findNearestEnemy = function () { var nearestEnemy = null; var shortestDistance = 600; // Search range much larger than attack range for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isDead) continue; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { shortestDistance = distance; nearestEnemy = enemy; } } return nearestEnemy; }; self.attack = function (target) { if (self.attackCooldown <= 0) { // Store target health before attack to check if it will die var targetWillDie = target.health <= self.damage; // Melee attack - damage directly without bullet target.takeDamage(self.damage); createExplosion(target.x, target.y); LK.getSound('shoot').play(); self.attackCooldown = self.attackDelay; // If target died from this attack, increment kill count if (targetWillDie && target.health <= 0) { self.killCount++; // Die after killing 4 enemies if (self.killCount >= self.maxKills) { self.isDead = true; } } } }; self.update = function () { if (self.isDead) { // Create death explosion effect createExplosion(self.x, self.y); return; } if (self.attackCooldown > 0) { self.attackCooldown--; } var target = self.findNearestEnemy(); if (target) { var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.attackRange) { // In attack range - attack the target self.attack(target); self.isMovingToTarget = false; } else { // Move towards the target self.isMovingToTarget = true; self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } self.currentTarget = target; } else { self.currentTarget = null; self.isMovingToTarget = false; } }; return self; }); var Sniper = PlayerUnit.expand(function () { var self = PlayerUnit.call(this); var sniperGraphics = self.attachAsset('playerInfantry', { anchorX: 0.5, anchorY: 0.5 }); sniperGraphics.tint = 0x800080; sniperGraphics.scaleX = 1.2; sniperGraphics.scaleY = 1.2; self.health = 50 + (upgrades.health - 1) * 12; self.maxHealth = self.health; self.attackRange = Math.min(2048, 1638); // 80% of arena width (2048 * 0.8) self.attackDelay = 180; // Very slow (3 seconds) self.damage = 80 + (upgrades.damage - 1) * 20; self.unitType = 'sniper'; self.cost = 150; return self; }); var Infantry = PlayerUnit.expand(function () { var self = PlayerUnit.call(this); var infantryGraphics = self.attachAsset('playerInfantry', { anchorX: 0.5, anchorY: 0.5 }); self.health = 60 + (upgrades.health - 1) * 15; self.maxHealth = self.health; self.attackRange = 150 + (upgrades.range - 1) * 15; self.attackDelay = 30; self.damage = 20 + (upgrades.damage - 1) * 5; self.unitType = 'infantry'; self.cost = 50; return self; }); var Artillery = PlayerUnit.expand(function () { var self = PlayerUnit.call(this); var artilleryGraphics = self.attachAsset('playerArtillery', { anchorX: 0.5, anchorY: 0.5 }); self.health = 80 + (upgrades.health - 1) * 20; self.maxHealth = self.health; self.attackRange = 350 + (upgrades.range - 1) * 35; self.attackDelay = 90; self.damage = 60 + (upgrades.damage - 1) * 15; self.unitType = 'artillery'; self.cost = 120; return self; }); var Archer = PlayerUnit.expand(function () { var self = PlayerUnit.call(this); var archerGraphics = self.attachAsset('playerInfantry', { anchorX: 0.5, anchorY: 0.5 }); archerGraphics.tint = 0x8B4513; self.health = 70 + (upgrades.health - 1) * 18; self.maxHealth = self.health; self.attackRange = 300 + (upgrades.range - 1) * 30; self.attackDelay = 50; self.damage = 40 + (upgrades.damage - 1) * 10; self.unitType = 'archer'; self.cost = 80; return self; }); var UnitUpgradePopup = Container.expand(function () { var self = Container.call(this); // Create popup background var popupBg = LK.getAsset('playerBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 6, scaleY: 4.5, alpha: 0.9 }); self.addChild(popupBg); popupBg.tint = 0x333333; // Create upgrade buttons // Create damage button background var damageBtnBg = LK.getAsset('playerBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2, alpha: 0.7 }); damageBtnBg.x = -200; damageBtnBg.y = -80; damageBtnBg.tint = 0x4a4a4a; self.addChild(damageBtnBg); var damageUpgradeBtn = new Text2('DAMAGE + (75)', { size: 50, fill: 0xFFFFFF }); damageUpgradeBtn.anchor.set(0.5, 0.5); damageUpgradeBtn.x = -200; damageUpgradeBtn.y = -80; self.addChild(damageUpgradeBtn); // Create health button background var healthBtnBg = LK.getAsset('playerBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2, alpha: 0.7 }); healthBtnBg.x = 200; healthBtnBg.y = -80; healthBtnBg.tint = 0x4a4a4a; self.addChild(healthBtnBg); var healthUpgradeBtn = new Text2('HEALTH + (75)', { size: 50, fill: 0xFFFFFF }); healthUpgradeBtn.anchor.set(0.5, 0.5); healthUpgradeBtn.x = 200; healthUpgradeBtn.y = -80; self.addChild(healthUpgradeBtn); // Create range button background var rangeBtnBg = LK.getAsset('playerBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2, alpha: 0.7 }); rangeBtnBg.x = 0; rangeBtnBg.y = 80; rangeBtnBg.tint = 0x4a4a4a; self.addChild(rangeBtnBg); var rangeUpgradeBtn = new Text2('RANGE + (75)', { size: 50, fill: 0xFFFFFF }); rangeUpgradeBtn.anchor.set(0.5, 0.5); rangeUpgradeBtn.x = 0; rangeUpgradeBtn.y = 80; self.addChild(rangeUpgradeBtn); self.targetUnit = null; self.upgradeCost = 75; self.show = function (unit) { self.targetUnit = unit; self.x = unit.x; self.y = unit.y - 100; self.visible = true; }; self.hide = function () { if (self.targetUnit) { self.targetUnit.hideAttackInfo(); } self.visible = false; self.targetUnit = null; }; damageUpgradeBtn.down = function () { if (self.targetUnit && resources >= self.upgradeCost) { resources -= self.upgradeCost; self.targetUnit.damage += 15; resourceText.setText('Resources: ' + resources); self.hide(); } }; healthUpgradeBtn.down = function () { if (self.targetUnit && resources >= self.upgradeCost) { resources -= self.upgradeCost; self.targetUnit.health += 50; self.targetUnit.maxHealth += 50; resourceText.setText('Resources: ' + resources); self.hide(); } }; rangeUpgradeBtn.down = function () { if (self.targetUnit && resources >= self.upgradeCost) { resources -= self.upgradeCost; self.targetUnit.attackRange += 50; resourceText.setText('Resources: ' + resources); self.hide(); } }; self.visible = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2F4F2F }); /**** * Game Code ****/ var playerUnits = []; var enemies = []; var bullets = []; var explosions = []; var resources = 200; var maxResources = 500; var resourceRegenRate = 1; var resourceRegenTimer = 0; var waveNumber = 1; var enemiesSpawned = 0; var enemiesPerWave = 5; var spawnTimer = 0; var spawnDelay = 120; var timeBetweenWaves = 300; var waveTimer = 0; var isWaveActive = false; var selectedUnitType = 'tank'; var unitCosts = { tank: 100, artillery: 120, infantry: 50, sword: 90, sniper: 150 }; var isHardMode = false; var isModeseMode = false; // Create unit upgrade popup var unitUpgradePopup = game.addChild(new UnitUpgradePopup()); // Initialize upgrade system var upgrades = { damage: 1, health: 1, range: 1 }; var upgradeBaseCost = 150; // Create player base var playerBase = game.addChild(new Base()); playerBase.x = 1024; playerBase.y = 2400; // Set base health to 1 in hard mode if (isHardMode) { playerBase.health = 1; playerBase.maxHealth = 1; } // Place 1 archer inside the base with double tank range var defenseArcher = game.addChild(new Archer()); defenseArcher.x = 1024; defenseArcher.y = 2400; // Same position as base defenseArcher.attackRange = 500; // Double the tank's base range (250 * 2) playerUnits.push(defenseArcher); // Create UI elements var resourceText = new Text2('Resources: ' + resources, { size: 40, fill: 0xFFFFFF }); resourceText.anchor.set(0, 0); LK.gui.topLeft.addChild(resourceText); resourceText.x = 120; resourceText.y = 20; var waveText = new Text2('Wave: ' + waveNumber, { size: 40, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); waveText.y = 140; var baseHealthText = new Text2('Base Health: ' + playerBase.health, { size: 40, fill: 0xFFFFFF }); baseHealthText.anchor.set(1, 0); LK.gui.topRight.addChild(baseHealthText); baseHealthText.x = -20; baseHealthText.y = 20; var scoreText = new Text2('Score: 0', { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topLeft.addChild(scoreText); scoreText.x = 120; scoreText.y = 70; // Unit selection buttons with square backgrounds var tankButtonBg = LK.getAsset('playerTank', { anchorX: 0.5, anchorY: 1, scaleX: 2, scaleY: 2 }); LK.gui.bottom.addChild(tankButtonBg); tankButtonBg.x = -500; tankButtonBg.y = -20; var tankButton = new Text2('TANK (100)', { size: 35, fill: 0xFFFFFF }); tankButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(tankButton); tankButton.x = -500; tankButton.y = -80; var artilleryButtonBg = LK.getAsset('playerArtillery', { anchorX: 0.5, anchorY: 1, scaleX: 2, scaleY: 2 }); LK.gui.bottom.addChild(artilleryButtonBg); artilleryButtonBg.x = -200; artilleryButtonBg.y = -20; var artilleryButton = new Text2('ARTILLERY (120)', { size: 35, fill: 0xFFFFFF }); artilleryButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(artilleryButton); artilleryButton.x = -200; artilleryButton.y = -80; var infantryButtonBg = LK.getAsset('playerInfantry', { anchorX: 0.5, anchorY: 1, scaleX: 2, scaleY: 2 }); LK.gui.bottom.addChild(infantryButtonBg); infantryButtonBg.x = 100; infantryButtonBg.y = -20; var infantryButton = new Text2('INFANTRY (50)', { size: 35, fill: 0xFFFFFF }); infantryButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(infantryButton); infantryButton.x = 100; infantryButton.y = -80; var swordButtonBg = LK.getAsset('swordWarrior', { anchorX: 0.5, anchorY: 1, scaleX: 2, scaleY: 2 }); LK.gui.bottom.addChild(swordButtonBg); swordButtonBg.x = 350; swordButtonBg.y = -20; var swordButton = new Text2('SWORD (90)', { size: 35, fill: 0xFFFFFF }); swordButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(swordButton); swordButton.x = 350; swordButton.y = -80; var sniperButtonBg = LK.getAsset('playerInfantry', { anchorX: 0.5, anchorY: 1, scaleX: 2, scaleY: 2 }); sniperButtonBg.tint = 0x800080; LK.gui.bottom.addChild(sniperButtonBg); sniperButtonBg.x = 550; sniperButtonBg.y = -20; sniperButtonBg.visible = false; var sniperButton = new Text2('SNIPER (150)', { size: 35, fill: 0xFFFFFF }); sniperButton.anchor.set(0.5, 1); LK.gui.bottom.addChild(sniperButton); sniperButton.x = 550; sniperButton.y = -80; sniperButton.visible = false; // Create upgrade buttons var damageUpgradeButton = new Text2('DMG LV' + upgrades.damage + ' (' + upgradeBaseCost * upgrades.damage + ')', { size: 50, fill: 0xFFFFFF }); damageUpgradeButton.anchor.set(1, 0); LK.gui.topRight.addChild(damageUpgradeButton); damageUpgradeButton.x = -20; damageUpgradeButton.y = 70; var healthUpgradeButton = new Text2('HP LV' + upgrades.health + ' (' + upgradeBaseCost * upgrades.health + ')', { size: 50, fill: 0xFFFFFF }); healthUpgradeButton.anchor.set(1, 0); LK.gui.topRight.addChild(healthUpgradeButton); healthUpgradeButton.x = -20; healthUpgradeButton.y = 130; var rangeUpgradeButton = new Text2('RNG LV' + upgrades.range + ' (' + upgradeBaseCost * upgrades.range + ')', { size: 50, fill: 0xFFFFFF }); rangeUpgradeButton.anchor.set(1, 0); LK.gui.topRight.addChild(rangeUpgradeButton); rangeUpgradeButton.x = -20; rangeUpgradeButton.y = 190; // Create modes button var modesButtonBg = LK.getAsset('playerBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2, alpha: 0.8 }); modesButtonBg.tint = 0x4a4a4a; LK.gui.top.addChild(modesButtonBg); modesButtonBg.x = 0; modesButtonBg.y = 80; var modesButton = new Text2('MODES', { size: 45, fill: 0xFFFFFF }); modesButton.anchor.set(0.5, 0.5); LK.gui.top.addChild(modesButton); modesButton.x = 0; modesButton.y = 80; function updateSelectedUnit() { tankButtonBg.tint = selectedUnitType === 'tank' ? 0xFFFF00 : 0x4169E1; artilleryButtonBg.tint = selectedUnitType === 'artillery' ? 0xFFFF00 : 0x8B4513; infantryButtonBg.tint = selectedUnitType === 'infantry' ? 0xFFFF00 : 0x228B22; swordButtonBg.tint = selectedUnitType === 'sword' ? 0xFFFF00 : 0xb8860b; if (isHardMode || isModeseMode) { sniperButtonBg.tint = selectedUnitType === 'sniper' ? 0xFFFF00 : 0x800080; } } updateSelectedUnit(); tankButtonBg.down = function () { selectedUnitType = 'tank'; updateSelectedUnit(); }; artilleryButtonBg.down = function () { selectedUnitType = 'artillery'; updateSelectedUnit(); }; infantryButtonBg.down = function () { selectedUnitType = 'infantry'; updateSelectedUnit(); }; swordButtonBg.down = function () { selectedUnitType = 'sword'; updateSelectedUnit(); }; sniperButtonBg.down = function () { if (isHardMode || isModeseMode) { selectedUnitType = 'sniper'; updateSelectedUnit(); } }; damageUpgradeButton.down = function () { var cost = upgradeBaseCost * upgrades.damage; if (resources >= cost && upgrades.damage < 10) { resources -= cost; upgrades.damage++; storage.damageLevel = upgrades.damage; damageUpgradeButton.setText('DMG LV' + upgrades.damage + ' (' + upgradeBaseCost * upgrades.damage + ')'); resourceText.setText('Resources: ' + resources); } }; healthUpgradeButton.down = function () { var cost = upgradeBaseCost * upgrades.health; if (resources >= cost && upgrades.health < 10) { resources -= cost; upgrades.health++; storage.healthLevel = upgrades.health; healthUpgradeButton.setText('HP LV' + upgrades.health + ' (' + upgradeBaseCost * upgrades.health + ')'); resourceText.setText('Resources: ' + resources); } }; rangeUpgradeButton.down = function () { var cost = upgradeBaseCost * upgrades.range; if (resources >= cost && upgrades.range < 10) { resources -= cost; upgrades.range++; storage.rangeLevel = upgrades.range; rangeUpgradeButton.setText('RNG LV' + upgrades.range + ' (' + upgradeBaseCost * upgrades.range + ')'); resourceText.setText('Resources: ' + resources); } }; // Create hard mode popup (initially hidden) var hardModePopupBg = LK.getAsset('playerBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 5, alpha: 0.95 }); hardModePopupBg.tint = 0x1a1a1a; LK.gui.center.addChild(hardModePopupBg); hardModePopupBg.x = 0; hardModePopupBg.y = 0; hardModePopupBg.visible = false; var hardModeButton = new Text2('OPEN HARD MODE', { size: 60, fill: 0xFF4500 }); hardModeButton.anchor.set(0.5, 0.5); LK.gui.center.addChild(hardModeButton); hardModeButton.x = 0; hardModeButton.y = -50; hardModeButton.visible = false; var modeseModeButton = new Text2('OPEN FREE MODE', { size: 60, fill: 0x4B0082 }); modeseModeButton.anchor.set(0.5, 0.5); LK.gui.center.addChild(modeseModeButton); modeseModeButton.x = 0; modeseModeButton.y = 30; modeseModeButton.visible = false; var closePopupButton = new Text2('CLOSE', { size: 45, fill: 0xFFFFFF }); closePopupButton.anchor.set(0.5, 0.5); LK.gui.center.addChild(closePopupButton); closePopupButton.x = 0; closePopupButton.y = 100; closePopupButton.visible = false; hardModeButton.down = function () { // Activate hard mode isHardMode = true; hardModeButton.setText('HARD MODE ACTIVE'); hardModeButton.tint = 0x00FF00; // Show sniper button in hard mode sniperButtonBg.visible = true; sniperButton.visible = true; updateSelectedUnit(); // Close the popup hardModePopupBg.visible = false; hardModeButton.visible = false; modeseModeButton.visible = false; closePopupButton.visible = false; }; modeseModeButton.down = function () { // Activate modese mode isModeseMode = true; modeseModeButton.setText('FREE MODE ACTIVE'); modeseModeButton.tint = 0x00FF00; // Set cash to 9999999 in free mode resources = 9999999; resourceText.setText('Resources: ' + resources); // Show sniper button in free mode sniperButtonBg.visible = true; sniperButton.visible = true; updateSelectedUnit(); // Close the popup hardModePopupBg.visible = false; hardModeButton.visible = false; modeseModeButton.visible = false; closePopupButton.visible = false; }; closePopupButton.down = function () { hardModePopupBg.visible = false; hardModeButton.visible = false; modeseModeButton.visible = false; closePopupButton.visible = false; }; modesButton.down = function () { // Show hard mode popup hardModePopupBg.visible = true; hardModeButton.visible = true; modeseModeButton.visible = true; closePopupButton.visible = true; }; function spawnEnemy() { var enemy; var spawnSide = Math.floor(Math.random() * 4); var spawnX, spawnY; // Random spawn from edges switch (spawnSide) { case 0: // Top spawnX = Math.random() * 2048; spawnY = 0; break; case 1: // Right spawnX = 2048; spawnY = Math.random() * 1500; break; case 2: // Left spawnX = 0; spawnY = Math.random() * 1500; break; case 3: // Top corners spawnX = Math.random() < 0.5 ? 0 : 2048; spawnY = Math.random() * 500; break; } // Boss waves every 5 waves, or every wave in hard mode if (waveNumber % 5 === 0 || isHardMode && Math.random() < 0.3) { // Boss wave - spawn boss in center top spawnX = 1024; spawnY = 0; if (waveNumber >= 15 && isHardMode) { // The Big Boss for hard mode high waves enemy = new TheBigBoss(); } else if (waveNumber >= 15) { // Mega boss for higher waves enemy = new MegaBoss(); } else { // Regular boss enemy = new EnemyBoss(); } } else { // Different enemies based on wave number var enemyType = Math.random(); if (waveNumber <= 3) { // Early waves: Basic infantry and some tanks if (enemyType < 0.7) { enemy = new EnemyInfantry(); } else { enemy = new EnemyTank(); } } else if (waveNumber <= 6) { // Mid-early waves: Add fast enemies if (enemyType < 0.4) { enemy = new EnemyInfantry(); } else if (enemyType < 0.7) { enemy = new EnemyTank(); } else if (enemyType < 0.85) { enemy = new EnemyFast(); } else if (isHardMode) { enemy = new FastZombie(); } else { enemy = new EnemyFast(); } } else if (waveNumber <= 10) { // Mid waves: Add heavy enemies if (enemyType < 0.3) { enemy = new EnemyInfantry(); } else if (enemyType < 0.5) { enemy = new EnemyTank(); } else if (enemyType < 0.65) { enemy = new EnemyFast(); } else if (enemyType < 0.8) { enemy = new EnemyHeavy(); } else if (isHardMode) { enemy = new FastZombie(); } else { enemy = new EnemyHeavy(); } } else if (waveNumber <= 15) { // Late-mid waves: Add swarm enemies if (enemyType < 0.2) { enemy = new EnemyInfantry(); } else if (enemyType < 0.4) { enemy = new EnemyTank(); } else if (enemyType < 0.6) { enemy = new EnemyFast(); } else if (enemyType < 0.8) { enemy = new EnemyHeavy(); } else { enemy = new EnemySwarm(); } } else if (waveNumber <= 20) { // Advanced waves: Add special enemies if (enemyType < 0.1) { enemy = new EnemyInfantry(); } else if (enemyType < 0.2) { enemy = new EnemyTank(); } else if (enemyType < 0.3) { enemy = new EnemyFast(); } else if (enemyType < 0.4) { enemy = new EnemyHeavy(); } else if (enemyType < 0.5) { enemy = new EnemySwarm(); } else if (enemyType < 0.6) { enemy = new EnemyShielder(); } else if (enemyType < 0.7) { enemy = new EnemyExploder(); } else if (enemyType < 0.8) { enemy = new EnemyHealer(); } else if (enemyType < 0.9) { enemy = new EnemyBoss(); } else if (isHardMode) { enemy = new FastZombie(); } else { enemy = new EnemyBoss(); } } else { // Ultimate waves: All enemy types with higher spawn rates for special enemies if (enemyType < 0.05) { enemy = new EnemyInfantry(); } else if (enemyType < 0.1) { enemy = new EnemyTank(); } else if (enemyType < 0.2) { enemy = new EnemyFast(); } else if (enemyType < 0.35) { enemy = new EnemyHeavy(); } else if (enemyType < 0.45) { enemy = new EnemySwarm(); } else if (enemyType < 0.6) { enemy = new EnemyShielder(); } else if (enemyType < 0.75) { enemy = new EnemyExploder(); } else if (enemyType < 0.85) { enemy = new EnemyHealer(); } else { enemy = new EnemyBoss(); } } } enemy.x = spawnX; enemy.y = spawnY; // Bosses don't get additional speed/health scaling to maintain their unique stats if (!enemy.isBoss) { enemy.speed += Math.random() * 0.5 + waveNumber * 0.1; // In hard mode, make all enemies much faster if (isHardMode) { enemy.speed *= 2.5; // 2.5x faster in hard mode // FastZombie gets even more speed boost in hard mode if (enemy.isHardModeOnly) { enemy.speed *= 1.5; // Additional 1.5x for FastZombie (total 3.75x) } } enemy.health += waveNumber * 10; enemy.maxHealth = enemy.health; } enemies.push(enemy); game.addChild(enemy); } function createExplosion(x, y) { var explosion = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, alpha: 1, scaleX: 0.5, scaleY: 0.5 }); game.addChild(explosion); tween(explosion, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 500, onFinish: function onFinish() { explosion.destroy(); } }); LK.getSound('explosion').play(); } game.down = function (x, y, obj) { // Hide attack info for all units when clicking elsewhere for (var i = 0; i < playerUnits.length; i++) { playerUnits[i].hideAttackInfo(); } // Check if click is on battlefield (not on UI) or near the base var nearBase = false; var distanceToBase = Math.sqrt((x - playerBase.x) * (x - playerBase.x) + (y - playerBase.y) * (y - playerBase.y)); if (distanceToBase <= 300) { nearBase = true; } if (y > 200 && y < 2200 || nearBase) { var cost = unitCosts[selectedUnitType]; // In free mode, all units cost 1 if (isModeseMode) { cost = 1; } if (resources >= cost) { var unit; switch (selectedUnitType) { case 'tank': unit = new Tank(); break; case 'artillery': unit = new Artillery(); break; case 'infantry': unit = new Infantry(); break; case 'sword': unit = new SwordWarrior(); break; case 'sniper': if (isHardMode || isModeseMode) { unit = new Sniper(); } break; } unit.x = x; unit.y = y; playerUnits.push(unit); game.addChild(unit); resources -= cost; resourceText.setText('Resources: ' + resources); } } }; game.update = function () { // Resource regeneration resourceRegenTimer++; if (resourceRegenTimer >= 60) { if (resources < maxResources) { resources += resourceRegenRate; resourceText.setText('Resources: ' + resources); } resourceRegenTimer = 0; } // Wave management if (!isWaveActive) { waveTimer++; var currentTimeBetweenWaves = timeBetweenWaves; // In hard mode, much shorter time between waves if (isHardMode) { currentTimeBetweenWaves = Math.max(30, timeBetweenWaves / 6); // 6x faster wave progression, minimum 30 frames } if (waveTimer >= currentTimeBetweenWaves) { isWaveActive = true; enemiesSpawned = 0; waveTimer = 0; // Boss waves have fewer enemies but they're stronger if (waveNumber % 5 === 0) { enemiesPerWave = isHardMode ? 3 : 1; // Hard mode: 3 bosses, normal: 1 boss } else { var baseEnemies = 5 + waveNumber * 2; enemiesPerWave = isHardMode ? baseEnemies * 5 : baseEnemies; // Hard mode: 5x enemies } } } else { spawnTimer++; // Boss waves spawn immediately, regular waves use spawn delay var currentSpawnDelay = waveNumber % 5 === 0 ? 0 : spawnDelay; // In hard mode, make spawning much faster if (isHardMode) { currentSpawnDelay = Math.max(5, currentSpawnDelay / 8); // 8x faster spawning, minimum 5 frames } if (spawnTimer >= currentSpawnDelay && enemiesSpawned < enemiesPerWave) { spawnEnemy(); enemiesSpawned++; spawnTimer = 0; } // Check if wave is complete if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) { isWaveActive = false; waveNumber++; waveText.setText('Wave: ' + waveNumber); LK.setScore(LK.getScore() + 100); scoreText.setText('Score: ' + LK.getScore()); // Award 30 resources for completing the wave resources += 30; resourceText.setText('Resources: ' + resources); } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; bullet.update(); if (bullet.hasReachedTarget || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) { if (bullet.hasReachedTarget) { createExplosion(bullet.x, bullet.y); } bullet.destroy(); bullets.splice(i, 1); continue; } // Check bullet collisions if (bullet.isPlayerBullet) { // Player bullets hit enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (!enemy.isDead && bullet.intersects(enemy)) { enemy.takeDamage(bullet.damage); createExplosion(bullet.x, bullet.y); if (enemy.isDead) { // Check if this was an exploder and trigger explosion if (enemy.explodeOnDeath) { enemy.explodeOnDeath(); } // Boss kills give more points var scoreReward = enemy.isBoss ? 500 : 25; LK.setScore(LK.getScore() + scoreReward); scoreText.setText('Score: ' + LK.getScore()); // Award resources for each enemy killed (5 in hard mode, 3 in normal) resources += isHardMode ? 5 : 3; resourceText.setText('Resources: ' + resources); enemy.destroy(); enemies.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); break; } } } else { // Enemy/Boss bullets hit player units for (var j = playerUnits.length - 1; j >= 0; j--) { var unit = playerUnits[j]; if (!unit.isDead && bullet.intersects(unit)) { unit.takeDamage(bullet.damage); createExplosion(bullet.x, bullet.y); if (unit.isDead) { unit.destroy(); playerUnits.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); break; } } } } // Update player units for (var i = playerUnits.length - 1; i >= 0; i--) { var unit = playerUnits[i]; unit.update(); if (unit.isDead) { unit.destroy(); playerUnits.splice(i, 1); } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.update(); if (enemy.isDead) { // Check if this was an exploder and trigger explosion if (enemy.explodeOnDeath) { enemy.explodeOnDeath(); } // Award resources for each enemy killed (5 in hard mode, 3 in normal) resources += isHardMode ? 5 : 3; resourceText.setText('Resources: ' + resources); enemy.destroy(); enemies.splice(i, 1); } else if (enemy.hasReachedBase) { enemy.destroy(); enemies.splice(i, 1); } } // Update base health display baseHealthText.setText('Base Health: ' + playerBase.health); // Check game over condition if (playerBase.isDead) { LK.showGameOver(); } // Check win condition (survive 20 waves) if (waveNumber > 20) { LK.showYouWin(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Base = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 500;
self.maxHealth = 500;
self.isDead = false;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
self.isDead = true;
}
};
return self;
});
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.targetX = 0;
self.targetY = 0;
self.isPlayerBullet = true;
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.hasReachedTarget = true;
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.health = 100;
self.maxHealth = 100;
self.speed = 1;
self.damage = 30;
self.isDead = false;
self.hasReachedBase = false;
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
self.isDead = true;
}
};
self.update = function () {
if (self.isDead || self.hasReachedBase) return;
var dx = playerBase.x - self.x;
var dy = playerBase.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
self.hasReachedBase = true;
playerBase.takeDamage(self.damage);
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var TheBigBoss = Enemy.expand(function () {
var self = Enemy.call(this);
var bigBossGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
bigBossGraphics.tint = 0xFF0000; // Red color for the big boss
bigBossGraphics.scaleX = 3.0; // Massive size
bigBossGraphics.scaleY = 3.0;
self.health = 1500 + waveNumber * 200;
self.maxHealth = self.health;
self.speed = 0.2; // Very slow but menacing
self.damage = 250;
self.attackRange = 500;
self.attackCooldown = 0;
self.attackDelay = 60; // 1 second
self.megaAttackCooldown = 0;
self.megaAttackDelay = 300; // 5 seconds
self.spawnCooldown = 0;
self.spawnDelay = 600; // 10 seconds
self.isBoss = true;
self.isHardModeOnly = true;
self.update = function () {
if (self.isDead || self.hasReachedBase) return;
// Regular movement towards base
var dx = playerBase.x - self.x;
var dy = playerBase.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 120) {
self.hasReachedBase = true;
playerBase.takeDamage(self.damage);
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Continuous attack - shoots at nearest unit
if (self.attackCooldown <= 0) {
var nearestUnit = null;
var shortestDistance = self.attackRange;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit.isDead) continue;
var udx = unit.x - self.x;
var udy = unit.y - self.y;
var udistance = Math.sqrt(udx * udx + udy * udy);
if (udistance < shortestDistance) {
shortestDistance = udistance;
nearestUnit = unit;
}
}
if (nearestUnit) {
var bigBossBullet = new Bullet();
bigBossBullet.x = self.x;
bigBossBullet.y = self.y;
bigBossBullet.targetX = nearestUnit.x;
bigBossBullet.targetY = nearestUnit.y;
bigBossBullet.damage = 120;
bigBossBullet.isPlayerBullet = false;
bigBossBullet.speed = 8;
bullets.push(bigBossBullet);
game.addChild(bigBossBullet);
self.attackCooldown = self.attackDelay;
}
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Mega attack - damages all units on screen
if (self.megaAttackCooldown <= 0) {
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (!unit.isDead) {
unit.takeDamage(80);
createExplosion(unit.x, unit.y);
}
}
// Screen flash effect
LK.effects.flashScreen(0xFF0000, 500);
self.megaAttackCooldown = self.megaAttackDelay;
}
if (self.megaAttackCooldown > 0) {
self.megaAttackCooldown--;
}
// Spawn fast zombies
if (self.spawnCooldown <= 0) {
for (var j = 0; j < 3; j++) {
var zombie = new FastZombie();
zombie.x = self.x + (Math.random() - 0.5) * 200;
zombie.y = self.y + (Math.random() - 0.5) * 200;
enemies.push(zombie);
game.addChild(zombie);
}
self.spawnCooldown = self.spawnDelay;
}
if (self.spawnCooldown > 0) {
self.spawnCooldown--;
}
};
return self;
});
var MegaBoss = Enemy.expand(function () {
var self = Enemy.call(this);
var megaBossGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
megaBossGraphics.tint = 0x4B0082;
megaBossGraphics.scaleX = 2.0;
megaBossGraphics.scaleY = 2.0;
self.health = 800 + waveNumber * 100;
self.maxHealth = self.health;
self.speed = 0.3;
self.damage = 150;
self.attackRange = 400;
self.attackCooldown = 0;
self.attackDelay = 90;
self.multiShotCooldown = 0;
self.multiShotDelay = 240; // 4 seconds
self.healCooldown = 0;
self.healDelay = 600; // 10 seconds
self.isBoss = true;
self.update = function () {
if (self.isDead || self.hasReachedBase) return;
// Regular movement towards base
var dx = playerBase.x - self.x;
var dy = playerBase.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
self.hasReachedBase = true;
playerBase.takeDamage(self.damage);
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Multi-shot attack
if (self.multiShotCooldown <= 0) {
// Shoot at up to 3 different targets
var targetsHit = 0;
for (var i = 0; i < playerUnits.length && targetsHit < 3; i++) {
var unit = playerUnits[i];
if (unit.isDead) continue;
var udx = unit.x - self.x;
var udy = unit.y - self.y;
var udistance = Math.sqrt(udx * udx + udy * udy);
if (udistance <= self.attackRange) {
var bossBullet = new Bullet();
bossBullet.x = self.x;
bossBullet.y = self.y;
bossBullet.targetX = unit.x;
bossBullet.targetY = unit.y;
bossBullet.damage = 80;
bossBullet.isPlayerBullet = false;
bossBullet.speed = 7;
bullets.push(bossBullet);
game.addChild(bossBullet);
targetsHit++;
}
}
if (targetsHit > 0) {
self.multiShotCooldown = self.multiShotDelay;
}
}
if (self.multiShotCooldown > 0) {
self.multiShotCooldown--;
}
// Self heal ability
if (self.healCooldown <= 0 && self.health < self.maxHealth * 0.7) {
self.health = Math.min(self.maxHealth, self.health + 200);
self.healCooldown = self.healDelay;
// Visual healing effect
var healEffect = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.7,
scaleX: 0.5,
scaleY: 0.5
});
healEffect.tint = 0x00FF00;
game.addChild(healEffect);
tween(healEffect, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
healEffect.destroy();
}
});
}
if (self.healCooldown > 0) {
self.healCooldown--;
}
};
return self;
});
var FastZombie = Enemy.expand(function () {
var self = Enemy.call(this);
var zombieGraphics = self.attachAsset('enemyInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
zombieGraphics.tint = 0x800080; // Purple color for zombie
zombieGraphics.scaleX = 0.8;
zombieGraphics.scaleY = 0.8;
self.health = 20;
self.maxHealth = 20;
self.speed = 3.5; // Very fast
self.damage = 12;
self.isHardModeOnly = true;
return self;
});
var EnemyTank = Enemy.expand(function () {
var self = Enemy.call(this);
var tankGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 120;
self.maxHealth = 120;
self.speed = 0.8;
self.damage = 40;
return self;
});
var EnemySwarm = Enemy.expand(function () {
var self = Enemy.call(this);
var swarmGraphics = self.attachAsset('enemyInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
swarmGraphics.tint = 0xFFFF00;
swarmGraphics.scaleX = 0.7;
swarmGraphics.scaleY = 0.7;
self.health = 25;
self.maxHealth = 25;
self.speed = 2.0;
self.damage = 10;
return self;
});
var EnemyShielder = Enemy.expand(function () {
var self = Enemy.call(this);
var shielderGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
shielderGraphics.tint = 0x4169E1;
shielderGraphics.scaleX = 1.1;
shielderGraphics.scaleY = 1.1;
self.health = 180;
self.maxHealth = 180;
self.speed = 0.7;
self.damage = 35;
self.shieldActive = true;
self.takeDamage = function (damage) {
// Shielder takes reduced damage when shield is active
var actualDamage = self.shieldActive ? damage * 0.5 : damage;
self.health -= actualDamage;
if (self.health <= 0) {
self.health = 0;
self.isDead = true;
}
// Shield breaks at half health
if (self.health <= self.maxHealth * 0.5) {
self.shieldActive = false;
shielderGraphics.tint = 0xFF4500;
}
};
return self;
});
var EnemyInfantry = Enemy.expand(function () {
var self = Enemy.call(this);
var infantryGraphics = self.attachAsset('enemyInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 50;
self.maxHealth = 50;
self.speed = 1.5;
self.damage = 25;
return self;
});
var EnemyHeavy = Enemy.expand(function () {
var self = Enemy.call(this);
var heavyGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
heavyGraphics.tint = 0x4B0082;
heavyGraphics.scaleX = 1.2;
heavyGraphics.scaleY = 1.2;
self.health = 200;
self.maxHealth = 200;
self.speed = 0.6;
self.damage = 60;
return self;
});
var EnemyHealer = Enemy.expand(function () {
var self = Enemy.call(this);
var healerGraphics = self.attachAsset('enemyInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
healerGraphics.tint = 0x9932CC;
healerGraphics.scaleX = 0.8;
healerGraphics.scaleY = 0.8;
self.health = 60;
self.maxHealth = 60;
self.speed = 1.2;
self.damage = 15;
self.healRange = 200;
self.healCooldown = 0;
self.healDelay = 180; // 3 seconds at 60fps
self.healPower = 30;
self.update = function () {
if (self.isDead || self.hasReachedBase) return;
// Heal nearby enemies
if (self.healCooldown <= 0) {
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy !== self && !enemy.isDead && enemy.health < enemy.maxHealth) {
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.healRange) {
enemy.health = Math.min(enemy.maxHealth, enemy.health + self.healPower);
self.healCooldown = self.healDelay;
// Visual healing effect
var healEffect = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: enemy.x,
y: enemy.y,
alpha: 0.7,
scaleX: 0.3,
scaleY: 0.3
});
healEffect.tint = 0x00FF00;
game.addChild(healEffect);
tween(healEffect, {
scaleX: 1,
scaleY: 1,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
healEffect.destroy();
}
});
break;
}
}
}
}
if (self.healCooldown > 0) {
self.healCooldown--;
}
// Move towards base
var dx = playerBase.x - self.x;
var dy = playerBase.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
self.hasReachedBase = true;
playerBase.takeDamage(self.damage);
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
return self;
});
var EnemyFast = Enemy.expand(function () {
var self = Enemy.call(this);
var fastGraphics = self.attachAsset('enemyInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
fastGraphics.tint = 0x00FF00;
self.health = 30;
self.maxHealth = 30;
self.speed = 2.5;
self.damage = 15;
return self;
});
var EnemyExploder = Enemy.expand(function () {
var self = Enemy.call(this);
var exploderGraphics = self.attachAsset('enemyInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
exploderGraphics.tint = 0xFF1493;
exploderGraphics.scaleX = 0.9;
exploderGraphics.scaleY = 0.9;
self.health = 40;
self.maxHealth = 40;
self.speed = 1.8;
self.damage = 20;
self.explosionRadius = 150;
self.explodeOnDeath = function () {
// Damage nearby player units when exploder dies
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var dx = unit.x - self.x;
var dy = unit.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.explosionRadius) {
unit.takeDamage(50);
}
}
// Visual explosion effect
createExplosion(self.x, self.y);
};
return self;
});
var EnemyBoss = Enemy.expand(function () {
var self = Enemy.call(this);
var bossGraphics = self.attachAsset('enemyTank', {
anchorX: 0.5,
anchorY: 0.5
});
bossGraphics.tint = 0x8B0000;
bossGraphics.scaleX = 1.5;
bossGraphics.scaleY = 1.5;
self.health = 400 + waveNumber * 50;
self.maxHealth = self.health;
self.speed = 0.4;
self.damage = 100;
self.attackRange = 300;
self.attackCooldown = 0;
self.attackDelay = 120; // 2 seconds
self.specialAttackCooldown = 0;
self.specialAttackDelay = 300; // 5 seconds
self.isBoss = true;
self.update = function () {
if (self.isDead || self.hasReachedBase) return;
// Regular movement towards base
var dx = playerBase.x - self.x;
var dy = playerBase.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
self.hasReachedBase = true;
playerBase.takeDamage(self.damage);
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Boss ranged attack
if (self.attackCooldown <= 0) {
// Find nearest player unit to attack
var nearestUnit = null;
var shortestDistance = self.attackRange;
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
if (unit.isDead) continue;
var udx = unit.x - self.x;
var udy = unit.y - self.y;
var udistance = Math.sqrt(udx * udx + udy * udy);
if (udistance < shortestDistance) {
shortestDistance = udistance;
nearestUnit = unit;
}
}
if (nearestUnit) {
// Create boss bullet
var bossBullet = new Bullet();
bossBullet.x = self.x;
bossBullet.y = self.y;
bossBullet.targetX = nearestUnit.x;
bossBullet.targetY = nearestUnit.y;
bossBullet.damage = 60;
bossBullet.isPlayerBullet = false;
bossBullet.speed = 6;
bullets.push(bossBullet);
game.addChild(bossBullet);
self.attackCooldown = self.attackDelay;
}
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
// Special area attack
if (self.specialAttackCooldown <= 0) {
// Damage all player units within range
for (var i = 0; i < playerUnits.length; i++) {
var unit = playerUnits[i];
var udx = unit.x - self.x;
var udy = unit.y - self.y;
var udistance = Math.sqrt(udx * udx + udy * udy);
if (udistance <= 400) {
unit.takeDamage(40);
createExplosion(unit.x, unit.y);
}
}
self.specialAttackCooldown = self.specialAttackDelay;
}
if (self.specialAttackCooldown > 0) {
self.specialAttackCooldown--;
}
};
return self;
});
var PlayerUnit = Container.expand(function () {
var self = Container.call(this);
self.health = 100;
self.maxHealth = 100;
self.attackRange = 200;
self.attackCooldown = 0;
self.attackDelay = 60;
self.damage = 25;
self.unitType = 'tank';
self.lastTarget = null;
self.rangeIndicator = null;
self.targetIndicator = null;
self.showingAttackInfo = false;
self.takeDamage = function (damage) {
// In hard mode, all units except sword have 1 health
if (isHardMode && self.unitType !== 'sword') {
self.health = 1;
self.maxHealth = 1;
}
// Only sword warriors can die, all other units are immortal
if (self.unitType !== 'sword') {
self.health -= damage;
if (self.health <= 0) {
self.health = 1; // Keep at least 1 health to prevent death
}
} else {
// Sword warriors can still die normally
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
self.isDead = true;
}
}
};
self.findNearestEnemy = function () {
var nearestEnemy = null;
var shortestDistance = self.attackRange;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
shortestDistance = distance;
nearestEnemy = enemy;
}
}
return nearestEnemy;
};
self.attack = function (target) {
if (self.attackCooldown <= 0) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = target.x;
bullet.targetY = target.y;
bullet.damage = self.damage;
bullet.isPlayerBullet = true;
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.attackCooldown = self.attackDelay;
// Update target indicator position if visible
if (self.showingAttackInfo && self.targetIndicator) {
self.targetIndicator.x = target.x;
self.targetIndicator.y = target.y;
}
}
};
self.showAttackInfo = function () {
if (self.showingAttackInfo) return;
// Create range indicator
self.rangeIndicator = LK.getAsset('attackRange', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3,
scaleX: self.attackRange / 5,
scaleY: self.attackRange / 5
});
self.rangeIndicator.x = self.x;
self.rangeIndicator.y = self.y;
game.addChild(self.rangeIndicator);
// Create target indicator if there's a current target
var currentTarget = self.findNearestEnemy();
if (currentTarget) {
self.targetIndicator = LK.getAsset('targetIndicator', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.targetIndicator.x = currentTarget.x;
self.targetIndicator.y = currentTarget.y;
game.addChild(self.targetIndicator);
}
self.showingAttackInfo = true;
};
self.hideAttackInfo = function () {
if (!self.showingAttackInfo) return;
if (self.rangeIndicator) {
self.rangeIndicator.destroy();
self.rangeIndicator = null;
}
if (self.targetIndicator) {
self.targetIndicator.destroy();
self.targetIndicator = null;
}
self.showingAttackInfo = false;
};
self.down = function (x, y, obj) {
// Hide attack info for all other units
for (var i = 0; i < playerUnits.length; i++) {
if (playerUnits[i] !== self) {
playerUnits[i].hideAttackInfo();
}
}
// Just show attack info without popup
self.showAttackInfo();
};
self.update = function () {
if (self.isDead) return;
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
var target = self.findNearestEnemy();
if (target) {
self.attack(target);
}
};
return self;
});
var Tank = PlayerUnit.expand(function () {
var self = PlayerUnit.call(this);
var tankGraphics = self.attachAsset('playerTank', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 150 + (upgrades.health - 1) * 30;
self.maxHealth = self.health;
self.attackRange = 250 + (upgrades.range - 1) * 25;
self.attackDelay = 45;
self.damage = 40 + (upgrades.damage - 1) * 10;
self.unitType = 'tank';
self.cost = 100;
return self;
});
var SwordWarrior = PlayerUnit.expand(function () {
var self = PlayerUnit.call(this);
var swordGraphics = self.attachAsset('swordWarrior', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 120 + (upgrades.health - 1) * 25;
self.maxHealth = self.health;
self.attackRange = 80; // Short melee range
self.attackDelay = 40;
self.damage = 50 + (upgrades.damage - 1) * 12;
self.unitType = 'sword';
self.cost = 90;
self.speed = 2.5; // Fast movement to reach enemies
self.currentTarget = null;
self.isMovingToTarget = false;
self.killCount = 0; // Track how many enemies killed
self.maxKills = 4; // Die after 4 kills
self.findNearestEnemy = function () {
var nearestEnemy = null;
var shortestDistance = 600; // Search range much larger than attack range
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead) continue;
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < shortestDistance) {
shortestDistance = distance;
nearestEnemy = enemy;
}
}
return nearestEnemy;
};
self.attack = function (target) {
if (self.attackCooldown <= 0) {
// Store target health before attack to check if it will die
var targetWillDie = target.health <= self.damage;
// Melee attack - damage directly without bullet
target.takeDamage(self.damage);
createExplosion(target.x, target.y);
LK.getSound('shoot').play();
self.attackCooldown = self.attackDelay;
// If target died from this attack, increment kill count
if (targetWillDie && target.health <= 0) {
self.killCount++;
// Die after killing 4 enemies
if (self.killCount >= self.maxKills) {
self.isDead = true;
}
}
}
};
self.update = function () {
if (self.isDead) {
// Create death explosion effect
createExplosion(self.x, self.y);
return;
}
if (self.attackCooldown > 0) {
self.attackCooldown--;
}
var target = self.findNearestEnemy();
if (target) {
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.attackRange) {
// In attack range - attack the target
self.attack(target);
self.isMovingToTarget = false;
} else {
// Move towards the target
self.isMovingToTarget = true;
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
self.currentTarget = target;
} else {
self.currentTarget = null;
self.isMovingToTarget = false;
}
};
return self;
});
var Sniper = PlayerUnit.expand(function () {
var self = PlayerUnit.call(this);
var sniperGraphics = self.attachAsset('playerInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
sniperGraphics.tint = 0x800080;
sniperGraphics.scaleX = 1.2;
sniperGraphics.scaleY = 1.2;
self.health = 50 + (upgrades.health - 1) * 12;
self.maxHealth = self.health;
self.attackRange = Math.min(2048, 1638); // 80% of arena width (2048 * 0.8)
self.attackDelay = 180; // Very slow (3 seconds)
self.damage = 80 + (upgrades.damage - 1) * 20;
self.unitType = 'sniper';
self.cost = 150;
return self;
});
var Infantry = PlayerUnit.expand(function () {
var self = PlayerUnit.call(this);
var infantryGraphics = self.attachAsset('playerInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 60 + (upgrades.health - 1) * 15;
self.maxHealth = self.health;
self.attackRange = 150 + (upgrades.range - 1) * 15;
self.attackDelay = 30;
self.damage = 20 + (upgrades.damage - 1) * 5;
self.unitType = 'infantry';
self.cost = 50;
return self;
});
var Artillery = PlayerUnit.expand(function () {
var self = PlayerUnit.call(this);
var artilleryGraphics = self.attachAsset('playerArtillery', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 80 + (upgrades.health - 1) * 20;
self.maxHealth = self.health;
self.attackRange = 350 + (upgrades.range - 1) * 35;
self.attackDelay = 90;
self.damage = 60 + (upgrades.damage - 1) * 15;
self.unitType = 'artillery';
self.cost = 120;
return self;
});
var Archer = PlayerUnit.expand(function () {
var self = PlayerUnit.call(this);
var archerGraphics = self.attachAsset('playerInfantry', {
anchorX: 0.5,
anchorY: 0.5
});
archerGraphics.tint = 0x8B4513;
self.health = 70 + (upgrades.health - 1) * 18;
self.maxHealth = self.health;
self.attackRange = 300 + (upgrades.range - 1) * 30;
self.attackDelay = 50;
self.damage = 40 + (upgrades.damage - 1) * 10;
self.unitType = 'archer';
self.cost = 80;
return self;
});
var UnitUpgradePopup = Container.expand(function () {
var self = Container.call(this);
// Create popup background
var popupBg = LK.getAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
scaleY: 4.5,
alpha: 0.9
});
self.addChild(popupBg);
popupBg.tint = 0x333333;
// Create upgrade buttons
// Create damage button background
var damageBtnBg = LK.getAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
alpha: 0.7
});
damageBtnBg.x = -200;
damageBtnBg.y = -80;
damageBtnBg.tint = 0x4a4a4a;
self.addChild(damageBtnBg);
var damageUpgradeBtn = new Text2('DAMAGE + (75)', {
size: 50,
fill: 0xFFFFFF
});
damageUpgradeBtn.anchor.set(0.5, 0.5);
damageUpgradeBtn.x = -200;
damageUpgradeBtn.y = -80;
self.addChild(damageUpgradeBtn);
// Create health button background
var healthBtnBg = LK.getAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
alpha: 0.7
});
healthBtnBg.x = 200;
healthBtnBg.y = -80;
healthBtnBg.tint = 0x4a4a4a;
self.addChild(healthBtnBg);
var healthUpgradeBtn = new Text2('HEALTH + (75)', {
size: 50,
fill: 0xFFFFFF
});
healthUpgradeBtn.anchor.set(0.5, 0.5);
healthUpgradeBtn.x = 200;
healthUpgradeBtn.y = -80;
self.addChild(healthUpgradeBtn);
// Create range button background
var rangeBtnBg = LK.getAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
alpha: 0.7
});
rangeBtnBg.x = 0;
rangeBtnBg.y = 80;
rangeBtnBg.tint = 0x4a4a4a;
self.addChild(rangeBtnBg);
var rangeUpgradeBtn = new Text2('RANGE + (75)', {
size: 50,
fill: 0xFFFFFF
});
rangeUpgradeBtn.anchor.set(0.5, 0.5);
rangeUpgradeBtn.x = 0;
rangeUpgradeBtn.y = 80;
self.addChild(rangeUpgradeBtn);
self.targetUnit = null;
self.upgradeCost = 75;
self.show = function (unit) {
self.targetUnit = unit;
self.x = unit.x;
self.y = unit.y - 100;
self.visible = true;
};
self.hide = function () {
if (self.targetUnit) {
self.targetUnit.hideAttackInfo();
}
self.visible = false;
self.targetUnit = null;
};
damageUpgradeBtn.down = function () {
if (self.targetUnit && resources >= self.upgradeCost) {
resources -= self.upgradeCost;
self.targetUnit.damage += 15;
resourceText.setText('Resources: ' + resources);
self.hide();
}
};
healthUpgradeBtn.down = function () {
if (self.targetUnit && resources >= self.upgradeCost) {
resources -= self.upgradeCost;
self.targetUnit.health += 50;
self.targetUnit.maxHealth += 50;
resourceText.setText('Resources: ' + resources);
self.hide();
}
};
rangeUpgradeBtn.down = function () {
if (self.targetUnit && resources >= self.upgradeCost) {
resources -= self.upgradeCost;
self.targetUnit.attackRange += 50;
resourceText.setText('Resources: ' + resources);
self.hide();
}
};
self.visible = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F2F
});
/****
* Game Code
****/
var playerUnits = [];
var enemies = [];
var bullets = [];
var explosions = [];
var resources = 200;
var maxResources = 500;
var resourceRegenRate = 1;
var resourceRegenTimer = 0;
var waveNumber = 1;
var enemiesSpawned = 0;
var enemiesPerWave = 5;
var spawnTimer = 0;
var spawnDelay = 120;
var timeBetweenWaves = 300;
var waveTimer = 0;
var isWaveActive = false;
var selectedUnitType = 'tank';
var unitCosts = {
tank: 100,
artillery: 120,
infantry: 50,
sword: 90,
sniper: 150
};
var isHardMode = false;
var isModeseMode = false;
// Create unit upgrade popup
var unitUpgradePopup = game.addChild(new UnitUpgradePopup());
// Initialize upgrade system
var upgrades = {
damage: 1,
health: 1,
range: 1
};
var upgradeBaseCost = 150;
// Create player base
var playerBase = game.addChild(new Base());
playerBase.x = 1024;
playerBase.y = 2400;
// Set base health to 1 in hard mode
if (isHardMode) {
playerBase.health = 1;
playerBase.maxHealth = 1;
}
// Place 1 archer inside the base with double tank range
var defenseArcher = game.addChild(new Archer());
defenseArcher.x = 1024;
defenseArcher.y = 2400; // Same position as base
defenseArcher.attackRange = 500; // Double the tank's base range (250 * 2)
playerUnits.push(defenseArcher);
// Create UI elements
var resourceText = new Text2('Resources: ' + resources, {
size: 40,
fill: 0xFFFFFF
});
resourceText.anchor.set(0, 0);
LK.gui.topLeft.addChild(resourceText);
resourceText.x = 120;
resourceText.y = 20;
var waveText = new Text2('Wave: ' + waveNumber, {
size: 40,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
waveText.y = 140;
var baseHealthText = new Text2('Base Health: ' + playerBase.health, {
size: 40,
fill: 0xFFFFFF
});
baseHealthText.anchor.set(1, 0);
LK.gui.topRight.addChild(baseHealthText);
baseHealthText.x = -20;
baseHealthText.y = 20;
var scoreText = new Text2('Score: 0', {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topLeft.addChild(scoreText);
scoreText.x = 120;
scoreText.y = 70;
// Unit selection buttons with square backgrounds
var tankButtonBg = LK.getAsset('playerTank', {
anchorX: 0.5,
anchorY: 1,
scaleX: 2,
scaleY: 2
});
LK.gui.bottom.addChild(tankButtonBg);
tankButtonBg.x = -500;
tankButtonBg.y = -20;
var tankButton = new Text2('TANK (100)', {
size: 35,
fill: 0xFFFFFF
});
tankButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(tankButton);
tankButton.x = -500;
tankButton.y = -80;
var artilleryButtonBg = LK.getAsset('playerArtillery', {
anchorX: 0.5,
anchorY: 1,
scaleX: 2,
scaleY: 2
});
LK.gui.bottom.addChild(artilleryButtonBg);
artilleryButtonBg.x = -200;
artilleryButtonBg.y = -20;
var artilleryButton = new Text2('ARTILLERY (120)', {
size: 35,
fill: 0xFFFFFF
});
artilleryButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(artilleryButton);
artilleryButton.x = -200;
artilleryButton.y = -80;
var infantryButtonBg = LK.getAsset('playerInfantry', {
anchorX: 0.5,
anchorY: 1,
scaleX: 2,
scaleY: 2
});
LK.gui.bottom.addChild(infantryButtonBg);
infantryButtonBg.x = 100;
infantryButtonBg.y = -20;
var infantryButton = new Text2('INFANTRY (50)', {
size: 35,
fill: 0xFFFFFF
});
infantryButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(infantryButton);
infantryButton.x = 100;
infantryButton.y = -80;
var swordButtonBg = LK.getAsset('swordWarrior', {
anchorX: 0.5,
anchorY: 1,
scaleX: 2,
scaleY: 2
});
LK.gui.bottom.addChild(swordButtonBg);
swordButtonBg.x = 350;
swordButtonBg.y = -20;
var swordButton = new Text2('SWORD (90)', {
size: 35,
fill: 0xFFFFFF
});
swordButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(swordButton);
swordButton.x = 350;
swordButton.y = -80;
var sniperButtonBg = LK.getAsset('playerInfantry', {
anchorX: 0.5,
anchorY: 1,
scaleX: 2,
scaleY: 2
});
sniperButtonBg.tint = 0x800080;
LK.gui.bottom.addChild(sniperButtonBg);
sniperButtonBg.x = 550;
sniperButtonBg.y = -20;
sniperButtonBg.visible = false;
var sniperButton = new Text2('SNIPER (150)', {
size: 35,
fill: 0xFFFFFF
});
sniperButton.anchor.set(0.5, 1);
LK.gui.bottom.addChild(sniperButton);
sniperButton.x = 550;
sniperButton.y = -80;
sniperButton.visible = false;
// Create upgrade buttons
var damageUpgradeButton = new Text2('DMG LV' + upgrades.damage + ' (' + upgradeBaseCost * upgrades.damage + ')', {
size: 50,
fill: 0xFFFFFF
});
damageUpgradeButton.anchor.set(1, 0);
LK.gui.topRight.addChild(damageUpgradeButton);
damageUpgradeButton.x = -20;
damageUpgradeButton.y = 70;
var healthUpgradeButton = new Text2('HP LV' + upgrades.health + ' (' + upgradeBaseCost * upgrades.health + ')', {
size: 50,
fill: 0xFFFFFF
});
healthUpgradeButton.anchor.set(1, 0);
LK.gui.topRight.addChild(healthUpgradeButton);
healthUpgradeButton.x = -20;
healthUpgradeButton.y = 130;
var rangeUpgradeButton = new Text2('RNG LV' + upgrades.range + ' (' + upgradeBaseCost * upgrades.range + ')', {
size: 50,
fill: 0xFFFFFF
});
rangeUpgradeButton.anchor.set(1, 0);
LK.gui.topRight.addChild(rangeUpgradeButton);
rangeUpgradeButton.x = -20;
rangeUpgradeButton.y = 190;
// Create modes button
var modesButtonBg = LK.getAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
alpha: 0.8
});
modesButtonBg.tint = 0x4a4a4a;
LK.gui.top.addChild(modesButtonBg);
modesButtonBg.x = 0;
modesButtonBg.y = 80;
var modesButton = new Text2('MODES', {
size: 45,
fill: 0xFFFFFF
});
modesButton.anchor.set(0.5, 0.5);
LK.gui.top.addChild(modesButton);
modesButton.x = 0;
modesButton.y = 80;
function updateSelectedUnit() {
tankButtonBg.tint = selectedUnitType === 'tank' ? 0xFFFF00 : 0x4169E1;
artilleryButtonBg.tint = selectedUnitType === 'artillery' ? 0xFFFF00 : 0x8B4513;
infantryButtonBg.tint = selectedUnitType === 'infantry' ? 0xFFFF00 : 0x228B22;
swordButtonBg.tint = selectedUnitType === 'sword' ? 0xFFFF00 : 0xb8860b;
if (isHardMode || isModeseMode) {
sniperButtonBg.tint = selectedUnitType === 'sniper' ? 0xFFFF00 : 0x800080;
}
}
updateSelectedUnit();
tankButtonBg.down = function () {
selectedUnitType = 'tank';
updateSelectedUnit();
};
artilleryButtonBg.down = function () {
selectedUnitType = 'artillery';
updateSelectedUnit();
};
infantryButtonBg.down = function () {
selectedUnitType = 'infantry';
updateSelectedUnit();
};
swordButtonBg.down = function () {
selectedUnitType = 'sword';
updateSelectedUnit();
};
sniperButtonBg.down = function () {
if (isHardMode || isModeseMode) {
selectedUnitType = 'sniper';
updateSelectedUnit();
}
};
damageUpgradeButton.down = function () {
var cost = upgradeBaseCost * upgrades.damage;
if (resources >= cost && upgrades.damage < 10) {
resources -= cost;
upgrades.damage++;
storage.damageLevel = upgrades.damage;
damageUpgradeButton.setText('DMG LV' + upgrades.damage + ' (' + upgradeBaseCost * upgrades.damage + ')');
resourceText.setText('Resources: ' + resources);
}
};
healthUpgradeButton.down = function () {
var cost = upgradeBaseCost * upgrades.health;
if (resources >= cost && upgrades.health < 10) {
resources -= cost;
upgrades.health++;
storage.healthLevel = upgrades.health;
healthUpgradeButton.setText('HP LV' + upgrades.health + ' (' + upgradeBaseCost * upgrades.health + ')');
resourceText.setText('Resources: ' + resources);
}
};
rangeUpgradeButton.down = function () {
var cost = upgradeBaseCost * upgrades.range;
if (resources >= cost && upgrades.range < 10) {
resources -= cost;
upgrades.range++;
storage.rangeLevel = upgrades.range;
rangeUpgradeButton.setText('RNG LV' + upgrades.range + ' (' + upgradeBaseCost * upgrades.range + ')');
resourceText.setText('Resources: ' + resources);
}
};
// Create hard mode popup (initially hidden)
var hardModePopupBg = LK.getAsset('playerBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 5,
alpha: 0.95
});
hardModePopupBg.tint = 0x1a1a1a;
LK.gui.center.addChild(hardModePopupBg);
hardModePopupBg.x = 0;
hardModePopupBg.y = 0;
hardModePopupBg.visible = false;
var hardModeButton = new Text2('OPEN HARD MODE', {
size: 60,
fill: 0xFF4500
});
hardModeButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(hardModeButton);
hardModeButton.x = 0;
hardModeButton.y = -50;
hardModeButton.visible = false;
var modeseModeButton = new Text2('OPEN FREE MODE', {
size: 60,
fill: 0x4B0082
});
modeseModeButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(modeseModeButton);
modeseModeButton.x = 0;
modeseModeButton.y = 30;
modeseModeButton.visible = false;
var closePopupButton = new Text2('CLOSE', {
size: 45,
fill: 0xFFFFFF
});
closePopupButton.anchor.set(0.5, 0.5);
LK.gui.center.addChild(closePopupButton);
closePopupButton.x = 0;
closePopupButton.y = 100;
closePopupButton.visible = false;
hardModeButton.down = function () {
// Activate hard mode
isHardMode = true;
hardModeButton.setText('HARD MODE ACTIVE');
hardModeButton.tint = 0x00FF00;
// Show sniper button in hard mode
sniperButtonBg.visible = true;
sniperButton.visible = true;
updateSelectedUnit();
// Close the popup
hardModePopupBg.visible = false;
hardModeButton.visible = false;
modeseModeButton.visible = false;
closePopupButton.visible = false;
};
modeseModeButton.down = function () {
// Activate modese mode
isModeseMode = true;
modeseModeButton.setText('FREE MODE ACTIVE');
modeseModeButton.tint = 0x00FF00;
// Set cash to 9999999 in free mode
resources = 9999999;
resourceText.setText('Resources: ' + resources);
// Show sniper button in free mode
sniperButtonBg.visible = true;
sniperButton.visible = true;
updateSelectedUnit();
// Close the popup
hardModePopupBg.visible = false;
hardModeButton.visible = false;
modeseModeButton.visible = false;
closePopupButton.visible = false;
};
closePopupButton.down = function () {
hardModePopupBg.visible = false;
hardModeButton.visible = false;
modeseModeButton.visible = false;
closePopupButton.visible = false;
};
modesButton.down = function () {
// Show hard mode popup
hardModePopupBg.visible = true;
hardModeButton.visible = true;
modeseModeButton.visible = true;
closePopupButton.visible = true;
};
function spawnEnemy() {
var enemy;
var spawnSide = Math.floor(Math.random() * 4);
var spawnX, spawnY;
// Random spawn from edges
switch (spawnSide) {
case 0:
// Top
spawnX = Math.random() * 2048;
spawnY = 0;
break;
case 1:
// Right
spawnX = 2048;
spawnY = Math.random() * 1500;
break;
case 2:
// Left
spawnX = 0;
spawnY = Math.random() * 1500;
break;
case 3:
// Top corners
spawnX = Math.random() < 0.5 ? 0 : 2048;
spawnY = Math.random() * 500;
break;
}
// Boss waves every 5 waves, or every wave in hard mode
if (waveNumber % 5 === 0 || isHardMode && Math.random() < 0.3) {
// Boss wave - spawn boss in center top
spawnX = 1024;
spawnY = 0;
if (waveNumber >= 15 && isHardMode) {
// The Big Boss for hard mode high waves
enemy = new TheBigBoss();
} else if (waveNumber >= 15) {
// Mega boss for higher waves
enemy = new MegaBoss();
} else {
// Regular boss
enemy = new EnemyBoss();
}
} else {
// Different enemies based on wave number
var enemyType = Math.random();
if (waveNumber <= 3) {
// Early waves: Basic infantry and some tanks
if (enemyType < 0.7) {
enemy = new EnemyInfantry();
} else {
enemy = new EnemyTank();
}
} else if (waveNumber <= 6) {
// Mid-early waves: Add fast enemies
if (enemyType < 0.4) {
enemy = new EnemyInfantry();
} else if (enemyType < 0.7) {
enemy = new EnemyTank();
} else if (enemyType < 0.85) {
enemy = new EnemyFast();
} else if (isHardMode) {
enemy = new FastZombie();
} else {
enemy = new EnemyFast();
}
} else if (waveNumber <= 10) {
// Mid waves: Add heavy enemies
if (enemyType < 0.3) {
enemy = new EnemyInfantry();
} else if (enemyType < 0.5) {
enemy = new EnemyTank();
} else if (enemyType < 0.65) {
enemy = new EnemyFast();
} else if (enemyType < 0.8) {
enemy = new EnemyHeavy();
} else if (isHardMode) {
enemy = new FastZombie();
} else {
enemy = new EnemyHeavy();
}
} else if (waveNumber <= 15) {
// Late-mid waves: Add swarm enemies
if (enemyType < 0.2) {
enemy = new EnemyInfantry();
} else if (enemyType < 0.4) {
enemy = new EnemyTank();
} else if (enemyType < 0.6) {
enemy = new EnemyFast();
} else if (enemyType < 0.8) {
enemy = new EnemyHeavy();
} else {
enemy = new EnemySwarm();
}
} else if (waveNumber <= 20) {
// Advanced waves: Add special enemies
if (enemyType < 0.1) {
enemy = new EnemyInfantry();
} else if (enemyType < 0.2) {
enemy = new EnemyTank();
} else if (enemyType < 0.3) {
enemy = new EnemyFast();
} else if (enemyType < 0.4) {
enemy = new EnemyHeavy();
} else if (enemyType < 0.5) {
enemy = new EnemySwarm();
} else if (enemyType < 0.6) {
enemy = new EnemyShielder();
} else if (enemyType < 0.7) {
enemy = new EnemyExploder();
} else if (enemyType < 0.8) {
enemy = new EnemyHealer();
} else if (enemyType < 0.9) {
enemy = new EnemyBoss();
} else if (isHardMode) {
enemy = new FastZombie();
} else {
enemy = new EnemyBoss();
}
} else {
// Ultimate waves: All enemy types with higher spawn rates for special enemies
if (enemyType < 0.05) {
enemy = new EnemyInfantry();
} else if (enemyType < 0.1) {
enemy = new EnemyTank();
} else if (enemyType < 0.2) {
enemy = new EnemyFast();
} else if (enemyType < 0.35) {
enemy = new EnemyHeavy();
} else if (enemyType < 0.45) {
enemy = new EnemySwarm();
} else if (enemyType < 0.6) {
enemy = new EnemyShielder();
} else if (enemyType < 0.75) {
enemy = new EnemyExploder();
} else if (enemyType < 0.85) {
enemy = new EnemyHealer();
} else {
enemy = new EnemyBoss();
}
}
}
enemy.x = spawnX;
enemy.y = spawnY;
// Bosses don't get additional speed/health scaling to maintain their unique stats
if (!enemy.isBoss) {
enemy.speed += Math.random() * 0.5 + waveNumber * 0.1;
// In hard mode, make all enemies much faster
if (isHardMode) {
enemy.speed *= 2.5; // 2.5x faster in hard mode
// FastZombie gets even more speed boost in hard mode
if (enemy.isHardModeOnly) {
enemy.speed *= 1.5; // Additional 1.5x for FastZombie (total 3.75x)
}
}
enemy.health += waveNumber * 10;
enemy.maxHealth = enemy.health;
}
enemies.push(enemy);
game.addChild(enemy);
}
function createExplosion(x, y) {
var explosion = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 1,
scaleX: 0.5,
scaleY: 0.5
});
game.addChild(explosion);
tween(explosion, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
LK.getSound('explosion').play();
}
game.down = function (x, y, obj) {
// Hide attack info for all units when clicking elsewhere
for (var i = 0; i < playerUnits.length; i++) {
playerUnits[i].hideAttackInfo();
}
// Check if click is on battlefield (not on UI) or near the base
var nearBase = false;
var distanceToBase = Math.sqrt((x - playerBase.x) * (x - playerBase.x) + (y - playerBase.y) * (y - playerBase.y));
if (distanceToBase <= 300) {
nearBase = true;
}
if (y > 200 && y < 2200 || nearBase) {
var cost = unitCosts[selectedUnitType];
// In free mode, all units cost 1
if (isModeseMode) {
cost = 1;
}
if (resources >= cost) {
var unit;
switch (selectedUnitType) {
case 'tank':
unit = new Tank();
break;
case 'artillery':
unit = new Artillery();
break;
case 'infantry':
unit = new Infantry();
break;
case 'sword':
unit = new SwordWarrior();
break;
case 'sniper':
if (isHardMode || isModeseMode) {
unit = new Sniper();
}
break;
}
unit.x = x;
unit.y = y;
playerUnits.push(unit);
game.addChild(unit);
resources -= cost;
resourceText.setText('Resources: ' + resources);
}
}
};
game.update = function () {
// Resource regeneration
resourceRegenTimer++;
if (resourceRegenTimer >= 60) {
if (resources < maxResources) {
resources += resourceRegenRate;
resourceText.setText('Resources: ' + resources);
}
resourceRegenTimer = 0;
}
// Wave management
if (!isWaveActive) {
waveTimer++;
var currentTimeBetweenWaves = timeBetweenWaves;
// In hard mode, much shorter time between waves
if (isHardMode) {
currentTimeBetweenWaves = Math.max(30, timeBetweenWaves / 6); // 6x faster wave progression, minimum 30 frames
}
if (waveTimer >= currentTimeBetweenWaves) {
isWaveActive = true;
enemiesSpawned = 0;
waveTimer = 0;
// Boss waves have fewer enemies but they're stronger
if (waveNumber % 5 === 0) {
enemiesPerWave = isHardMode ? 3 : 1; // Hard mode: 3 bosses, normal: 1 boss
} else {
var baseEnemies = 5 + waveNumber * 2;
enemiesPerWave = isHardMode ? baseEnemies * 5 : baseEnemies; // Hard mode: 5x enemies
}
}
} else {
spawnTimer++;
// Boss waves spawn immediately, regular waves use spawn delay
var currentSpawnDelay = waveNumber % 5 === 0 ? 0 : spawnDelay;
// In hard mode, make spawning much faster
if (isHardMode) {
currentSpawnDelay = Math.max(5, currentSpawnDelay / 8); // 8x faster spawning, minimum 5 frames
}
if (spawnTimer >= currentSpawnDelay && enemiesSpawned < enemiesPerWave) {
spawnEnemy();
enemiesSpawned++;
spawnTimer = 0;
}
// Check if wave is complete
if (enemiesSpawned >= enemiesPerWave && enemies.length === 0) {
isWaveActive = false;
waveNumber++;
waveText.setText('Wave: ' + waveNumber);
LK.setScore(LK.getScore() + 100);
scoreText.setText('Score: ' + LK.getScore());
// Award 30 resources for completing the wave
resources += 30;
resourceText.setText('Resources: ' + resources);
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
if (bullet.hasReachedTarget || bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
if (bullet.hasReachedTarget) {
createExplosion(bullet.x, bullet.y);
}
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check bullet collisions
if (bullet.isPlayerBullet) {
// Player bullets hit enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (!enemy.isDead && bullet.intersects(enemy)) {
enemy.takeDamage(bullet.damage);
createExplosion(bullet.x, bullet.y);
if (enemy.isDead) {
// Check if this was an exploder and trigger explosion
if (enemy.explodeOnDeath) {
enemy.explodeOnDeath();
}
// Boss kills give more points
var scoreReward = enemy.isBoss ? 500 : 25;
LK.setScore(LK.getScore() + scoreReward);
scoreText.setText('Score: ' + LK.getScore());
// Award resources for each enemy killed (5 in hard mode, 3 in normal)
resources += isHardMode ? 5 : 3;
resourceText.setText('Resources: ' + resources);
enemy.destroy();
enemies.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
break;
}
}
} else {
// Enemy/Boss bullets hit player units
for (var j = playerUnits.length - 1; j >= 0; j--) {
var unit = playerUnits[j];
if (!unit.isDead && bullet.intersects(unit)) {
unit.takeDamage(bullet.damage);
createExplosion(bullet.x, bullet.y);
if (unit.isDead) {
unit.destroy();
playerUnits.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
break;
}
}
}
}
// Update player units
for (var i = playerUnits.length - 1; i >= 0; i--) {
var unit = playerUnits[i];
unit.update();
if (unit.isDead) {
unit.destroy();
playerUnits.splice(i, 1);
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.update();
if (enemy.isDead) {
// Check if this was an exploder and trigger explosion
if (enemy.explodeOnDeath) {
enemy.explodeOnDeath();
}
// Award resources for each enemy killed (5 in hard mode, 3 in normal)
resources += isHardMode ? 5 : 3;
resourceText.setText('Resources: ' + resources);
enemy.destroy();
enemies.splice(i, 1);
} else if (enemy.hasReachedBase) {
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update base health display
baseHealthText.setText('Base Health: ' + playerBase.health);
// Check game over condition
if (playerBase.isDead) {
LK.showGameOver();
}
// Check win condition (survive 20 waves)
if (waveNumber > 20) {
LK.showYouWin();
}
};