User prompt
thats on top of gold now, put the wave at the bottom instead
User prompt
move the wave text to inside the right bar
User prompt
Please fix the bug: 'LK.getMusic is not a function' in or related to this line: 'if (waveStartText.alpha <= 0) {' Line Number: 563
User prompt
Please fix the bug: 'LK.getMusic is not a function' in or related to this line: 'LK.getMusic('background').play();' Line Number: 528
User prompt
Please fix the bug: 'self.createUpgradeButton is not a function' in or related to this line: 'var buttons = {' Line Number: 330
User prompt
Please fix the bug: 'LK.Sprite is not a constructor' in or related to this line: 'background = new LK.Sprite('background');' Line Number: 573
User prompt
Please fix the bug: 'LK.getMusic is not a function' in or related to this line: 'LK.getMusic('background').play();' Line Number: 571
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'LK.getMusic is not a function' in or related to this line: 'if (waveStartText.alpha <= 0) {' Line Number: 560
Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'bonusText is not defined' in or related to this line: 'if (bonusText) {' Line Number: 548
User prompt
Please fix the bug: 'waveCompleteText is not defined' in or related to this line: 'if (waveCompleteText) {' Line Number: 544
User prompt
the wave completed text from last round should get removed when have starts
User prompt
the wave completed text should get removed after the wave end
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'var dx = tower.x - self.x;' Line Number: 449
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < zombies.length; i++) {' Line Number: 128
Code edit (1 edits merged)
Please save this source code
Initial prompt
Horde Defence
/**** * Classes ****/ var Bullet = Container.expand(function (damage) { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.damage = damage || 10; self.lifetime = 60; // Bullets disappear after 1 second self.update = function () { // Move bullet self.x += self.vx; self.y += self.vy; // Decrease lifetime self.lifetime--; return self.lifetime <= 0; }; return self; }); var Explosion = Container.expand(function () { var self = Container.call(this); var explosionGraphics = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 15; self.scale.set(0.2); self.update = function () { self.lifetime--; self.scale.set(1 - self.lifetime / 20); self.alpha = self.lifetime / 15; return self.lifetime <= 0; }; return self; }); var ManualTarget = Container.expand(function () { var self = Container.call(this); var targetGraphics = self.attachAsset('manualTarget', { anchorX: 0.5, anchorY: 0.5 }); self.lifetime = 15; self.alpha = 0.5; self.update = function () { self.lifetime--; self.alpha = self.lifetime / 15; self.rotation += 0.1; return self.lifetime <= 0; }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); // Tower base (doesn't rotate) var base = self.attachAsset('towerBase', { anchorX: 0.5, anchorY: 0.5 }); // Tower turret (rotates to aim) var turret = new Container(); var turretGraphics = turret.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(turret); // Tower stats self.health = 100; self.maxHealth = 100; self.damage = 10; self.fireRate = 60; // frames between shots self.fireTimer = 0; self.range = 800; self.upgradeLevel = { damage: 1, fireRate: 1, range: 1, health: 1 }; // Health bar var healthBarContainer = new Container(); var healthBarBg = healthBarContainer.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, width: 200 }); var healthBarFill = new Container(); var healthBarGraphics = healthBarFill.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5, width: 200 }); healthBarContainer.addChild(healthBarFill); healthBarContainer.y = -150; self.addChild(healthBarContainer); // Methods self.update = function (zombies) { // Decrease fire timer if (self.fireTimer > 0) { self.fireTimer--; } // Update health bar healthBarFill.scale.x = self.health / self.maxHealth; // Find target if none currently var target = null; var closestDist = self.range; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var dx = zombie.x - self.x; var dy = zombie.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < closestDist) { closestDist = dist; target = zombie; } } // Aim at target if (target) { var angle = Math.atan2(target.y - self.y, target.x - self.x); turret.rotation = angle; // Fire if ready if (self.fireTimer <= 0) { var bullet = new Bullet(self.damage); bullet.x = self.x; bullet.y = self.y; bullet.rotation = angle; bullet.vx = Math.cos(angle) * 15; bullet.vy = Math.sin(angle) * 15; game.addChild(bullet); bullets.push(bullet); self.fireTimer = self.fireRate; LK.getSound('shoot').play(); } } }; self.manualShoot = function (x, y) { var angle = Math.atan2(y - self.y, x - self.x); turret.rotation = angle; var bullet = new Bullet(self.damage * 2); // Manual shots do double damage bullet.x = self.x; bullet.y = self.y; bullet.rotation = angle; bullet.vx = Math.cos(angle) * 15; bullet.vy = Math.sin(angle) * 15; game.addChild(bullet); bullets.push(bullet); LK.getSound('shoot').play(); }; self.takeDamage = function (amount) { self.health -= amount; // Flash the tower when hit LK.effects.flashObject(self, 0xff0000, 300); LK.getSound('towerHit').play(); if (self.health <= 0) { return true; // Tower destroyed } return false; }; self.upgradeDamage = function () { self.upgradeLevel.damage++; self.damage += 5; LK.getSound('upgrade').play(); }; self.upgradeFireRate = function () { self.upgradeLevel.fireRate++; self.fireRate = Math.max(10, self.fireRate - 10); LK.getSound('upgrade').play(); }; self.upgradeRange = function () { self.upgradeLevel.range++; self.range += 100; LK.getSound('upgrade').play(); }; self.upgradeHealth = function () { self.upgradeLevel.health++; var oldMaxHealth = self.maxHealth; self.maxHealth += 50; self.health += self.maxHealth - oldMaxHealth; LK.getSound('upgrade').play(); }; return self; }); var UpgradePanel = Container.expand(function (tower) { var self = Container.call(this); // Panel background var panel = self.attachAsset('upgradePanel', { anchorX: 0, anchorY: 0 }); // Title var title = new Text2("UPGRADES", { size: 60, fill: 0xffffff }); title.x = 250; title.y = 100; title.anchor.set(0.5); self.addChild(title); // Gold display var goldIcon = new Container(); var goldIconGraphics = goldIcon.attachAsset('goldIcon', { anchorX: 0.5, anchorY: 0.5 }); goldIcon.x = 100; goldIcon.y = 200; self.addChild(goldIcon); var goldText = new Text2("0", { size: 50, fill: 0xffcc00 }); goldText.x = 180; goldText.y = 200; goldText.anchor.set(0, 0.5); self.addChild(goldText); // Upgrade buttons var upgrades = [{ name: "Damage", action: "upgradeDamage", cost: 50, desc: "+5 damage per shot" }, { name: "Fire Rate", action: "upgradeFireRate", cost: 75, desc: "Shoot faster" }, { name: "Range", action: "upgradeRange", cost: 100, desc: "+100 targeting range" }, { name: "Health", action: "upgradeHealth", cost: 150, desc: "+50 max health" }]; var buttons = []; for (var i = 0; i < upgrades.length; i++) { var y = 300 + i * 150; var button = new Container(); var buttonGraphics = button.attachAsset('upgradeButton', { anchorX: 0.5, anchorY: 0.5 }); button.x = 250; button.y = y; var nameText = new Text2(upgrades[i].name, { size: 40, fill: 0xffffff }); nameText.x = 0; nameText.y = -30; nameText.anchor.set(0.5); button.addChild(nameText); var costText = new Text2(upgrades[i].cost + " gold", { size: 30, fill: 0xffcc00 }); costText.x = 0; costText.y = 0; costText.anchor.set(0.5); button.addChild(costText); var descText = new Text2(upgrades[i].desc, { size: 25, fill: 0xcccccc }); descText.x = 0; descText.y = 30; descText.anchor.set(0.5); button.addChild(descText); button.upgrade = upgrades[i]; buttons.push(button); self.addChild(button); } // Wave info var waveInfo = new Container(); var waveInfoGraphics = waveInfo.attachAsset('waveIndicator', { anchorX: 0.5, anchorY: 0.5 }); waveInfo.x = 250; waveInfo.y = 1000; self.addChild(waveInfo); var waveText = new Text2("WAVE 1", { size: 40, fill: 0xffffff }); waveText.x = 0; waveText.y = 0; waveText.anchor.set(0.5); waveInfo.addChild(waveText); // Stats display var statsTitle = new Text2("TOWER STATS", { size: 50, fill: 0xffffff }); statsTitle.x = 250; statsTitle.y = 1200; statsTitle.anchor.set(0.5); self.addChild(statsTitle); var stats = [{ name: "Damage", getValue: function getValue() { return tower.damage; } }, { name: "Fire Rate", getValue: function getValue() { return (60 / tower.fireRate).toFixed(1) + "/sec"; } }, { name: "Range", getValue: function getValue() { return tower.range; } }, { name: "Health", getValue: function getValue() { return tower.health + "/" + tower.maxHealth; } }]; var statTexts = []; for (var j = 0; j < stats.length; j++) { var statY = 1300 + j * 70; var statName = new Text2(stats[j].name + ":", { size: 35, fill: 0xcccccc }); statName.x = 100; statName.y = statY; statName.anchor.set(0, 0.5); self.addChild(statName); var statValue = new Text2("0", { size: 35, fill: 0xffffff }); statValue.x = 400; statValue.y = statY; statValue.anchor.set(1, 0.5); statValue.getValue = stats[j].getValue; statTexts.push(statValue); self.addChild(statValue); } // Methods self.updateGold = function (amount) { goldText.setText(amount.toString()); // Update button states based on available gold for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; // Make buttons grey if can't afford if (button.upgrade.cost > amount) { button.getChildAt(0).tint = 0x888888; } else { button.getChildAt(0).tint = 0xffffff; } } }; self.updateWave = function (wave) { waveText.setText("WAVE " + wave); }; self.update = function () { // Update stat displays for (var i = 0; i < statTexts.length; i++) { statTexts[i].setText(statTexts[i].getValue()); } }; self.checkUpgradeClick = function (x, y, gold) { for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; var dx = x - (button.x + self.x); var dy = y - (button.y + self.y); var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 200) { // Check if can afford if (gold >= button.upgrade.cost) { tower[button.upgrade.action](); return button.upgrade.cost; } } } return 0; }; return self; }); var Zombie = Container.expand(function (level) { var self = Container.call(this); // Scale difficulty with level level = level || 1; // Zombie graphics var zombieGraphics = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); // Randomize color slightly var green = 0x00ff00; var colorVariation = Math.floor(Math.random() * 0x303030); zombieGraphics.tint = green - colorVariation; // Health bar var healthBarContainer = new Container(); var healthBarBg = healthBarContainer.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5 }); var healthBarFill = new Container(); var healthBarGraphics = healthBarFill.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); healthBarContainer.addChild(healthBarFill); healthBarContainer.y = -50; self.addChild(healthBarContainer); // Zombie stats self.health = 20 + level * 5; self.maxHealth = self.health; self.damage = 5 + level; self.speed = 1 + level * 0.1; self.value = 5 + level; // Gold value // Update self.update = function (tower) { // Move towards tower var dx = tower.x - self.x; var dy = tower.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); // Normalize and apply speed if (dist > 0) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; // Rotate zombie to face movement direction self.rotation = Math.atan2(dy, dx); } // Update health bar healthBarFill.scale.x = self.health / self.maxHealth; // Attack tower if close enough if (dist < 150) { if (tower.takeDamage(self.damage / 60)) { // Damage per second return "gameover"; } } return false; }; self.takeDamage = function (amount) { self.health -= amount; // Flash when hit LK.effects.flashObject(self, 0xffffff, 100); if (self.health <= 0) { return true; // Zombie destroyed } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x001100 }); /**** * Game Code ****/ // Constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var CENTER_X = GAME_WIDTH / 2; var CENTER_Y = GAME_HEIGHT / 2; // Game variables var tower; var upgradePanel; var bullets = []; var zombies = []; var explosions = []; var targets = []; var gold = 100; var wave = 1; var zombiesKilled = 0; var waveSize = 10; var zombiesInWave = 0; var waveInterval = 0; var isGameActive = false; var manualCooldown = 0; function startGame() { // Play background music LK.playMusic('background', { loop: true }); // Create background var background = game.attachAsset('background', { anchorX: 0.5, anchorY: 0.5 }); background.x = CENTER_X; background.y = CENTER_Y; // Create tower tower = new Tower(); tower.x = CENTER_X; tower.y = CENTER_Y; game.addChild(tower); // Create upgrade panel upgradePanel = new UpgradePanel(tower); upgradePanel.x = GAME_WIDTH - 500; upgradePanel.y = 0; game.addChild(upgradePanel); // Initialize UI upgradePanel.updateGold(gold); upgradePanel.updateWave(wave); // Start first wave startWave(); isGameActive = true; } function startWave() { waveSize = 10 + wave * 5; zombiesInWave = waveSize; waveInterval = Math.max(30, 120 - wave * 5); // Spawn interval decreases with each wave upgradePanel.updateWave(wave); LK.getSound('waveStart').play(); // Flash wave indicator var waveText = new Text2("WAVE " + wave + " INCOMING!", { size: 80, fill: 0xff0000 }); waveText.x = CENTER_X; waveText.y = 200; waveText.anchor.set(0.5); game.addChild(waveText); // Fade out wave text var fadeInterval = LK.setInterval(function () { waveText.alpha -= 0.02; if (waveText.alpha <= 0) { waveText.destroy(); LK.clearInterval(fadeInterval); } }, 30); } function spawnZombie() { if (zombiesInWave <= 0) { return; } var zombie = new Zombie(wave); // Spawn at random edge position var angle = Math.random() * Math.PI * 2; var distance = 1200; // Spawn outside visible area zombie.x = CENTER_X + Math.cos(angle) * distance; zombie.y = CENTER_Y + Math.sin(angle) * distance; game.addChild(zombie); zombies.push(zombie); zombiesInWave--; } function gameOver() { isGameActive = false; // Play game over sound LK.getSound('gameOver').play(); // Display message var gameOverText = new Text2("GAME OVER!", { size: 120, fill: 0xff0000 }); gameOverText.x = CENTER_X; gameOverText.y = CENTER_Y; gameOverText.anchor.set(0.5); game.addChild(gameOverText); var scoreText = new Text2("Waves Survived: " + wave + "\nZombies Killed: " + zombiesKilled, { size: 60, fill: 0xffffff }); scoreText.x = CENTER_X; scoreText.y = CENTER_Y + 150; scoreText.anchor.set(0.5); game.addChild(scoreText); // Set final score (waves survived * zombies killed) LK.setScore(wave * zombiesKilled); // Show game over screen after delay LK.setTimeout(function () { LK.showGameOver(); }, 3000); } /**** * Event Handlers ****/ game.on('down', function (x, y, obj) { if (!isGameActive) { return; } var event = obj; var pos = game.toLocal(event.global); // Check if clicking on upgrade panel if (pos.x > GAME_WIDTH - 500) { var upgradeCost = upgradePanel.checkUpgradeClick(pos.x, pos.y, gold); if (upgradeCost > 0) { gold -= upgradeCost; upgradePanel.updateGold(gold); } } else { // Manual targeting - can only fire once per second if (manualCooldown <= 0) { // Create target indicator var target = new ManualTarget(); target.x = pos.x; target.y = pos.y; game.addChild(target); targets.push(target); // Fire at clicked location tower.manualShoot(pos.x, pos.y); manualCooldown = 60; // 1 second cooldown } } }); /**** * Game Loop ****/ LK.on('tick', function () { if (!isGameActive) { return; } // Update tower tower.update(zombies); // Update upgrade panel upgradePanel.update(); // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { if (bullets[i].update()) { bullets[i].destroy(); bullets.splice(i, 1); continue; } // Check for zombie hits for (var j = zombies.length - 1; j >= 0; j--) { if (bullets[i] && bullets[i].intersects(zombies[j])) { if (zombies[j].takeDamage(bullets[i].damage)) { // Zombie killed var explosion = new Explosion(); explosion.x = zombies[j].x; explosion.y = zombies[j].y; game.addChild(explosion); explosions.push(explosion); // Add gold gold += zombies[j].value; upgradePanel.updateGold(gold); // Show gold pop-up var goldPop = new Text2("+" + zombies[j].value, { size: 30, fill: 0xffcc00 }); goldPop.x = zombies[j].x; goldPop.y = zombies[j].y - 50; goldPop.anchor.set(0.5); game.addChild(goldPop); // Fade out gold text var fadeInterval = LK.setInterval(function () { goldPop.alpha -= 0.05; goldPop.y -= 1; if (goldPop.alpha <= 0) { goldPop.destroy(); LK.clearInterval(fadeInterval); } }, 30); LK.getSound('zombieDeath').play(); zombies[j].destroy(); zombies.splice(j, 1); zombiesKilled++; } // Remove bullet bullets[i].destroy(); bullets.splice(i, 1); break; } } } // Update zombies for (var k = zombies.length - 1; k >= 0; k--) { var result = zombies[k].update(tower); if (result === "gameover") { gameOver(); return; } } // Update explosions for (var l = explosions.length - 1; l >= 0; l--) { if (explosions[l].update()) { explosions[l].destroy(); explosions.splice(l, 1); } } // Update targets for (var m = targets.length - 1; m >= 0; m--) { if (targets[m].update()) { targets[m].destroy(); targets.splice(m, 1); } } // Spawn zombies if (LK.ticks % waveInterval === 0 && zombiesInWave > 0) { spawnZombie(); } // Check if wave is complete if (zombiesInWave <= 0 && zombies.length === 0) { // Start next wave after delay wave++; LK.setTimeout(function () { startWave(); }, 3000); // Grant bonus gold between waves var bonus = 50 + wave * 10; gold += bonus; upgradePanel.updateGold(gold); // Show wave complete message var waveCompleteText = new Text2("WAVE " + (wave - 1) + " COMPLETE!", { size: 80, fill: 0x33ff33 }); waveCompleteText.x = CENTER_X; waveCompleteText.y = CENTER_Y; waveCompleteText.anchor.set(0.5); game.addChild(waveCompleteText); var bonusText = new Text2("BONUS: +" + bonus + " GOLD", { size: 60, fill: 0xffcc00 }); bonusText.x = CENTER_X; bonusText.y = CENTER_Y + 100; bonusText.anchor.set(0.5); game.addChild(bonusText); // Fade out text var fadeInterval = LK.setInterval(function () { waveCompleteText.alpha -= 0.02; bonusText.alpha -= 0.02; if (waveCompleteText.alpha <= 0) { waveCompleteText.destroy(); bonusText.destroy(); LK.clearInterval(fadeInterval); } }, 30); } // Update manual cooldown if (manualCooldown > 0) { manualCooldown--; } }); // Start the game startGame();
===================================================================
--- original.js
+++ change.js
@@ -1,119 +1,746 @@
-/****
+/****
* Classes
-****/
-// Bullet class
-var Bullet = Container.expand(function () {
- var self = Container.call(this);
- var bulletGraphics = self.attachAsset('bullet', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.speed = -10;
- self.update = function () {
- self.y += self.speed;
- if (self.y < -50) {
- self.destroy();
- }
- };
+****/
+var Bullet = Container.expand(function (damage) {
+ var self = Container.call(this);
+ var bulletGraphics = self.attachAsset('bullet', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.vx = 0;
+ self.vy = 0;
+ self.damage = damage || 10;
+ self.lifetime = 60; // Bullets disappear after 1 second
+ self.update = function () {
+ // Move bullet
+ self.x += self.vx;
+ self.y += self.vy;
+ // Decrease lifetime
+ self.lifetime--;
+ return self.lifetime <= 0;
+ };
+ return self;
});
-//<Assets used in the game will automatically appear here>
-//<Write imports for supported plugins here>
-// Tower class
+var Explosion = Container.expand(function () {
+ var self = Container.call(this);
+ var explosionGraphics = self.attachAsset('explosion', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.lifetime = 15;
+ self.scale.set(0.2);
+ self.update = function () {
+ self.lifetime--;
+ self.scale.set(1 - self.lifetime / 20);
+ self.alpha = self.lifetime / 15;
+ return self.lifetime <= 0;
+ };
+ return self;
+});
+var ManualTarget = Container.expand(function () {
+ var self = Container.call(this);
+ var targetGraphics = self.attachAsset('manualTarget', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.lifetime = 15;
+ self.alpha = 0.5;
+ self.update = function () {
+ self.lifetime--;
+ self.alpha = self.lifetime / 15;
+ self.rotation += 0.1;
+ return self.lifetime <= 0;
+ };
+ return self;
+});
var Tower = Container.expand(function () {
- var self = Container.call(this);
- var towerGraphics = self.attachAsset('tower', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.health = 100;
- self.upgradeLevel = 1;
- self.upgrade = function () {
- if (self.upgradeLevel < 5) {
- self.upgradeLevel++;
- // Increase tower capabilities
- }
- };
- self.takeDamage = function (damage) {
- self.health -= damage;
- if (self.health <= 0) {
- LK.showGameOver();
- }
- };
+ var self = Container.call(this);
+ // Tower base (doesn't rotate)
+ var base = self.attachAsset('towerBase', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Tower turret (rotates to aim)
+ var turret = new Container();
+ var turretGraphics = turret.attachAsset('tower', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.addChild(turret);
+ // Tower stats
+ self.health = 100;
+ self.maxHealth = 100;
+ self.damage = 10;
+ self.fireRate = 60; // frames between shots
+ self.fireTimer = 0;
+ self.range = 800;
+ self.upgradeLevel = {
+ damage: 1,
+ fireRate: 1,
+ range: 1,
+ health: 1
+ };
+ // Health bar
+ var healthBarContainer = new Container();
+ var healthBarBg = healthBarContainer.attachAsset('healthBarBg', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 200
+ });
+ var healthBarFill = new Container();
+ var healthBarGraphics = healthBarFill.attachAsset('healthBar', {
+ anchorX: 0,
+ anchorY: 0.5,
+ width: 200
+ });
+ healthBarContainer.addChild(healthBarFill);
+ healthBarContainer.y = -150;
+ self.addChild(healthBarContainer);
+ // Methods
+ self.update = function (zombies) {
+ // Decrease fire timer
+ if (self.fireTimer > 0) {
+ self.fireTimer--;
+ }
+ // Update health bar
+ healthBarFill.scale.x = self.health / self.maxHealth;
+ // Find target if none currently
+ var target = null;
+ var closestDist = self.range;
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ var dx = zombie.x - self.x;
+ var dy = zombie.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < closestDist) {
+ closestDist = dist;
+ target = zombie;
+ }
+ }
+ // Aim at target
+ if (target) {
+ var angle = Math.atan2(target.y - self.y, target.x - self.x);
+ turret.rotation = angle;
+ // Fire if ready
+ if (self.fireTimer <= 0) {
+ var bullet = new Bullet(self.damage);
+ bullet.x = self.x;
+ bullet.y = self.y;
+ bullet.rotation = angle;
+ bullet.vx = Math.cos(angle) * 15;
+ bullet.vy = Math.sin(angle) * 15;
+ game.addChild(bullet);
+ bullets.push(bullet);
+ self.fireTimer = self.fireRate;
+ LK.getSound('shoot').play();
+ }
+ }
+ };
+ self.manualShoot = function (x, y) {
+ var angle = Math.atan2(y - self.y, x - self.x);
+ turret.rotation = angle;
+ var bullet = new Bullet(self.damage * 2); // Manual shots do double damage
+ bullet.x = self.x;
+ bullet.y = self.y;
+ bullet.rotation = angle;
+ bullet.vx = Math.cos(angle) * 15;
+ bullet.vy = Math.sin(angle) * 15;
+ game.addChild(bullet);
+ bullets.push(bullet);
+ LK.getSound('shoot').play();
+ };
+ self.takeDamage = function (amount) {
+ self.health -= amount;
+ // Flash the tower when hit
+ LK.effects.flashObject(self, 0xff0000, 300);
+ LK.getSound('towerHit').play();
+ if (self.health <= 0) {
+ return true; // Tower destroyed
+ }
+ return false;
+ };
+ self.upgradeDamage = function () {
+ self.upgradeLevel.damage++;
+ self.damage += 5;
+ LK.getSound('upgrade').play();
+ };
+ self.upgradeFireRate = function () {
+ self.upgradeLevel.fireRate++;
+ self.fireRate = Math.max(10, self.fireRate - 10);
+ LK.getSound('upgrade').play();
+ };
+ self.upgradeRange = function () {
+ self.upgradeLevel.range++;
+ self.range += 100;
+ LK.getSound('upgrade').play();
+ };
+ self.upgradeHealth = function () {
+ self.upgradeLevel.health++;
+ var oldMaxHealth = self.maxHealth;
+ self.maxHealth += 50;
+ self.health += self.maxHealth - oldMaxHealth;
+ LK.getSound('upgrade').play();
+ };
+ return self;
});
-// Zombie class
-var Zombie = Container.expand(function () {
- var self = Container.call(this);
- var zombieGraphics = self.attachAsset('zombie', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- self.speed = 2;
- self.health = 20;
- self.update = function () {
- self.y += self.speed;
- if (self.y > 2732) {
- tower.takeDamage(10);
- self.destroy();
- }
- };
- self.takeDamage = function (damage) {
- self.health -= damage;
- if (self.health <= 0) {
- self.destroy();
- }
- };
+var UpgradePanel = Container.expand(function (tower) {
+ var self = Container.call(this);
+ // Panel background
+ var panel = self.attachAsset('upgradePanel', {
+ anchorX: 0,
+ anchorY: 0
+ });
+ // Title
+ var title = new Text2("UPGRADES", {
+ size: 60,
+ fill: 0xffffff
+ });
+ title.x = 250;
+ title.y = 100;
+ title.anchor.set(0.5);
+ self.addChild(title);
+ // Gold display
+ var goldIcon = new Container();
+ var goldIconGraphics = goldIcon.attachAsset('goldIcon', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ goldIcon.x = 100;
+ goldIcon.y = 200;
+ self.addChild(goldIcon);
+ var goldText = new Text2("0", {
+ size: 50,
+ fill: 0xffcc00
+ });
+ goldText.x = 180;
+ goldText.y = 200;
+ goldText.anchor.set(0, 0.5);
+ self.addChild(goldText);
+ // Upgrade buttons
+ var upgrades = [{
+ name: "Damage",
+ action: "upgradeDamage",
+ cost: 50,
+ desc: "+5 damage per shot"
+ }, {
+ name: "Fire Rate",
+ action: "upgradeFireRate",
+ cost: 75,
+ desc: "Shoot faster"
+ }, {
+ name: "Range",
+ action: "upgradeRange",
+ cost: 100,
+ desc: "+100 targeting range"
+ }, {
+ name: "Health",
+ action: "upgradeHealth",
+ cost: 150,
+ desc: "+50 max health"
+ }];
+ var buttons = [];
+ for (var i = 0; i < upgrades.length; i++) {
+ var y = 300 + i * 150;
+ var button = new Container();
+ var buttonGraphics = button.attachAsset('upgradeButton', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ button.x = 250;
+ button.y = y;
+ var nameText = new Text2(upgrades[i].name, {
+ size: 40,
+ fill: 0xffffff
+ });
+ nameText.x = 0;
+ nameText.y = -30;
+ nameText.anchor.set(0.5);
+ button.addChild(nameText);
+ var costText = new Text2(upgrades[i].cost + " gold", {
+ size: 30,
+ fill: 0xffcc00
+ });
+ costText.x = 0;
+ costText.y = 0;
+ costText.anchor.set(0.5);
+ button.addChild(costText);
+ var descText = new Text2(upgrades[i].desc, {
+ size: 25,
+ fill: 0xcccccc
+ });
+ descText.x = 0;
+ descText.y = 30;
+ descText.anchor.set(0.5);
+ button.addChild(descText);
+ button.upgrade = upgrades[i];
+ buttons.push(button);
+ self.addChild(button);
+ }
+ // Wave info
+ var waveInfo = new Container();
+ var waveInfoGraphics = waveInfo.attachAsset('waveIndicator', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ waveInfo.x = 250;
+ waveInfo.y = 1000;
+ self.addChild(waveInfo);
+ var waveText = new Text2("WAVE 1", {
+ size: 40,
+ fill: 0xffffff
+ });
+ waveText.x = 0;
+ waveText.y = 0;
+ waveText.anchor.set(0.5);
+ waveInfo.addChild(waveText);
+ // Stats display
+ var statsTitle = new Text2("TOWER STATS", {
+ size: 50,
+ fill: 0xffffff
+ });
+ statsTitle.x = 250;
+ statsTitle.y = 1200;
+ statsTitle.anchor.set(0.5);
+ self.addChild(statsTitle);
+ var stats = [{
+ name: "Damage",
+ getValue: function getValue() {
+ return tower.damage;
+ }
+ }, {
+ name: "Fire Rate",
+ getValue: function getValue() {
+ return (60 / tower.fireRate).toFixed(1) + "/sec";
+ }
+ }, {
+ name: "Range",
+ getValue: function getValue() {
+ return tower.range;
+ }
+ }, {
+ name: "Health",
+ getValue: function getValue() {
+ return tower.health + "/" + tower.maxHealth;
+ }
+ }];
+ var statTexts = [];
+ for (var j = 0; j < stats.length; j++) {
+ var statY = 1300 + j * 70;
+ var statName = new Text2(stats[j].name + ":", {
+ size: 35,
+ fill: 0xcccccc
+ });
+ statName.x = 100;
+ statName.y = statY;
+ statName.anchor.set(0, 0.5);
+ self.addChild(statName);
+ var statValue = new Text2("0", {
+ size: 35,
+ fill: 0xffffff
+ });
+ statValue.x = 400;
+ statValue.y = statY;
+ statValue.anchor.set(1, 0.5);
+ statValue.getValue = stats[j].getValue;
+ statTexts.push(statValue);
+ self.addChild(statValue);
+ }
+ // Methods
+ self.updateGold = function (amount) {
+ goldText.setText(amount.toString());
+ // Update button states based on available gold
+ for (var i = 0; i < buttons.length; i++) {
+ var button = buttons[i];
+ // Make buttons grey if can't afford
+ if (button.upgrade.cost > amount) {
+ button.getChildAt(0).tint = 0x888888;
+ } else {
+ button.getChildAt(0).tint = 0xffffff;
+ }
+ }
+ };
+ self.updateWave = function (wave) {
+ waveText.setText("WAVE " + wave);
+ };
+ self.update = function () {
+ // Update stat displays
+ for (var i = 0; i < statTexts.length; i++) {
+ statTexts[i].setText(statTexts[i].getValue());
+ }
+ };
+ self.checkUpgradeClick = function (x, y, gold) {
+ for (var i = 0; i < buttons.length; i++) {
+ var button = buttons[i];
+ var dx = x - (button.x + self.x);
+ var dy = y - (button.y + self.y);
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 200) {
+ // Check if can afford
+ if (gold >= button.upgrade.cost) {
+ tower[button.upgrade.action]();
+ return button.upgrade.cost;
+ }
+ }
+ }
+ return 0;
+ };
+ return self;
});
+var Zombie = Container.expand(function (level) {
+ var self = Container.call(this);
+ // Scale difficulty with level
+ level = level || 1;
+ // Zombie graphics
+ var zombieGraphics = self.attachAsset('zombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Randomize color slightly
+ var green = 0x00ff00;
+ var colorVariation = Math.floor(Math.random() * 0x303030);
+ zombieGraphics.tint = green - colorVariation;
+ // Health bar
+ var healthBarContainer = new Container();
+ var healthBarBg = healthBarContainer.attachAsset('healthBarBg', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ var healthBarFill = new Container();
+ var healthBarGraphics = healthBarFill.attachAsset('healthBar', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ healthBarContainer.addChild(healthBarFill);
+ healthBarContainer.y = -50;
+ self.addChild(healthBarContainer);
+ // Zombie stats
+ self.health = 20 + level * 5;
+ self.maxHealth = self.health;
+ self.damage = 5 + level;
+ self.speed = 1 + level * 0.1;
+ self.value = 5 + level; // Gold value
+ // Update
+ self.update = function (tower) {
+ // Move towards tower
+ var dx = tower.x - self.x;
+ var dy = tower.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ // Normalize and apply speed
+ if (dist > 0) {
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ // Rotate zombie to face movement direction
+ self.rotation = Math.atan2(dy, dx);
+ }
+ // Update health bar
+ healthBarFill.scale.x = self.health / self.maxHealth;
+ // Attack tower if close enough
+ if (dist < 150) {
+ if (tower.takeDamage(self.damage / 60)) {
+ // Damage per second
+ return "gameover";
+ }
+ }
+ return false;
+ };
+ self.takeDamage = function (amount) {
+ self.health -= amount;
+ // Flash when hit
+ LK.effects.flashObject(self, 0xffffff, 100);
+ if (self.health <= 0) {
+ return true; // Zombie destroyed
+ }
+ return false;
+ };
+ return self;
+});
-/****
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000 //Init game with black background
+ backgroundColor: 0x001100
});
-/****
+/****
* Game Code
-****/
-var tower = game.addChild(new Tower());
-tower.x = 2048 / 2;
-tower.y = 2732 - 200;
+****/
+// Constants
+var GAME_WIDTH = 2048;
+var GAME_HEIGHT = 2732;
+var CENTER_X = GAME_WIDTH / 2;
+var CENTER_Y = GAME_HEIGHT / 2;
+// Game variables
+var tower;
+var upgradePanel;
var bullets = [];
var zombies = [];
-var lastZombieSpawn = 0;
-game.update = function () {
- // Update bullets
- for (var i = bullets.length - 1; i >= 0; i--) {
- var bullet = bullets[i];
- bullet.update();
- for (var j = zombies.length - 1; j >= 0; j--) {
- var zombie = zombies[j];
- if (bullet.intersects(zombie)) {
- zombie.takeDamage(10);
- bullet.destroy();
- bullets.splice(i, 1);
- break;
- }
- }
- }
- // Update zombies
- for (var i = zombies.length - 1; i >= 0; i--) {
- var zombie = zombies[i];
- zombie.update();
- }
- // Spawn new zombies
- if (LK.ticks - lastZombieSpawn > 60) {
- var newZombie = new Zombie();
- newZombie.x = Math.random() * 2048;
- newZombie.y = -50;
- zombies.push(newZombie);
- game.addChild(newZombie);
- lastZombieSpawn = LK.ticks;
- }
-};
-game.down = function (x, y, obj) {
- var newBullet = new Bullet();
- newBullet.x = tower.x;
- newBullet.y = tower.y;
- bullets.push(newBullet);
- game.addChild(newBullet);
-};
\ No newline at end of file
+var explosions = [];
+var targets = [];
+var gold = 100;
+var wave = 1;
+var zombiesKilled = 0;
+var waveSize = 10;
+var zombiesInWave = 0;
+var waveInterval = 0;
+var isGameActive = false;
+var manualCooldown = 0;
+function startGame() {
+ // Play background music
+ LK.playMusic('background', {
+ loop: true
+ });
+ // Create background
+ var background = game.attachAsset('background', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ background.x = CENTER_X;
+ background.y = CENTER_Y;
+ // Create tower
+ tower = new Tower();
+ tower.x = CENTER_X;
+ tower.y = CENTER_Y;
+ game.addChild(tower);
+ // Create upgrade panel
+ upgradePanel = new UpgradePanel(tower);
+ upgradePanel.x = GAME_WIDTH - 500;
+ upgradePanel.y = 0;
+ game.addChild(upgradePanel);
+ // Initialize UI
+ upgradePanel.updateGold(gold);
+ upgradePanel.updateWave(wave);
+ // Start first wave
+ startWave();
+ isGameActive = true;
+}
+function startWave() {
+ waveSize = 10 + wave * 5;
+ zombiesInWave = waveSize;
+ waveInterval = Math.max(30, 120 - wave * 5); // Spawn interval decreases with each wave
+ upgradePanel.updateWave(wave);
+ LK.getSound('waveStart').play();
+ // Flash wave indicator
+ var waveText = new Text2("WAVE " + wave + " INCOMING!", {
+ size: 80,
+ fill: 0xff0000
+ });
+ waveText.x = CENTER_X;
+ waveText.y = 200;
+ waveText.anchor.set(0.5);
+ game.addChild(waveText);
+ // Fade out wave text
+ var fadeInterval = LK.setInterval(function () {
+ waveText.alpha -= 0.02;
+ if (waveText.alpha <= 0) {
+ waveText.destroy();
+ LK.clearInterval(fadeInterval);
+ }
+ }, 30);
+}
+function spawnZombie() {
+ if (zombiesInWave <= 0) {
+ return;
+ }
+ var zombie = new Zombie(wave);
+ // Spawn at random edge position
+ var angle = Math.random() * Math.PI * 2;
+ var distance = 1200; // Spawn outside visible area
+ zombie.x = CENTER_X + Math.cos(angle) * distance;
+ zombie.y = CENTER_Y + Math.sin(angle) * distance;
+ game.addChild(zombie);
+ zombies.push(zombie);
+ zombiesInWave--;
+}
+function gameOver() {
+ isGameActive = false;
+ // Play game over sound
+ LK.getSound('gameOver').play();
+ // Display message
+ var gameOverText = new Text2("GAME OVER!", {
+ size: 120,
+ fill: 0xff0000
+ });
+ gameOverText.x = CENTER_X;
+ gameOverText.y = CENTER_Y;
+ gameOverText.anchor.set(0.5);
+ game.addChild(gameOverText);
+ var scoreText = new Text2("Waves Survived: " + wave + "\nZombies Killed: " + zombiesKilled, {
+ size: 60,
+ fill: 0xffffff
+ });
+ scoreText.x = CENTER_X;
+ scoreText.y = CENTER_Y + 150;
+ scoreText.anchor.set(0.5);
+ game.addChild(scoreText);
+ // Set final score (waves survived * zombies killed)
+ LK.setScore(wave * zombiesKilled);
+ // Show game over screen after delay
+ LK.setTimeout(function () {
+ LK.showGameOver();
+ }, 3000);
+}
+/****
+* Event Handlers
+****/
+game.on('down', function (x, y, obj) {
+ if (!isGameActive) {
+ return;
+ }
+ var event = obj;
+ var pos = game.toLocal(event.global);
+ // Check if clicking on upgrade panel
+ if (pos.x > GAME_WIDTH - 500) {
+ var upgradeCost = upgradePanel.checkUpgradeClick(pos.x, pos.y, gold);
+ if (upgradeCost > 0) {
+ gold -= upgradeCost;
+ upgradePanel.updateGold(gold);
+ }
+ } else {
+ // Manual targeting - can only fire once per second
+ if (manualCooldown <= 0) {
+ // Create target indicator
+ var target = new ManualTarget();
+ target.x = pos.x;
+ target.y = pos.y;
+ game.addChild(target);
+ targets.push(target);
+ // Fire at clicked location
+ tower.manualShoot(pos.x, pos.y);
+ manualCooldown = 60; // 1 second cooldown
+ }
+ }
+});
+/****
+* Game Loop
+****/
+LK.on('tick', function () {
+ if (!isGameActive) {
+ return;
+ }
+ // Update tower
+ tower.update(zombies);
+ // Update upgrade panel
+ upgradePanel.update();
+ // Update bullets
+ for (var i = bullets.length - 1; i >= 0; i--) {
+ if (bullets[i].update()) {
+ bullets[i].destroy();
+ bullets.splice(i, 1);
+ continue;
+ }
+ // Check for zombie hits
+ for (var j = zombies.length - 1; j >= 0; j--) {
+ if (bullets[i] && bullets[i].intersects(zombies[j])) {
+ if (zombies[j].takeDamage(bullets[i].damage)) {
+ // Zombie killed
+ var explosion = new Explosion();
+ explosion.x = zombies[j].x;
+ explosion.y = zombies[j].y;
+ game.addChild(explosion);
+ explosions.push(explosion);
+ // Add gold
+ gold += zombies[j].value;
+ upgradePanel.updateGold(gold);
+ // Show gold pop-up
+ var goldPop = new Text2("+" + zombies[j].value, {
+ size: 30,
+ fill: 0xffcc00
+ });
+ goldPop.x = zombies[j].x;
+ goldPop.y = zombies[j].y - 50;
+ goldPop.anchor.set(0.5);
+ game.addChild(goldPop);
+ // Fade out gold text
+ var fadeInterval = LK.setInterval(function () {
+ goldPop.alpha -= 0.05;
+ goldPop.y -= 1;
+ if (goldPop.alpha <= 0) {
+ goldPop.destroy();
+ LK.clearInterval(fadeInterval);
+ }
+ }, 30);
+ LK.getSound('zombieDeath').play();
+ zombies[j].destroy();
+ zombies.splice(j, 1);
+ zombiesKilled++;
+ }
+ // Remove bullet
+ bullets[i].destroy();
+ bullets.splice(i, 1);
+ break;
+ }
+ }
+ }
+ // Update zombies
+ for (var k = zombies.length - 1; k >= 0; k--) {
+ var result = zombies[k].update(tower);
+ if (result === "gameover") {
+ gameOver();
+ return;
+ }
+ }
+ // Update explosions
+ for (var l = explosions.length - 1; l >= 0; l--) {
+ if (explosions[l].update()) {
+ explosions[l].destroy();
+ explosions.splice(l, 1);
+ }
+ }
+ // Update targets
+ for (var m = targets.length - 1; m >= 0; m--) {
+ if (targets[m].update()) {
+ targets[m].destroy();
+ targets.splice(m, 1);
+ }
+ }
+ // Spawn zombies
+ if (LK.ticks % waveInterval === 0 && zombiesInWave > 0) {
+ spawnZombie();
+ }
+ // Check if wave is complete
+ if (zombiesInWave <= 0 && zombies.length === 0) {
+ // Start next wave after delay
+ wave++;
+ LK.setTimeout(function () {
+ startWave();
+ }, 3000);
+ // Grant bonus gold between waves
+ var bonus = 50 + wave * 10;
+ gold += bonus;
+ upgradePanel.updateGold(gold);
+ // Show wave complete message
+ var waveCompleteText = new Text2("WAVE " + (wave - 1) + " COMPLETE!", {
+ size: 80,
+ fill: 0x33ff33
+ });
+ waveCompleteText.x = CENTER_X;
+ waveCompleteText.y = CENTER_Y;
+ waveCompleteText.anchor.set(0.5);
+ game.addChild(waveCompleteText);
+ var bonusText = new Text2("BONUS: +" + bonus + " GOLD", {
+ size: 60,
+ fill: 0xffcc00
+ });
+ bonusText.x = CENTER_X;
+ bonusText.y = CENTER_Y + 100;
+ bonusText.anchor.set(0.5);
+ game.addChild(bonusText);
+ // Fade out text
+ var fadeInterval = LK.setInterval(function () {
+ waveCompleteText.alpha -= 0.02;
+ bonusText.alpha -= 0.02;
+ if (waveCompleteText.alpha <= 0) {
+ waveCompleteText.destroy();
+ bonusText.destroy();
+ LK.clearInterval(fadeInterval);
+ }
+ }, 30);
+ }
+ // Update manual cooldown
+ if (manualCooldown > 0) {
+ manualCooldown--;
+ }
+});
+// Start the game
+startGame();
\ No newline at end of file