User prompt
Increase the archer tower’s HP by 5 times. agein
User prompt
Increase the archer tower’s HP by 5 times.
User prompt
The game should end when the archer towers are destroyed; this rule only applies to the main building and the character.
User prompt
The game should be over when the character’s HP or the main building’s HP drops below 1.
User prompt
The game should be over when the main building’s HP is 0 or goes below zero (negative values).
User prompt
The game should not be over unless the main building's HP reaches 0.
User prompt
At wave 10, when one of the two bosses dies, consider the other boss as dead as well (both die simultaneously).
User prompt
Do not end the game unless a tower’s HP reaches exactly 0 (not above 0 or below -1). Every 5 waves, increase the character’s attack power and multiply both the towers’ HP and attack power by 5.
User prompt
much Move the waves 1, 5, 10, 15, 20, 30, 40, and 50 bar slightly downward on the screen.
User prompt
Move the waves 1, 5, 10, 15, 20, 30, 40, and 50 bar slightly downward on the screen.
User prompt
Make the “Pause” (or “Stop”) button smaller in size.
User prompt
Move the waves 1, 5, 10, 15, 20, 30, 40, and 50 bar slightly downward on the screen.
User prompt
Move the wave bar slightly downward on the screen.
User prompt
The game should end when any tower’s HP reaches zero or below.
User prompt
Add buttons for waves 1, 5, 10, 15, 20, 30, 40, and 50 to the game interface. When a button is clicked, start the corresponding wave immediately.
User prompt
The only condition to advance from wave 5 to wave 6 should be that the boss’s HP reaches 0; remove all other conditions for this transition.
User prompt
After the boss is defeated, 30 enemies arrive in groups of 5 (five at a time), not together with the boss.
User prompt
Correction: In wave 5, the boss attacks first alone, and after the boss is defeated, 30 enemies arrive sequentially.
User prompt
In wave 5, have 30 enemies attack together along with the boss.
User prompt
Remove the cannon button from the game interface. Start wave 6 only after all the gold on the map has been collected.
User prompt
After every 30 enemy kills, advance to the next wave.
User prompt
Make the gold and enemies counters smaller and shift them further to the left.
User prompt
Display the number of enemies killed below the gold counter, and shift both counters slightly to the left on the screen.
User prompt
Remove the sound on/off button from the screen.
User prompt
Display a sound on/off button on the screen that, when clicked, mutes the game audio. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); self.health = Math.floor(8000 * difficultyScaling.healthMultiplier); self.maxHealth = Math.floor(8000 * difficultyScaling.healthMultiplier); self.speed = 0.6; self.goldValue = 100; self.currentTarget = null; self.lastX = 0; self.lastY = 0; self.dustTimer = 0; self.motionEffects = []; var graphics = self.attachAsset('boos', { anchorX: 0.5, anchorY: 1 }); // Scale boss to be larger graphics.scaleX = 1.5; graphics.scaleY = 1.5; graphics.tint = 0x800080; // Purple tint for boss // Add glowing red eyes self.redEyes = self.attachAsset('redEyes', { anchorX: 0.5, anchorY: 0.5 }); self.redEyes.y = -graphics.height * 0.8; self.redEyes.scaleX = 1.5; self.redEyes.scaleY = 1.5; // Make eyes glow with pulsing effect self.eyeGlowDirection = 1; self.eyeGlowAlpha = 0.8; self.targetX = 1024; self.targetY = 1366; self.findNearestTarget = function () { var nearestTarget = null; var shortestDistance = Infinity; // Check crystal tower var dx = crystalTower.x - self.x; var dy = crystalTower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); nearestTarget = crystalTower; shortestDistance = distance; // Check hero dx = hero.x - self.x; dy = hero.y - self.y; distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { nearestTarget = hero; shortestDistance = distance; } // Check towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; dx = tower.x - self.x; dy = tower.y - self.y; distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { nearestTarget = tower; shortestDistance = distance; } } return nearestTarget; }; self.createDustCloud = function () { var dust = LK.getAsset('dustCloud', { anchorX: 0.5, anchorY: 0.5 }); dust.x = self.x + (Math.random() - 0.5) * 30; dust.y = self.y + (Math.random() - 0.5) * 20; dust.alpha = 0.6; game.addChild(dust); // Animate dust cloud tween(dust, { alpha: 0, scaleX: 1.5, scaleY: 1.5, y: dust.y - 20 }, { duration: Math.floor(800 / gameSpeed), easing: tween.easeOut, onFinish: function onFinish() { dust.destroy(); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.die(); } }; self.die = function () { // Clean up motion effects for (var i = 0; i < self.motionEffects.length; i++) { self.motionEffects[i].destroy(); } var coin = new GoldCoin(); coin.x = self.x; coin.y = self.y; coin.value = self.goldValue; game.addChild(coin); goldCoins.push(coin); LK.getSound('enemyDeath').play(); enemyKills++; // Increment kill counter when boss dies killsText.setText('Enemies Killed: ' + enemyKills); // Continue wave progression after boss death and re-enable hero shooting bossActive = false; heroCanShoot = true; // Special wave advancement for wave 5 to 6 transition if (wave === 5) { wave++; enemiesInWave += 2; enemiesSpawned = 0; enemyKills = 0; // Reset kill counter for next wave killsText.setText('Enemies Killed: ' + enemyKills); waveText.setText('Wave: ' + wave); } // Remove boss from enemies array for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.update = function () { // Store last position for motion effects self.lastX = self.x; self.lastY = self.y; // Update glowing eyes effect self.eyeGlowAlpha += self.eyeGlowDirection * 0.05 * gameSpeed; if (self.eyeGlowAlpha >= 1) { self.eyeGlowDirection = -1; } else if (self.eyeGlowAlpha <= 0.5) { self.eyeGlowDirection = 1; } self.redEyes.alpha = self.eyeGlowAlpha; // Find nearest target and charge toward it self.currentTarget = self.findNearestTarget(); var targetX = self.currentTarget.x; var targetY = self.currentTarget.y; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Move toward target with charging behavior var moveX = dx / distance * self.speed * gameSpeed; var moveY = dy / distance * self.speed * gameSpeed; self.x += moveX; self.y += moveY; // Create dust clouds periodically while moving self.dustTimer += gameSpeed; if (self.dustTimer >= 15) { self.createDustCloud(); self.dustTimer = 0; } } else { // Attack the target (boss deals more damage) if (self.currentTarget === crystalTower) { crystalTower.takeDamage(40); enemyAttacks++; } else if (self.currentTarget === hero) { // Flash hero red when attacked LK.effects.flashObject(hero, 0xff0000, 500); // Damage hero heroHealth -= 50; enemyAttacks++; if (heroHealth <= 0 && !heroCollapsed) { heroCollapsed = true; // Hero collapse effect tween(hero, { alpha: 0.3, scaleX: 0.8, scaleY: 0.4, rotation: Math.PI / 2 }, { duration: Math.floor(1000 / gameSpeed), easing: tween.easeOut }); // Add smoke effect var smoke = new SmokeEffect(); smoke.x = hero.x; smoke.y = hero.y; game.addChild(smoke); } } else { // Check if target is a tower for (var i = 0; i < towers.length; i++) { if (towers[i] === self.currentTarget) { self.currentTarget.takeDamage(); enemyAttacks++; break; } } } } }; return self; }); var CrystalTower = Container.expand(function () { var self = Container.call(this); self.health = 1500; self.maxHealth = 1500; self.range = 200; self.damage = 40; self.fireRate = 30; self.lastShot = 0; self.hits = 0; self.maxHits = 10; // Create magic aura for attack radius visualization self.magicAura = new MagicAura(); self.addChild(self.magicAura); // Scale aura to match tower range (range 200 = diameter 400) self.magicAura.scaleX = self.range * 2 / 400; self.magicAura.scaleY = self.range * 2 / 400; var graphics = self.attachAsset('crystalTower', { anchorX: 0.5, anchorY: 1 }); self.takeDamage = function (damage) { self.health -= damage; self.hits++; // Tower blinks red when taking damage tween(graphics, { tint: 0xff0000 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(graphics, { tint: 0xffffff }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(graphics, { tint: 0xff0000 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(graphics, { tint: 0xffffff }, { duration: 150, easing: tween.easeOut }); } }); } }); } }); LK.effects.flashObject(self, 0xff0000, 500); // Add damage effects based on health percentage var healthPercent = self.health / self.maxHealth; if (healthPercent < 0.7) { // Add cracks var crack = LK.getAsset('crackEffect', { anchorX: 0.5, anchorY: 0.5 }); crack.x = self.x + (Math.random() - 0.5) * 100; crack.y = self.y - Math.random() * 150; crack.rotation = Math.random() * Math.PI; crack.alpha = 0.8; game.addChild(crack); } if (healthPercent < 0.5) { // Add smoke effects var smoke = new SmokeEffect(); smoke.x = self.x + (Math.random() - 0.5) * 80; smoke.y = self.y - 100 - Math.random() * 50; game.addChild(smoke); } if (healthPercent < 0.3) { // Add red alert glow LK.effects.flashScreen(0xff0000, 300); } if (self.hits >= self.maxHits) { // Create massive explosion effect for (var i = 0; i < 5; i++) { var explosion = new ExplosionEffect(); explosion.x = self.x + (Math.random() - 0.5) * 150; explosion.y = self.y - Math.random() * 200; game.addChild(explosion); } // Play destruction sound LK.getSound('towerDestroy').play(); // Flash screen red for dramatic effect LK.effects.flashScreen(0xff0000, 1500); // Only show game over if both tower and hero HP reach 0 if (heroHealth <= 0) { LK.showGameOver(); } } }; self.findTarget = function () { var closest = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closest = enemy; closestDistance = distance; } } return closest; }; self.shoot = function (target) { if (LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed)) { var projectile = new TowerProjectile(); projectile.x = self.x; projectile.y = self.y - 60; projectile.target = target; projectile.damage = self.damage; game.addChild(projectile); towerProjectiles.push(projectile); self.lastShot = LK.ticks; // Trigger magical attack aura effect self.magicAura.showAttackPulse(); // Add magical flash effect to tower LK.effects.flashObject(graphics, 0x4169e1, 500); // Add muzzle flash var flash = new MuzzleFlash(); flash.x = self.x; flash.y = self.y - 80; game.addChild(flash); LK.getSound('towerShoot').play(); } }; self.update = function () { var target = self.findTarget(); if (target) { self.shoot(target); } }; return self; }); var Enemy = Container.expand(function (type) { var self = Container.call(this); self.enemyType = type || 'goblin'; self.health = 100; self.maxHealth = 100; self.speed = 1; self.goldValue = 10; self.currentTarget = null; self.lastX = 0; self.lastY = 0; self.dustTimer = 0; self.motionEffects = []; if (self.enemyType === 'goblin') { self.health = Math.floor(160 * difficultyScaling.healthMultiplier); self.maxHealth = Math.floor(160 * difficultyScaling.healthMultiplier); self.speed = 1.5; self.goldValue = 15; var graphics = self.attachAsset('goblin', { anchorX: 0.5, anchorY: 1 }); } else if (self.enemyType === 'troll') { self.health = Math.floor(400 * difficultyScaling.healthMultiplier); self.maxHealth = Math.floor(400 * difficultyScaling.healthMultiplier); self.speed = 0.8; self.goldValue = 30; var graphics = self.attachAsset('troll', { anchorX: 0.5, anchorY: 1 }); } else if (self.enemyType === 'skeleton') { self.health = Math.floor(240 * difficultyScaling.healthMultiplier); self.maxHealth = Math.floor(240 * difficultyScaling.healthMultiplier); self.speed = 1.2 * difficultyScaling.skeletonSpeedMultiplier; self.goldValue = 20; var graphics = self.attachAsset('skeleton', { anchorX: 0.5, anchorY: 1 }); } // Add glowing red eyes self.redEyes = self.attachAsset('redEyes', { anchorX: 0.5, anchorY: 0.5 }); self.redEyes.y = -graphics.height * 0.8; // Make eyes glow with pulsing effect self.eyeGlowDirection = 1; self.eyeGlowAlpha = 0.8; self.targetX = 1024; self.targetY = 1366; self.findNearestTarget = function () { var nearestTarget = null; var shortestDistance = Infinity; // Check crystal tower var dx = crystalTower.x - self.x; var dy = crystalTower.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); nearestTarget = crystalTower; shortestDistance = distance; // Check hero dx = hero.x - self.x; dy = hero.y - self.y; distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { nearestTarget = hero; shortestDistance = distance; } // Check towers for (var i = 0; i < towers.length; i++) { var tower = towers[i]; dx = tower.x - self.x; dy = tower.y - self.y; distance = Math.sqrt(dx * dx + dy * dy); if (distance < shortestDistance) { nearestTarget = tower; shortestDistance = distance; } } return nearestTarget; }; self.createDustCloud = function () { var dust = LK.getAsset('dustCloud', { anchorX: 0.5, anchorY: 0.5 }); dust.x = self.x + (Math.random() - 0.5) * 30; dust.y = self.y + (Math.random() - 0.5) * 20; dust.alpha = 0.6; game.addChild(dust); // Animate dust cloud tween(dust, { alpha: 0, scaleX: 1.5, scaleY: 1.5, y: dust.y - 20 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { dust.destroy(); } }); }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.die(); } }; self.die = function () { // Clean up motion effects for (var i = 0; i < self.motionEffects.length; i++) { self.motionEffects[i].destroy(); } var coin = new GoldCoin(); coin.x = self.x; coin.y = self.y; coin.value = self.goldValue; game.addChild(coin); goldCoins.push(coin); LK.getSound('enemyDeath').play(); enemyKills++; // Increment kill counter when enemy dies killsText.setText('Enemies Killed: ' + enemyKills); for (var i = enemies.length - 1; i >= 0; i--) { if (enemies[i] === self) { enemies.splice(i, 1); break; } } self.destroy(); }; self.update = function () { // Store last position for motion effects self.lastX = self.x; self.lastY = self.y; // Update glowing eyes effect self.eyeGlowAlpha += self.eyeGlowDirection * 0.05 * gameSpeed; if (self.eyeGlowAlpha >= 1) { self.eyeGlowDirection = -1; } else if (self.eyeGlowAlpha <= 0.5) { self.eyeGlowDirection = 1; } self.redEyes.alpha = self.eyeGlowAlpha; // Find nearest target and charge toward it self.currentTarget = self.findNearestTarget(); var targetX = self.currentTarget.x; var targetY = self.currentTarget.y; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { // Move toward target with charging behavior var moveX = dx / distance * self.speed * gameSpeed; var moveY = dy / distance * self.speed * gameSpeed; self.x += moveX; self.y += moveY; // Create dust clouds periodically while moving self.dustTimer += gameSpeed; if (self.dustTimer >= 15) { self.createDustCloud(); self.dustTimer = 0; } } else { // Attack the target if (self.currentTarget === crystalTower) { crystalTower.takeDamage(20); enemyAttacks++; } else if (self.currentTarget === hero) { // Flash hero red when attacked LK.effects.flashObject(hero, 0xff0000, 500); // Damage hero heroHealth -= 25; enemyAttacks++; if (heroHealth <= 0 && !heroCollapsed) { heroCollapsed = true; // Hero collapse effect tween(hero, { alpha: 0.3, scaleX: 0.8, scaleY: 0.4, rotation: Math.PI / 2 }, { duration: Math.floor(1000 / gameSpeed), easing: tween.easeOut }); // Add smoke effect var smoke = new SmokeEffect(); smoke.x = hero.x; smoke.y = hero.y; game.addChild(smoke); } } else { // Check if target is a tower for (var i = 0; i < towers.length; i++) { if (towers[i] === self.currentTarget) { self.currentTarget.takeDamage(); enemyAttacks++; break; } } } } }; return self; }); var ExplosionEffect = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); graphics.alpha = 0.8; self.scaleX = 0.3; self.scaleY = 0.3; // Animate explosion tween(self, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); return self; }); var GoldCoin = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('goldCoin', { anchorX: 0.5, anchorY: 0.5 }); self.value = 10; self.collected = false; self.collect = function () { if (!self.collected) { self.collected = true; gold += self.value; goldText.setText('Gold: ' + gold); LK.getSound('coinCollect').play(); for (var i = goldCoins.length - 1; i >= 0; i--) { if (goldCoins[i] === self) { goldCoins.splice(i, 1); break; } } self.destroy(); } }; self.down = function (x, y, obj) { self.collect(); }; return self; }); var GrassPatch = Container.expand(function (grassType) { var self = Container.call(this); self.grassType = grassType || 'grassPatch'; self.swayAmount = 0.1 + Math.random() * 0.15; // Random sway intensity self.swaySpeed = 2000 + Math.random() * 1000; // Random sway duration var graphics = self.attachAsset(self.grassType, { anchorX: 0.5, anchorY: 1 }); // Start initial sway animation self.startSway = function () { var targetRotation = self.swayAmount * (Math.random() > 0.5 ? 1 : -1); tween(self, { rotation: targetRotation }, { duration: self.swaySpeed, easing: tween.easeInOut, onFinish: function onFinish() { // Reverse sway direction var reverseRotation = -targetRotation * (0.8 + Math.random() * 0.4); tween(self, { rotation: reverseRotation }, { duration: self.swaySpeed * (0.8 + Math.random() * 0.4), easing: tween.easeInOut, onFinish: function onFinish() { self.startSway(); // Continue swaying indefinitely } }); } }); }; // Start swaying with random delay to create natural effect LK.setTimeout(function () { self.startSway(); }, Math.random() * 2000); return self; }); var Hero = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('hero', { anchorX: 0.5, anchorY: 1 }); self.speed = 3; self.range = 450; self.damage = 35; self.fireRate = 20; self.lastShot = 0; self.findTarget = function () { var closest = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closest = enemy; closestDistance = distance; } } return closest; }; self.shoot = function (target) { if (LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed)) { var projectile = new HeroProjectile(); projectile.x = self.x; projectile.y = self.y - 25; projectile.target = target; projectile.damage = self.damage; game.addChild(projectile); heroProjectiles.push(projectile); self.lastShot = LK.ticks; } }; self.update = function () { // Only shoot if hero is not collapsed (dead) and is allowed to shoot if (!heroCollapsed && heroCanShoot) { var target = self.findTarget(); if (target) { self.shoot(target); } } }; return self; }); var HeroProjectile = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('heroProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 10; self.target = null; self.damage = 35; self.update = function () { if (!self.target || self.target.destroyed) { self.destroy(); for (var i = heroProjectiles.length - 1; i >= 0; i--) { if (heroProjectiles[i] === self) { heroProjectiles.splice(i, 1); break; } } return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.target.takeDamage(self.damage); LK.getSound('arrowImpact').play(); self.destroy(); for (var i = heroProjectiles.length - 1; i >= 0; i--) { if (heroProjectiles[i] === self) { heroProjectiles.splice(i, 1); break; } } } else { self.x += dx / distance * self.speed * gameSpeed; self.y += dy / distance * self.speed * gameSpeed; } }; return self; }); var MagicAura = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('magicAura', { anchorX: 0.5, anchorY: 0.5 }); graphics.alpha = 0.2; self.pulseDirection = 1; self.basePulse = 0.15; self.currentPulse = self.basePulse; self.update = function () { // Create pulsing effect self.currentPulse += self.pulseDirection * 0.003; if (self.currentPulse >= 0.4) { self.pulseDirection = -1; } else if (self.currentPulse <= self.basePulse) { self.pulseDirection = 1; } graphics.alpha = self.currentPulse; // Slight rotation for magical effect graphics.rotation += 0.01; }; self.showAttackPulse = function () { // Flash brighter when tower attacks tween(graphics, { alpha: 0.7, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(graphics, { alpha: self.currentPulse, scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); }; return self; }); var MuzzleFlash = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('muzzleFlash', { anchorX: 0.5, anchorY: 0.5 }); graphics.alpha = 0.9; self.scaleX = 0.5; self.scaleY = 0.5; // Quick flash effect tween(self, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); return self; }); var SmokeEffect = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('smokeCloud', { anchorX: 0.5, anchorY: 0.5 }); graphics.alpha = 0.4; self.scaleX = 0.8; self.scaleY = 0.8; // Rising smoke animation tween(self, { y: self.y - 100, scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); return self; }); var Tower = Container.expand(function (type) { var self = Container.call(this); self.towerType = type || 'archer'; self.range = 150; self.damage = 25; self.fireRate = 60; self.lastShot = 0; self.hits = 0; self.maxHits = 4; if (self.towerType === 'archer') { self.range = 180; self.damage = 30; self.fireRate = 45; self.maxHits = 40; var graphics = self.attachAsset('archerTower', { anchorX: 0.5, anchorY: 1 }); } else if (self.towerType === 'cannon') { self.range = 120; self.damage = 60; self.fireRate = 90; self.maxHits = 40; var graphics = self.attachAsset('cannonTower', { anchorX: 0.5, anchorY: 1 }); } self.takeDamage = function () { self.hits++; // Tower blinks red when taking damage tween(graphics, { tint: 0xff0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(graphics, { tint: 0xffffff }, { duration: 200, easing: tween.easeOut }); } }); if (self.hits >= self.maxHits) { // Create explosion effect var explosion = new ExplosionEffect(); explosion.x = self.x; explosion.y = self.y; game.addChild(explosion); // Play destruction sound LK.getSound('towerDestroy').play(); // Remove from towers array for (var i = towers.length - 1; i >= 0; i--) { if (towers[i] === self) { towers.splice(i, 1); break; } } self.destroy(); } }; self.findTarget = function () { var closest = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < closestDistance) { closest = enemy; closestDistance = distance; } } return closest; }; self.shoot = function (target) { if (LK.ticks - self.lastShot >= Math.floor(self.fireRate / gameSpeed)) { var projectile = new TowerProjectile(); projectile.x = self.x; projectile.y = self.y - 40; projectile.target = target; projectile.damage = self.damage; game.addChild(projectile); towerProjectiles.push(projectile); self.lastShot = LK.ticks; LK.getSound('towerShoot').play(); // Add muzzle flash effect var flash = new MuzzleFlash(); flash.x = self.x; flash.y = self.y - 30; game.addChild(flash); } }; self.update = function () { var target = self.findTarget(); if (target) { self.shoot(target); } }; return self; }); var TowerProjectile = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('towerProjectile', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.target = null; self.damage = 25; self.update = function () { if (!self.target || self.target.destroyed) { self.destroy(); for (var i = towerProjectiles.length - 1; i >= 0; i--) { if (towerProjectiles[i] === self) { towerProjectiles.splice(i, 1); break; } } return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 20) { self.target.takeDamage(self.damage); self.destroy(); for (var i = towerProjectiles.length - 1; i >= 0; i--) { if (towerProjectiles[i] === self) { towerProjectiles.splice(i, 1); break; } } } else { self.x += dx / distance * self.speed * gameSpeed; self.y += dy / distance * self.speed * gameSpeed; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a4d1a }); /**** * Game Code ****/ var enemies = []; var towers = []; var towerProjectiles = []; var heroProjectiles = []; var goldCoins = []; var crystalTower; var hero; var gold = 100; var wave = 1; var enemiesInWave = 5; var enemySpawnTimer = 0; var minEnemiesOnField = 3; // Always maintain minimum enemies var enemiesSpawned = 0; var waveComplete = false; var draggedHero = false; var heroCollapsed = false; var heroHealth = 1000; var heroMaxHealth = 1000; var enemyAttacks = 0; var enemyKills = 0; var bossActive = false; var difficultyScaling = { healthMultiplier: 1.0, skeletonSpeedMultiplier: 1.0 }; // Control hero shooting behavior var heroCanShoot = true; // Track enemies in contact with crystal tower for continuous damage var enemiesInContact = []; var contactDamageTimer = 0; // Game speed control var gameSpeed = 1.0; var gameSpeedOptions = [1.0, 1.5, 2.0, 3.0, 5.0]; var currentSpeedIndex = 0; // Sound control var soundMuted = false; // Grass background removed - clean battlefield environment var grassPatches = []; // Create crystal tower at center crystalTower = new CrystalTower(); crystalTower.x = 1024; crystalTower.y = 1366; game.addChild(crystalTower); // Create hero hero = new Hero(); hero.x = 900; hero.y = 1200; game.addChild(hero); // Create hero HP bar that follows hero var heroHealthBar = LK.getAsset('uiPanel', { anchorX: 0.5, anchorY: 0.5 }); heroHealthBar.scaleX = 1.0; heroHealthBar.scaleY = 0.6; heroHealthBar.tint = 0x404040; game.addChild(heroHealthBar); var heroHealthFill = LK.getAsset('uiPanel', { anchorX: 0, anchorY: 0.5 }); heroHealthFill.scaleX = 1.0; heroHealthFill.scaleY = 0.6; heroHealthFill.tint = 0x00ff00; heroHealthBar.addChild(heroHealthFill); heroHealthFill.x = -100; heroHealthFill.y = 0; var heroHealthText = new Text2(heroHealth + '/' + heroMaxHealth, { size: 28, fill: 0xFFFFFF }); heroHealthText.anchor.set(0.5, 0.5); heroHealthBar.addChild(heroHealthText); // UI Elements var goldText = new Text2('Gold: ' + gold, { size: 40, fill: 0xFFD700 }); goldText.anchor.set(0, 0); LK.gui.topRight.addChild(goldText); goldText.x = -350; goldText.y = 20; var killsText = new Text2('Enemies Killed: ' + enemyKills, { size: 40, fill: 0xFFFFFF }); killsText.anchor.set(0, 0); LK.gui.topRight.addChild(killsText); killsText.x = -350; killsText.y = 70; var waveText = new Text2('Wave: ' + wave, { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0, 0); LK.gui.top.addChild(waveText); waveText.x = -100; waveText.y = 20; var healthText = new Text2('Tower HP: ' + crystalTower.health, { size: 50, fill: 0x00FFFF }); healthText.anchor.set(0, 0); LK.gui.bottom.addChild(healthText); healthText.x = -150; healthText.y = -80; // Boss health bar (initially hidden) var bossHealthBar = LK.getAsset('uiPanel', { anchorX: 0.5, anchorY: 0.5 }); bossHealthBar.scaleX = 4; bossHealthBar.scaleY = 0.5; bossHealthBar.tint = 0x800080; bossHealthBar.visible = false; LK.gui.top.addChild(bossHealthBar); bossHealthBar.x = 0; bossHealthBar.y = 100; var bossHealthFill = LK.getAsset('uiPanel', { anchorX: 0, anchorY: 0.5 }); bossHealthFill.scaleX = 4; bossHealthFill.scaleY = 0.4; bossHealthFill.tint = 0xff0000; bossHealthFill.visible = false; bossHealthBar.addChild(bossHealthFill); bossHealthFill.x = -400; bossHealthFill.y = 0; var bossHealthText = new Text2('BOSS', { size: 40, fill: 0xFFFFFF }); bossHealthText.anchor.set(0.5, 0.5); bossHealthText.visible = false; LK.gui.top.addChild(bossHealthText); bossHealthText.x = 0; bossHealthText.y = 60; // Tower placement UI var archerButton = LK.getAsset('uiPanel', { anchorX: 0.5, anchorY: 0.5 }); archerButton.x = 150; archerButton.y = -150; LK.gui.bottom.addChild(archerButton); var archerText = new Text2('Archer\n50g', { size: 30, fill: 0xFFFFFF }); archerText.anchor.set(0.5, 0.5); archerButton.addChild(archerText); var cannonButton = LK.getAsset('uiPanel', { anchorX: 0.5, anchorY: 0.5 }); cannonButton.x = 400; cannonButton.y = -150; LK.gui.bottom.addChild(cannonButton); var cannonText = new Text2('Cannon\n80g', { size: 30, fill: 0xFFFFFF }); cannonText.anchor.set(0.5, 0.5); cannonButton.addChild(cannonText); // Game speed button var speedButton = LK.getAsset('uiPanel', { anchorX: 0.5, anchorY: 0.5 }); speedButton.x = 150; speedButton.y = -50; speedButton.scaleX = 1.2; speedButton.scaleY = 0.8; speedButton.tint = 0x4169e1; LK.gui.bottomLeft.addChild(speedButton); var speedText = new Text2('Speed: 1.0x', { size: 28, fill: 0xFFFFFF }); speedText.anchor.set(0.5, 0.5); speedButton.addChild(speedText); // Restart button var restartButton = LK.getAsset('uiPanel', { anchorX: 0.5, anchorY: 0.5 }); restartButton.x = 150; restartButton.y = -200; restartButton.scaleX = 1.2; restartButton.scaleY = 0.8; restartButton.tint = 0xff4500; LK.gui.bottomLeft.addChild(restartButton); var restartText = new Text2('Restart', { size: 28, fill: 0xFFFFFF }); restartText.anchor.set(0.5, 0.5); restartButton.addChild(restartText); // Map panning state var isPanning = false; var panStartX = 0; var panStartY = 0; var panLastX = 0; var panLastY = 0; var mapOffsetX = 0; var mapOffsetY = 0; // Map expansion variables var mapBounds = { left: 0, right: 2048, top: 0, bottom: 2732 }; var mapExpansionDistance = 500; // How much to expand when character reaches edge // Speed button handler speedButton.down = function (x, y, obj) { // Play button scale animation tween(speedButton, { scaleX: 1.4, scaleY: 0.9 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(speedButton, { scaleX: 1.2, scaleY: 0.8 }, { duration: 120, easing: tween.easeOut }); } }); currentSpeedIndex = (currentSpeedIndex + 1) % gameSpeedOptions.length; gameSpeed = gameSpeedOptions[currentSpeedIndex]; speedText.setText('Speed: ' + gameSpeed + 'x'); // Note: Game speed functionality not available in LK engine }; // Restart button handler restartButton.down = function (x, y, obj) { // Play button scale animation tween(restartButton, { scaleX: 1.4, scaleY: 0.9 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(restartButton, { scaleX: 1.2, scaleY: 0.8 }, { duration: 120, easing: tween.easeOut }); } }); // Trigger game over state first, which will automatically reset the game LK.showGameOver(); }; // Tower placement handlers archerButton.down = function (x, y, obj) { // Play button scale animation tween(archerButton, { scaleX: 0.65, scaleY: 0.65 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(archerButton, { scaleX: 0.5, scaleY: 0.5 }, { duration: 120, easing: tween.easeOut }); } }); if (gold >= 50) { placingTowerType = 'archer'; placingTowerCost = 50; } }; cannonButton.down = function (x, y, obj) { // Play button scale animation tween(cannonButton, { scaleX: 0.65, scaleY: 0.65 }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { tween(cannonButton, { scaleX: 0.5, scaleY: 0.5 }, { duration: 120, easing: tween.easeOut }); } }); if (gold >= 80) { placingTowerType = 'cannon'; placingTowerCost = 80; } }; var placingTowerType = null; var placingTowerCost = 0; function spawnEnemy() { var enemyTypes = ['goblin', 'goblin', 'skeleton']; if (wave >= 3) enemyTypes.push('troll'); if (wave >= 5) enemyTypes.push('troll', 'troll'); var randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; var enemy = new Enemy(randomType); // Spawn from random edge using dynamic map boundaries var side = Math.floor(Math.random() * 4); if (side === 0) { // Top enemy.x = mapBounds.left + Math.random() * (mapBounds.right - mapBounds.left); enemy.y = mapBounds.top; } else if (side === 1) { // Right enemy.x = mapBounds.right; enemy.y = mapBounds.top + Math.random() * (mapBounds.bottom - mapBounds.top); } else if (side === 2) { // Bottom enemy.x = mapBounds.left + Math.random() * (mapBounds.right - mapBounds.left); enemy.y = mapBounds.bottom; } else { // Left enemy.x = mapBounds.left; enemy.y = mapBounds.top + Math.random() * (mapBounds.bottom - mapBounds.top); } game.addChild(enemy); enemies.push(enemy); enemiesSpawned++; } function expandMapIfNeeded() { var expanded = false; var edgeBuffer = 200; // Distance from edge to trigger expansion // Check if hero is near left edge if (hero.x - mapBounds.left < edgeBuffer) { mapBounds.left -= mapExpansionDistance; expanded = true; } // Check if hero is near right edge if (mapBounds.right - hero.x < edgeBuffer) { mapBounds.right += mapExpansionDistance; expanded = true; } // Check if hero is near top edge if (hero.y - mapBounds.top < edgeBuffer) { mapBounds.top -= mapExpansionDistance; expanded = true; } // Check if hero is near bottom edge if (mapBounds.bottom - hero.y < edgeBuffer) { mapBounds.bottom += mapExpansionDistance; expanded = true; } if (expanded) { // Visual feedback for map expansion LK.effects.flashScreen(0x4169e1, 300); } } function checkWaveComplete() { // Special handling for wave 5 to 6 transition - only advance when boss dies if (wave === 5) { // Wave 5 to 6 transition is handled in boss.die() method only return; } if (enemyKills >= 30) { // Start next wave after 30 kills wave++; // Increase all enemy HP by 10 after each wave difficultyScaling.healthMultiplier += 10 / 160; // Base goblin health is 160, so this adds roughly 10 HP enemiesInWave += 2; enemiesSpawned = 0; enemyKills = 0; // Reset kill counter for next wave killsText.setText('Enemies Killed: ' + enemyKills); bossActive = false; // Reset boss active flag for next wave // Re-enable hero shooting for wave 6 and beyond (after first boss death) if (wave >= 6) { heroCanShoot = true; } waveText.setText('Wave: ' + wave); if (wave > 100) { LK.showYouWin(); } } } game.down = function (x, y, obj) { // Check for middle mouse button to start panning if (obj.event && obj.event.button === 1) { isPanning = true; panStartX = x; panStartY = y; panLastX = x; panLastY = y; return; } if (placingTowerType) { // Check if position is valid (not too close to crystal tower or other towers) var tooClose = false; var dx = x - crystalTower.x; var dy = y - crystalTower.y; if (Math.sqrt(dx * dx + dy * dy) < 200) { tooClose = true; } for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var dx = x - tower.x; var dy = y - tower.y; if (Math.sqrt(dx * dx + dy * dy) < 120) { tooClose = true; break; } } if (!tooClose) { var newTower = new Tower(placingTowerType); newTower.x = x; newTower.y = y; game.addChild(newTower); towers.push(newTower); gold -= placingTowerCost; goldText.setText('Gold: ' + gold); LK.getSound('towerPlace').play(); } placingTowerType = null; placingTowerCost = 0; } else { // Check if clicking on hero to start drag - expanded hitbox 4x larger than original visual area var dx = x - hero.x; var dy = y - hero.y; // Hero asset is 125x156, expand hitbox to 4x the size (500x624 total area) var heroWidth = 125 * 2; // 4x area means 2x width/height var heroHeight = 156 * 2; if (Math.abs(dx) < heroWidth / 2 && Math.abs(dy) < heroHeight / 2) { draggedHero = true; } } }; game.move = function (x, y, obj) { if (isPanning) { // Calculate pan delta var deltaX = x - panLastX; var deltaY = y - panLastY; // Update map offset mapOffsetX += deltaX; mapOffsetY += deltaY; // Apply pan offset to game container game.x = mapOffsetX; game.y = mapOffsetY; // Update last position panLastX = x; panLastY = y; } else if (draggedHero) { hero.x = x - mapOffsetX; hero.y = y - mapOffsetY; } }; game.up = function (x, y, obj) { if (isPanning) { isPanning = false; } draggedHero = false; }; game.update = function () { // Determine boss spawn requirements based on wave var shouldSpawnBoss = false; var bossCount = 0; var shouldSpawnRegularEnemies = true; if (wave === 5 && !bossActive && enemiesSpawned === 0) { shouldSpawnBoss = true; bossCount = 1; shouldSpawnRegularEnemies = false; // Only boss on wave 5 } else if (wave === 10 && !bossActive && enemiesSpawned === 0) { shouldSpawnBoss = true; bossCount = 2; shouldSpawnRegularEnemies = false; // Only bosses on wave 10 } else if (wave === 15 && !bossActive && enemiesSpawned === 0) { shouldSpawnBoss = true; bossCount = 1; // One boss plus regular enemies shouldSpawnRegularEnemies = true; // Both bosses and regular enemies } else if (wave === 100 && !bossActive && enemiesSpawned === 0) { // Special wave 100: spawn main character as boss shouldSpawnBoss = true; bossCount = 1; shouldSpawnRegularEnemies = false; } if (shouldSpawnBoss) { for (var bossIndex = 0; bossIndex < bossCount; bossIndex++) { var boss; if (wave === 100) { // Create main character as enemy with 10,000 HP boss = new Boss(); boss.health = 10000; boss.maxHealth = 10000; boss.goldValue = 1000; // Make it look like the hero boss.removeChild(boss.attachedAssets[0]); // Remove boss graphics var heroGraphics = boss.attachAsset('hero', { anchorX: 0.5, anchorY: 1 }); heroGraphics.tint = 0x800000; // Dark red tint to show it's evil heroGraphics.scaleX = 2; // Make it larger heroGraphics.scaleY = 2; } else { boss = new Boss(); } // Spawn from random edge using dynamic map boundaries var side = Math.floor(Math.random() * 4); if (side === 0) { // Top boss.x = mapBounds.left + Math.random() * (mapBounds.right - mapBounds.left); boss.y = mapBounds.top; } else if (side === 1) { // Right boss.x = mapBounds.right; boss.y = mapBounds.top + Math.random() * (mapBounds.bottom - mapBounds.top); } else if (side === 2) { // Bottom boss.x = mapBounds.left + Math.random() * (mapBounds.right - mapBounds.left); boss.y = mapBounds.bottom; } else { // Left boss.x = mapBounds.left; boss.y = mapBounds.top + Math.random() * (mapBounds.bottom - mapBounds.top); } game.addChild(boss); enemies.push(boss); enemiesSpawned++; // Count boss as spawned enemy } bossActive = true; // Flash screen to indicate boss spawn LK.effects.flashScreen(0x800080, 1000); // Increase difficulty scaling after each boss round difficultyScaling.healthMultiplier += 0.15; // 15% health increase per round difficultyScaling.skeletonSpeedMultiplier += 0.08; // 8% speed increase for skeletons per round } // Spawn regular enemies continuously - always keep enemies on the field // Only spawn regular enemies if it's not a boss-only wave (5, 10) or if it's a mixed wave (15) var shouldSpawnRegularNow = true; if ((wave === 5 || wave === 10) && !bossActive) { shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves before boss spawns } else if ((wave === 5 || wave === 10) && bossActive) { shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves after boss spawns } if (shouldSpawnRegularNow && (enemiesSpawned < enemiesInWave || enemies.length < 3)) { enemySpawnTimer += gameSpeed; if (enemySpawnTimer >= 60) { // Spawn every 1 second for continuous pressure spawnEnemy(); enemySpawnTimer = 0; } } // Update health display healthText.setText('Tower HP: ' + crystalTower.health); // Update boss health bar var currentBoss = null; for (var i = 0; i < enemies.length; i++) { if (enemies[i].health === undefined || enemies[i].maxHealth === undefined) continue; if (enemies[i].maxHealth >= 800) { // Boss has 800+ max health currentBoss = enemies[i]; break; } } if (currentBoss && bossActive) { bossHealthBar.visible = true; bossHealthFill.visible = true; bossHealthText.visible = true; var healthPercent = currentBoss.health / currentBoss.maxHealth; bossHealthFill.scaleX = 4 * healthPercent; bossHealthText.setText('BOSS - ' + currentBoss.health + '/' + currentBoss.maxHealth); } else { bossHealthBar.visible = false; bossHealthFill.visible = false; bossHealthText.visible = false; } // Check for enemies in contact with crystal tower for continuous damage var currentEnemiesInContact = []; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - crystalTower.x; var dy = enemy.y - crystalTower.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { // Contact threshold currentEnemiesInContact.push(enemy); } } // Apply contact damage every second (60 ticks) contactDamageTimer += gameSpeed; if (contactDamageTimer >= 60 && currentEnemiesInContact.length > 0) { crystalTower.takeDamage(2 * currentEnemiesInContact.length); contactDamageTimer = 0; } // Update enemies in contact array enemiesInContact = currentEnemiesInContact; // Check for hero collecting coins for (var i = goldCoins.length - 1; i >= 0; i--) { var coin = goldCoins[i]; var dx = coin.x - hero.x; var dy = coin.y - hero.y; if (Math.sqrt(dx * dx + dy * dy) < 40) { coin.collect(); } } // Add random battlefield explosions during intense moments if (enemies.length > 8 && LK.ticks % Math.max(1, Math.floor(180 / gameSpeed)) === 0) { var explosion = new ExplosionEffect(); explosion.x = mapBounds.left + 200 + Math.random() * (mapBounds.right - mapBounds.left - 400); explosion.y = mapBounds.top + 200 + Math.random() * (mapBounds.bottom - mapBounds.top - 400); game.addChild(explosion); } // Add red alert glow when tower health is critical if (crystalTower.health < crystalTower.maxHealth * 0.2 && LK.ticks % Math.max(1, Math.floor(60 / gameSpeed)) === 0) { LK.effects.flashScreen(0x8b0000, 200); } // Check for map expansion based on hero position expandMapIfNeeded(); // Start background music on loop LK.playMusic('med'); // Hero can continue fighting even when collapsed - no game over from hero death // Update hero HP bar position to follow hero heroHealthBar.x = hero.x; heroHealthBar.y = hero.y + 40; // Position below hero // Update hero HP bar display var heroHealthPercent = Math.max(0, heroHealth) / heroMaxHealth; heroHealthFill.scaleX = 1.0 * heroHealthPercent; heroHealthText.setText(Math.max(0, heroHealth) + '/' + heroMaxHealth); // Change HP bar color based on health if (heroHealthPercent > 0.6) { heroHealthFill.tint = 0x00ff00; // Green } else if (heroHealthPercent > 0.3) { heroHealthFill.tint = 0xffff00; // Yellow } else { heroHealthFill.tint = 0xff0000; // Red } checkWaveComplete(); }; // Override LK.getSound to respect mute state var originalGetSound = LK.getSound; LK.getSound = function (soundId) { var sound = originalGetSound(soundId); var originalPlay = sound.play; sound.play = function () { if (!soundMuted) { originalPlay.call(sound); } }; return sound; };
===================================================================
--- original.js
+++ change.js
@@ -113,13 +113,16 @@
killsText.setText('Enemies Killed: ' + enemyKills);
// Continue wave progression after boss death and re-enable hero shooting
bossActive = false;
heroCanShoot = true;
- // Initialize post-boss enemy spawning for waves 5 and 10
- if (wave === 5 || wave === 10) {
- postBossEnemiesSpawned = 0;
- postBossGroupTimer = 0;
- postBossGroupsSpawned = 0;
+ // Special wave advancement for wave 5 to 6 transition
+ if (wave === 5) {
+ wave++;
+ enemiesInWave += 2;
+ enemiesSpawned = 0;
+ enemyKills = 0; // Reset kill counter for next wave
+ killsText.setText('Enemies Killed: ' + enemyKills);
+ waveText.setText('Wave: ' + wave);
}
// Remove boss from enemies array
for (var i = enemies.length - 1; i >= 0; i--) {
if (enemies[i] === self) {
@@ -996,11 +999,8 @@
var heroMaxHealth = 1000;
var enemyAttacks = 0;
var enemyKills = 0;
var bossActive = false;
-var postBossEnemiesSpawned;
-var postBossGroupTimer;
-var postBossGroupsSpawned;
var difficultyScaling = {
healthMultiplier: 1.0,
skeletonSpeedMultiplier: 1.0
};
@@ -1130,8 +1130,21 @@
fill: 0xFFFFFF
});
archerText.anchor.set(0.5, 0.5);
archerButton.addChild(archerText);
+var cannonButton = LK.getAsset('uiPanel', {
+ anchorX: 0.5,
+ anchorY: 0.5
+});
+cannonButton.x = 400;
+cannonButton.y = -150;
+LK.gui.bottom.addChild(cannonButton);
+var cannonText = new Text2('Cannon\n80g', {
+ size: 30,
+ fill: 0xFFFFFF
+});
+cannonText.anchor.set(0.5, 0.5);
+cannonButton.addChild(cannonText);
// Game speed button
var speedButton = LK.getAsset('uiPanel', {
anchorX: 0.5,
anchorY: 0.5
@@ -1250,8 +1263,31 @@
placingTowerType = 'archer';
placingTowerCost = 50;
}
};
+cannonButton.down = function (x, y, obj) {
+ // Play button scale animation
+ tween(cannonButton, {
+ scaleX: 0.65,
+ scaleY: 0.65
+ }, {
+ duration: 80,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(cannonButton, {
+ scaleX: 0.5,
+ scaleY: 0.5
+ }, {
+ duration: 120,
+ easing: tween.easeOut
+ });
+ }
+ });
+ if (gold >= 80) {
+ placingTowerType = 'cannon';
+ placingTowerCost = 80;
+ }
+};
var placingTowerType = null;
var placingTowerCost = 0;
function spawnEnemy() {
var enemyTypes = ['goblin', 'goblin', 'skeleton'];
@@ -1310,14 +1346,14 @@
LK.effects.flashScreen(0x4169e1, 300);
}
}
function checkWaveComplete() {
+ // Special handling for wave 5 to 6 transition - only advance when boss dies
+ if (wave === 5) {
+ // Wave 5 to 6 transition is handled in boss.die() method only
+ return;
+ }
if (enemyKills >= 30) {
- // Check if advancing to wave 6 - require all gold to be collected
- if (wave === 5 && goldCoins.length > 0) {
- // Don't advance to wave 6 until all gold is collected
- return;
- }
// Start next wave after 30 kills
wave++;
// Increase all enemy HP by 10 after each wave
difficultyScaling.healthMultiplier += 10 / 160; // Base goblin health is 160, so this adds roughly 10 HP
@@ -1325,12 +1361,8 @@
enemiesSpawned = 0;
enemyKills = 0; // Reset kill counter for next wave
killsText.setText('Enemies Killed: ' + enemyKills);
bossActive = false; // Reset boss active flag for next wave
- // Reset post-boss spawning variables for next wave
- postBossEnemiesSpawned = undefined;
- postBossGroupTimer = undefined;
- postBossGroupsSpawned = undefined;
// Re-enable hero shooting for wave 6 and beyond (after first boss death)
if (wave >= 6) {
heroCanShoot = true;
}
@@ -1483,12 +1515,8 @@
enemies.push(boss);
enemiesSpawned++; // Count boss as spawned enemy
}
bossActive = true;
- // Reset post-boss spawning variables when boss spawns
- postBossEnemiesSpawned = undefined;
- postBossGroupTimer = undefined;
- postBossGroupsSpawned = undefined;
// Flash screen to indicate boss spawn
LK.effects.flashScreen(0x800080, 1000);
// Increase difficulty scaling after each boss round
difficultyScaling.healthMultiplier += 0.15; // 15% health increase per round
@@ -1496,28 +1524,13 @@
}
// Spawn regular enemies continuously - always keep enemies on the field
// Only spawn regular enemies if it's not a boss-only wave (5, 10) or if it's a mixed wave (15)
var shouldSpawnRegularNow = true;
- if ((wave === 5 || wave === 10) && bossActive) {
- shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves while boss is active
- } else if ((wave === 5 || wave === 10) && !bossActive && typeof postBossEnemiesSpawned === 'undefined') {
+ if ((wave === 5 || wave === 10) && !bossActive) {
shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves before boss spawns
+ } else if ((wave === 5 || wave === 10) && bossActive) {
+ shouldSpawnRegularNow = false; // Don't spawn regular enemies on boss-only waves after boss spawns
}
- // Handle post-boss enemy spawning (30 enemies in groups of 5)
- if ((wave === 5 || wave === 10) && !bossActive && typeof postBossEnemiesSpawned !== 'undefined') {
- if (typeof postBossGroupTimer === 'undefined') postBossGroupTimer = 0;
- if (typeof postBossGroupsSpawned === 'undefined') postBossGroupsSpawned = 0;
- postBossGroupTimer += gameSpeed;
- if (postBossGroupTimer >= 120 && postBossGroupsSpawned < 6) {
- // Spawn every 2 seconds, 6 groups total
- // Spawn 5 enemies at once
- for (var groupIndex = 0; groupIndex < 5; groupIndex++) {
- spawnEnemy();
- }
- postBossGroupsSpawned++;
- postBossGroupTimer = 0;
- }
- }
if (shouldSpawnRegularNow && (enemiesSpawned < enemiesInWave || enemies.length < 3)) {
enemySpawnTimer += gameSpeed;
if (enemySpawnTimer >= 60) {
// Spawn every 1 second for continuous pressure
izometric cannon tower. In-Game asset. 2d. High contrast. No shadows. izometric
archerTower. In-Game asset. 2d. High contrast. No shadows
goblin. In-Game asset. 2d. High contrast. No shadows
goldCoin. In-Game asset. 2d. High contrast. No shadows
Archer hero. In-Game asset. 2d. High contrast. No shadows
skeleton. In-Game asset. 2d. High contrast. No shadows
troll. In-Game asset. 2d. High contrast. No shadows
single arrow image. In-Game asset. 2d. High contrast. No shadows
Create a flying dragon enemy with the following features:. In-Game asset. 2d. High contrast. No shadows
Here’s a prompt for a very distant, wide-angle view of a tree-free forest floor: **Prompt:** A very distant, wide-angle aerial view of a tree-free forest floor, showing expansive grassy plains with patches of dirt, scattered rocks, and low vegetation. The terrain stretches far into the horizon with subtle color variations and soft natural lighting, creating a vast, open, and serene natural environment without any trees.. In-Game asset. 2d. High contrast. No shadows
A stylized full-body illustration of a small hobbit holding a glowing ring in one hand, viewed from a 45-degree angle. The hobbit has curly hair, bare feet, and wears rustic, earth-toned clothing with detailed textures. The scene has warm, soft lighting emphasizing the character’s expressive face and the shining ring. The art style is cartoonish with rich colors, smooth shading, and a fantasy atmosphere.. In-Game asset. 2d. High contrast. No shadows
A magical yellow light glowing softly, with radiant beams and sparkling particles floating around. The light has a warm, enchanting aura that illuminates its surroundings with a golden hue. The atmosphere feels mystical and inviting, perfect for fantasy scenes or magical effects.. In-Game asset. 2d. High contrast. No shadows
A full-body stylized illustration of Smeagol (Gollum), showing his thin, hunched frame and large expressive eyes. He is barefoot and shirtless, wearing ragged shorts, with exaggerated cartoonish features that highlight his creepy yet pitiful nature. He clutches a glowing precious ring tightly in one hand. The art style is dark fantasy with vibrant colors, detailed skin textures, and a shadowy, mysterious background to enhance the eerie atmosphere. Perfect for full-character concept art or game design.. In-Game asset. 2d. High contrast. No shadows
A full-body stylized illustration of an orc warrior, standing in a dynamic pose. The orc has green or grayish skin, muscular build, tusks, and tribal armor made of bone, leather, and metal. The style is fantasy-themed with bold lines, exaggerated proportions, and detailed textures. The lighting is dramatic, emphasizing the orc’s strength and menace. Background is minimal or softly blurred to keep focus on the character. Suitable for fantasy RPG game concept art.. In-Game asset. 2d. High contrast. No shadows
A stylized fantasy axe with a broad, curved blade and intricate engravings. The handle is wrapped in worn leather, and the metal has a slightly weathered look, giving it a battle-worn feel. The design is bold and exaggerated, suitable for an orc warrior, with a glowing rune etched into the blade. The style is high-fantasy with clean lines, vibrant highlights, and a dramatic shadow for depth. Perfect for 2D game assets or concept art.. In-Game asset. 2d. High contrast. No shadows