/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Base class var Base = Container.expand(function () { var self = Container.call(this); // Attach base asset (ellipse, blue) var baseGraphics = self.attachAsset('base', { anchorX: 0.5, anchorY: 0.5 }); baseGraphics.width = 180; baseGraphics.height = 180; baseGraphics.color = 0x3399ff; self.maxHp = 20; self.hp = self.maxHp; // Flash on hit self.hit = function () { LK.effects.flashObject(self, 0xff0000, 400); }; // Repair self.repair = function () { self.hp = Math.min(self.maxHp, self.hp + 5); }; // Upgrade self.upgrade = function () { self.maxHp += 10; self.hp = self.maxHp; baseGraphics.scaleX += 0.1; baseGraphics.scaleY += 0.1; }; return self; }); // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); // Attach bullet asset (box, yellow) var bulletGraphics = self.attachAsset('bullet', { anchorX: -2, anchorY: 2 }); bulletGraphics.width = 20; bulletGraphics.height = 20; bulletGraphics.color = 0xb8b031; self.speed = 24; self.target = null; self.side = 1; self.damage = 1; self.update = function () { if (!self.target || self.target.destroyed) { self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 40) { // Hit! self.target.hp -= self.damage; LK.effects.flashObject(self.target, 0xffff00, 200); self.destroy(); return; } var step = self.speed; self.x += dx / dist * step; self.y += dy / dist * step; }; return self; }); // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); // Attach enemy asset (ellipse, red) // Use separate images for left- and right-spawned enemies var assetId = self.direction === -1 ? 'enemyLeft' : 'enemyRight'; var enemyGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Set size and color enemyGraphics.width = 90; enemyGraphics.height = 90; enemyGraphics.color = 0xd83318; // Enemy properties self.speed = 3 + Math.random() * 1.5; // Will be set per wave self.hp = 1; // Will be set per wave self.direction = 1; // 1 for right-to-left, -1 for left-to-right // Update method self.update = function () { self.x += self.speed * self.direction; }; return self; }); // Turret class var Turret = Container.expand(function () { var self = Container.call(this); // Attach turret asset (box, green) var turretGraphics = self.attachAsset('turret', { anchorX: 0.5, anchorY: 0.5 }); turretGraphics.width = 100; turretGraphics.height = 100; turretGraphics.color = 0x83de44; // Turret properties self.range = 500; self.fireRate = 60; // frames between shots self.cooldown = 0; self.level = 1; self.side = 1; // 1 = right, -1 = left // Upgrade method self.upgrade = function () { if (self.level < 3) { self.level += 1; self.range += 100; self.fireRate = Math.max(30, self.fireRate - 10); turretGraphics.scaleX += 0.1; turretGraphics.scaleY += 0.1; } }; // Update method self.update = function () { if (self.cooldown > 0) { self.cooldown--; return; } // Find closest enemy in range on this side var closest = null; var minDist = self.range + 1; for (var i = 0; i < enemies.length; i++) { var e = enemies[i]; if (self.side === 1 && e.x > base.x || self.side === -1 && e.x < base.x) { var dx = e.x - self.x; var dy = e.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < minDist && dist <= self.range) { minDist = dist; closest = e; } } } if (closest) { // Fire at enemy var b = new Bullet(); b.x = self.x; b.y = self.y; b.target = closest; b.side = self.side; b.damage = self.level; // Reverse bullet image for left turrets if (self.side === -1 && b.children && b.children.length > 0) { if (typeof b.children[0].scaleX !== "undefined") { b.children[0].scaleX = -1 * Math.abs(b.children[0].scaleX); } } bullets.push(b); game.addChild(b); self.cooldown = self.fireRate; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Add background image to the game scene var backgroundImg = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, width: 2048, height: 2732 }); game.addChild(backgroundImg); // Game state variables var base = new Base(); base.x = 2048 / 2; base.y = 2732 / 2; game.addChild(base); var turrets = []; var enemies = []; var bullets = []; var gold = 50; var wave = 1; var enemiesToSpawn = 0; var enemiesSpawned = 0; var enemiesPerWave = 6; var waveInProgress = false; var spawnTimer = 0; var nextWaveTimer = 0; var baseHealthTxt, goldTxt, waveTxt; var selectedTurret = null; // HP Bar for base (black outline, gray background, green foreground) // Use shape assets for outline, background, and foreground bar var baseHpBarOutline = LK.getAsset('baseHpBarOutline', { anchorX: 0.5, anchorY: 0.5, x: base.x, y: base.y - 190, width: 208, height: 40 }); var baseHpBarBg = LK.getAsset('baseHpBarBg', { anchorX: 0.5, anchorY: 0.5, x: base.x, y: base.y - 190, width: 200, height: 32 }); var baseHpBar = LK.getAsset('baseHpBar', { anchorX: 0, anchorY: 0.5, x: base.x - 100, y: base.y - 190, width: 200, height: 32 }); game.addChild(baseHpBarOutline); game.addChild(baseHpBarBg); game.addChild(baseHpBar); // (ground platform removed) // GUI // HP text removed goldTxt = new Text2("Gold: " + gold, { size: 80, fill: 0xFFD700 }); goldTxt.anchor.set(1, 0); LK.gui.topRight.addChild(goldTxt); waveTxt = new Text2("Wave: " + wave, { size: 80, fill: "#fff" }); waveTxt.anchor.set(0.5, 0); LK.gui.top.addChild(waveTxt); // Turret placement buttons (left and right) // Space out the buttons and texts at the bottom of the screen for better separation var bottomY = 2732 - 180; var leftTurretBtn = LK.getAsset('turret', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 700, y: bottomY, scaleX: 1.2, scaleY: 1.2 }); var rightTurretBtn = LK.getAsset('turret', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 700, y: bottomY, scaleX: 1.2, scaleY: 1.2 }); game.addChild(leftTurretBtn); game.addChild(rightTurretBtn); var leftTurretCostTxt = new Text2("Buy: 30", { size: 60, fill: "#fff" }); leftTurretCostTxt.anchor.set(0.5, 0); leftTurretCostTxt.x = leftTurretBtn.x; leftTurretCostTxt.y = leftTurretBtn.y + 100; game.addChild(leftTurretCostTxt); var rightTurretCostTxt = new Text2("Buy: 30", { size: 60, fill: "#fff" }); rightTurretCostTxt.anchor.set(0.5, 0); rightTurretCostTxt.x = rightTurretBtn.x; rightTurretCostTxt.y = rightTurretBtn.y + 100; game.addChild(rightTurretCostTxt); // Base repair and upgrade buttons var repairBtn = LK.getAsset('repair', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 350, y: bottomY, scaleX: 0.7, scaleY: 0.7 }); var upgradeBtn = LK.getAsset('upgrade', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 350, y: bottomY, scaleX: 0.7, scaleY: 0.7 }); game.addChild(repairBtn); game.addChild(upgradeBtn); var repairCostTxt = new Text2("Repair: 15", { size: 60, fill: "#fff" }); repairCostTxt.anchor.set(0.5, 0); repairCostTxt.x = repairBtn.x; repairCostTxt.y = repairBtn.y + 100; game.addChild(repairCostTxt); var upgradeCostTxt = new Text2("Upgrade: 40", { size: 60, fill: "#fff" }); upgradeCostTxt.anchor.set(0.5, 0); upgradeCostTxt.x = upgradeBtn.x; upgradeCostTxt.y = upgradeBtn.y + 100; game.addChild(upgradeCostTxt); // Turret upgrade buttons (appear above turrets) function showTurretUpgradeBtn(turret) { if (turret.upgradeBtn) { return; } var btn = LK.getAsset('turret', { anchorX: 0.5, anchorY: 0.5, x: turret.x, y: turret.y - 120, scaleX: 0.6, scaleY: 0.6 }); var txt = new Text2("Upgrade: 25", { size: 50, fill: "#fff" }); txt.anchor.set(0.5, 0); txt.x = btn.x; txt.y = btn.y + 50; btn._upgradeTxt = txt; game.addChild(btn); game.addChild(txt); turret.upgradeBtn = btn; turret.upgradeTxt = txt; } // Remove upgrade button function hideTurretUpgradeBtn(turret) { if (turret.upgradeBtn) { turret.upgradeBtn.destroy(); turret.upgradeTxt.destroy(); turret.upgradeBtn = null; turret.upgradeTxt = null; } } // Place initial turrets function placeTurret(side) { // Count how many turrets already exist on this side var count = 0; for (var i = 0; i < turrets.length; i++) { if (turrets[i].side === side) { count++; } } // Place the first turret at center, then alternate above/below var x = base.x + (side === 1 ? 300 : -300); var y = base.y; if (count > 0) { // Alternate above and below, spacing by 180px var offset = 180 * Math.ceil(count / 2); if (count % 2 === 1) { y = base.y - offset; // above } else { y = base.y + offset; // below } } var t = new Turret(); t.x = x; t.y = y; t.side = side; // Reverse the image for the left turret if (side === -1 && t.children && t.children.length > 0) { // Assume the first child is the turret graphics if (typeof t.children[0].scaleX !== "undefined") { t.children[0].scaleX = -1 * Math.abs(t.children[0].scaleX); } } turrets.push(t); game.addChild(t); return t; } // Initial turrets placeTurret(-1); placeTurret(1); // Handle turret placement leftTurretBtn.down = function (x, y, obj) { if (gold >= 30) { var t = placeTurret(-1); gold -= 30; goldTxt.setText("Gold: " + gold); } }; rightTurretBtn.down = function (x, y, obj) { if (gold >= 30) { var t = placeTurret(1); gold -= 30; goldTxt.setText("Gold: " + gold); } }; // Handle base repair/upgrade repairBtn.down = function (x, y, obj) { if (gold >= 15 && base.hp < base.maxHp) { base.repair(); gold -= 15; goldTxt.setText("Gold: " + gold); // HP text removed, no update needed here } }; upgradeBtn.down = function (x, y, obj) { if (gold >= 40) { base.upgrade(); gold -= 40; goldTxt.setText("Gold: " + gold); // HP text removed, no update needed here } }; // Handle turret upgrade game.down = function (x, y, obj) { // Check if a turret was tapped for (var i = 0; i < turrets.length; i++) { var t = turrets[i]; if (t && LK.util && typeof LK.util.containsPoint === "function" && LK.util.containsPoint(t, { x: x, y: y })) { selectedTurret = t; showTurretUpgradeBtn(t); return; } } // Check if upgrade button was tapped for (var i = 0; i < turrets.length; i++) { var t = turrets[i]; if (t && t.upgradeBtn && LK.util && typeof LK.util.containsPoint === "function" && LK.util.containsPoint(t.upgradeBtn, { x: x, y: y })) { if (gold >= 25 && t.level < 3) { t.upgrade(); gold -= 25; goldTxt.setText("Gold: " + gold); if (t.level >= 3) { hideTurretUpgradeBtn(t); } } return; } } // Deselect turret if tap elsewhere for (var i = 0; i < turrets.length; i++) { hideTurretUpgradeBtn(turrets[i]); } selectedTurret = null; }; // Enemy spawn logic function startWave() { waveInProgress = true; enemiesToSpawn = Math.floor(enemiesPerWave + (wave - 1) * 4.5); // more enemies per wave, longer waves enemiesSpawned = 0; spawnTimer = 0; waveTxt.setText("Wave: " + wave); } function endWave() { waveInProgress = false; nextWaveTimer = 120; // 2 seconds gold += 20 + wave * 5; goldTxt.setText("Gold: " + gold); enemiesPerWave += 4; // increase more per wave, longer waves wave += 1; } // Main update loop game.update = function () { // Update HP bar position and width baseHpBarOutline.x = base.x; baseHpBarOutline.y = base.y - 190; baseHpBarBg.x = base.x; baseHpBarBg.y = base.y - 190; // Health bar shrinks left-to-right as base takes damage var hpRatio = Math.max(0, base.hp) / base.maxHp; baseHpBar.width = 200 * hpRatio; baseHpBar.x = base.x - 100; // left edge stays fixed baseHpBar.y = base.y - 190; // Update turrets for (var i = 0; i < turrets.length; i++) { turrets[i].update(); } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); // Check if reached base if (e.direction === 1 && e.x >= base.x - 90 || e.direction === -1 && e.x <= base.x + 90) { base.hp -= 1; base.hit(); e.destroy(); enemies.splice(i, 1); if (base.hp <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } continue; } // Check if dead if (e.hp <= 0) { e.destroy(); enemies.splice(i, 1); if (Math.random() < 0.4) { // 40% chance to get gold per enemy gold += 5; goldTxt.setText("Gold: " + gold); } continue; } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.update(); if (b.destroyed) { b.destroy(); bullets.splice(i, 1); } } // Wave logic if (waveInProgress) { if (enemiesSpawned < enemiesToSpawn) { if (spawnTimer <= 0) { // Spawn enemy from left or right var side = Math.random() < 0.5 ? -1 : 1; var EnemyClass = Enemy; EnemyClass.prototype.direction = side; // Set direction before instantiation for asset selection var e = new EnemyClass(); e.direction = side; e.x = side === 1 ? 0 : 2048; e.y = base.y - 200 + Math.random() * 400; // Make enemies slower and waves harder: lower base speed, increase HP and spawn more per wave e.speed = 1.5 + wave * 0.15 + Math.random() * 0.5; // slower base, less scaling e.hp = 2 + Math.floor(wave * 1.2); // more HP per wave enemies.push(e); game.addChild(e); enemiesSpawned++; spawnTimer = 36 + Math.floor(Math.random() * 24); // slightly slower spawn rate } else { spawnTimer--; } } else if (enemies.length === 0) { endWave(); } } else { if (nextWaveTimer > 0) { nextWaveTimer--; if (nextWaveTimer === 0) { startWave(); } } } }; // Start first wave startWave();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Base class
var Base = Container.expand(function () {
var self = Container.call(this);
// Attach base asset (ellipse, blue)
var baseGraphics = self.attachAsset('base', {
anchorX: 0.5,
anchorY: 0.5
});
baseGraphics.width = 180;
baseGraphics.height = 180;
baseGraphics.color = 0x3399ff;
self.maxHp = 20;
self.hp = self.maxHp;
// Flash on hit
self.hit = function () {
LK.effects.flashObject(self, 0xff0000, 400);
};
// Repair
self.repair = function () {
self.hp = Math.min(self.maxHp, self.hp + 5);
};
// Upgrade
self.upgrade = function () {
self.maxHp += 10;
self.hp = self.maxHp;
baseGraphics.scaleX += 0.1;
baseGraphics.scaleY += 0.1;
};
return self;
});
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
// Attach bullet asset (box, yellow)
var bulletGraphics = self.attachAsset('bullet', {
anchorX: -2,
anchorY: 2
});
bulletGraphics.width = 20;
bulletGraphics.height = 20;
bulletGraphics.color = 0xb8b031;
self.speed = 24;
self.target = null;
self.side = 1;
self.damage = 1;
self.update = function () {
if (!self.target || self.target.destroyed) {
self.destroy();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 40) {
// Hit!
self.target.hp -= self.damage;
LK.effects.flashObject(self.target, 0xffff00, 200);
self.destroy();
return;
}
var step = self.speed;
self.x += dx / dist * step;
self.y += dy / dist * step;
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach enemy asset (ellipse, red)
// Use separate images for left- and right-spawned enemies
var assetId = self.direction === -1 ? 'enemyLeft' : 'enemyRight';
var enemyGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Set size and color
enemyGraphics.width = 90;
enemyGraphics.height = 90;
enemyGraphics.color = 0xd83318;
// Enemy properties
self.speed = 3 + Math.random() * 1.5; // Will be set per wave
self.hp = 1; // Will be set per wave
self.direction = 1; // 1 for right-to-left, -1 for left-to-right
// Update method
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
// Turret class
var Turret = Container.expand(function () {
var self = Container.call(this);
// Attach turret asset (box, green)
var turretGraphics = self.attachAsset('turret', {
anchorX: 0.5,
anchorY: 0.5
});
turretGraphics.width = 100;
turretGraphics.height = 100;
turretGraphics.color = 0x83de44;
// Turret properties
self.range = 500;
self.fireRate = 60; // frames between shots
self.cooldown = 0;
self.level = 1;
self.side = 1; // 1 = right, -1 = left
// Upgrade method
self.upgrade = function () {
if (self.level < 3) {
self.level += 1;
self.range += 100;
self.fireRate = Math.max(30, self.fireRate - 10);
turretGraphics.scaleX += 0.1;
turretGraphics.scaleY += 0.1;
}
};
// Update method
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
return;
}
// Find closest enemy in range on this side
var closest = null;
var minDist = self.range + 1;
for (var i = 0; i < enemies.length; i++) {
var e = enemies[i];
if (self.side === 1 && e.x > base.x || self.side === -1 && e.x < base.x) {
var dx = e.x - self.x;
var dy = e.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < minDist && dist <= self.range) {
minDist = dist;
closest = e;
}
}
}
if (closest) {
// Fire at enemy
var b = new Bullet();
b.x = self.x;
b.y = self.y;
b.target = closest;
b.side = self.side;
b.damage = self.level;
// Reverse bullet image for left turrets
if (self.side === -1 && b.children && b.children.length > 0) {
if (typeof b.children[0].scaleX !== "undefined") {
b.children[0].scaleX = -1 * Math.abs(b.children[0].scaleX);
}
}
bullets.push(b);
game.addChild(b);
self.cooldown = self.fireRate;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Add background image to the game scene
var backgroundImg = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
width: 2048,
height: 2732
});
game.addChild(backgroundImg);
// Game state variables
var base = new Base();
base.x = 2048 / 2;
base.y = 2732 / 2;
game.addChild(base);
var turrets = [];
var enemies = [];
var bullets = [];
var gold = 50;
var wave = 1;
var enemiesToSpawn = 0;
var enemiesSpawned = 0;
var enemiesPerWave = 6;
var waveInProgress = false;
var spawnTimer = 0;
var nextWaveTimer = 0;
var baseHealthTxt, goldTxt, waveTxt;
var selectedTurret = null;
// HP Bar for base (black outline, gray background, green foreground)
// Use shape assets for outline, background, and foreground bar
var baseHpBarOutline = LK.getAsset('baseHpBarOutline', {
anchorX: 0.5,
anchorY: 0.5,
x: base.x,
y: base.y - 190,
width: 208,
height: 40
});
var baseHpBarBg = LK.getAsset('baseHpBarBg', {
anchorX: 0.5,
anchorY: 0.5,
x: base.x,
y: base.y - 190,
width: 200,
height: 32
});
var baseHpBar = LK.getAsset('baseHpBar', {
anchorX: 0,
anchorY: 0.5,
x: base.x - 100,
y: base.y - 190,
width: 200,
height: 32
});
game.addChild(baseHpBarOutline);
game.addChild(baseHpBarBg);
game.addChild(baseHpBar);
// (ground platform removed)
// GUI
// HP text removed
goldTxt = new Text2("Gold: " + gold, {
size: 80,
fill: 0xFFD700
});
goldTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(goldTxt);
waveTxt = new Text2("Wave: " + wave, {
size: 80,
fill: "#fff"
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
// Turret placement buttons (left and right)
// Space out the buttons and texts at the bottom of the screen for better separation
var bottomY = 2732 - 180;
var leftTurretBtn = LK.getAsset('turret', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 700,
y: bottomY,
scaleX: 1.2,
scaleY: 1.2
});
var rightTurretBtn = LK.getAsset('turret', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 700,
y: bottomY,
scaleX: 1.2,
scaleY: 1.2
});
game.addChild(leftTurretBtn);
game.addChild(rightTurretBtn);
var leftTurretCostTxt = new Text2("Buy: 30", {
size: 60,
fill: "#fff"
});
leftTurretCostTxt.anchor.set(0.5, 0);
leftTurretCostTxt.x = leftTurretBtn.x;
leftTurretCostTxt.y = leftTurretBtn.y + 100;
game.addChild(leftTurretCostTxt);
var rightTurretCostTxt = new Text2("Buy: 30", {
size: 60,
fill: "#fff"
});
rightTurretCostTxt.anchor.set(0.5, 0);
rightTurretCostTxt.x = rightTurretBtn.x;
rightTurretCostTxt.y = rightTurretBtn.y + 100;
game.addChild(rightTurretCostTxt);
// Base repair and upgrade buttons
var repairBtn = LK.getAsset('repair', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 350,
y: bottomY,
scaleX: 0.7,
scaleY: 0.7
});
var upgradeBtn = LK.getAsset('upgrade', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 350,
y: bottomY,
scaleX: 0.7,
scaleY: 0.7
});
game.addChild(repairBtn);
game.addChild(upgradeBtn);
var repairCostTxt = new Text2("Repair: 15", {
size: 60,
fill: "#fff"
});
repairCostTxt.anchor.set(0.5, 0);
repairCostTxt.x = repairBtn.x;
repairCostTxt.y = repairBtn.y + 100;
game.addChild(repairCostTxt);
var upgradeCostTxt = new Text2("Upgrade: 40", {
size: 60,
fill: "#fff"
});
upgradeCostTxt.anchor.set(0.5, 0);
upgradeCostTxt.x = upgradeBtn.x;
upgradeCostTxt.y = upgradeBtn.y + 100;
game.addChild(upgradeCostTxt);
// Turret upgrade buttons (appear above turrets)
function showTurretUpgradeBtn(turret) {
if (turret.upgradeBtn) {
return;
}
var btn = LK.getAsset('turret', {
anchorX: 0.5,
anchorY: 0.5,
x: turret.x,
y: turret.y - 120,
scaleX: 0.6,
scaleY: 0.6
});
var txt = new Text2("Upgrade: 25", {
size: 50,
fill: "#fff"
});
txt.anchor.set(0.5, 0);
txt.x = btn.x;
txt.y = btn.y + 50;
btn._upgradeTxt = txt;
game.addChild(btn);
game.addChild(txt);
turret.upgradeBtn = btn;
turret.upgradeTxt = txt;
}
// Remove upgrade button
function hideTurretUpgradeBtn(turret) {
if (turret.upgradeBtn) {
turret.upgradeBtn.destroy();
turret.upgradeTxt.destroy();
turret.upgradeBtn = null;
turret.upgradeTxt = null;
}
}
// Place initial turrets
function placeTurret(side) {
// Count how many turrets already exist on this side
var count = 0;
for (var i = 0; i < turrets.length; i++) {
if (turrets[i].side === side) {
count++;
}
}
// Place the first turret at center, then alternate above/below
var x = base.x + (side === 1 ? 300 : -300);
var y = base.y;
if (count > 0) {
// Alternate above and below, spacing by 180px
var offset = 180 * Math.ceil(count / 2);
if (count % 2 === 1) {
y = base.y - offset; // above
} else {
y = base.y + offset; // below
}
}
var t = new Turret();
t.x = x;
t.y = y;
t.side = side;
// Reverse the image for the left turret
if (side === -1 && t.children && t.children.length > 0) {
// Assume the first child is the turret graphics
if (typeof t.children[0].scaleX !== "undefined") {
t.children[0].scaleX = -1 * Math.abs(t.children[0].scaleX);
}
}
turrets.push(t);
game.addChild(t);
return t;
}
// Initial turrets
placeTurret(-1);
placeTurret(1);
// Handle turret placement
leftTurretBtn.down = function (x, y, obj) {
if (gold >= 30) {
var t = placeTurret(-1);
gold -= 30;
goldTxt.setText("Gold: " + gold);
}
};
rightTurretBtn.down = function (x, y, obj) {
if (gold >= 30) {
var t = placeTurret(1);
gold -= 30;
goldTxt.setText("Gold: " + gold);
}
};
// Handle base repair/upgrade
repairBtn.down = function (x, y, obj) {
if (gold >= 15 && base.hp < base.maxHp) {
base.repair();
gold -= 15;
goldTxt.setText("Gold: " + gold);
// HP text removed, no update needed here
}
};
upgradeBtn.down = function (x, y, obj) {
if (gold >= 40) {
base.upgrade();
gold -= 40;
goldTxt.setText("Gold: " + gold);
// HP text removed, no update needed here
}
};
// Handle turret upgrade
game.down = function (x, y, obj) {
// Check if a turret was tapped
for (var i = 0; i < turrets.length; i++) {
var t = turrets[i];
if (t && LK.util && typeof LK.util.containsPoint === "function" && LK.util.containsPoint(t, {
x: x,
y: y
})) {
selectedTurret = t;
showTurretUpgradeBtn(t);
return;
}
}
// Check if upgrade button was tapped
for (var i = 0; i < turrets.length; i++) {
var t = turrets[i];
if (t && t.upgradeBtn && LK.util && typeof LK.util.containsPoint === "function" && LK.util.containsPoint(t.upgradeBtn, {
x: x,
y: y
})) {
if (gold >= 25 && t.level < 3) {
t.upgrade();
gold -= 25;
goldTxt.setText("Gold: " + gold);
if (t.level >= 3) {
hideTurretUpgradeBtn(t);
}
}
return;
}
}
// Deselect turret if tap elsewhere
for (var i = 0; i < turrets.length; i++) {
hideTurretUpgradeBtn(turrets[i]);
}
selectedTurret = null;
};
// Enemy spawn logic
function startWave() {
waveInProgress = true;
enemiesToSpawn = Math.floor(enemiesPerWave + (wave - 1) * 4.5); // more enemies per wave, longer waves
enemiesSpawned = 0;
spawnTimer = 0;
waveTxt.setText("Wave: " + wave);
}
function endWave() {
waveInProgress = false;
nextWaveTimer = 120; // 2 seconds
gold += 20 + wave * 5;
goldTxt.setText("Gold: " + gold);
enemiesPerWave += 4; // increase more per wave, longer waves
wave += 1;
}
// Main update loop
game.update = function () {
// Update HP bar position and width
baseHpBarOutline.x = base.x;
baseHpBarOutline.y = base.y - 190;
baseHpBarBg.x = base.x;
baseHpBarBg.y = base.y - 190;
// Health bar shrinks left-to-right as base takes damage
var hpRatio = Math.max(0, base.hp) / base.maxHp;
baseHpBar.width = 200 * hpRatio;
baseHpBar.x = base.x - 100; // left edge stays fixed
baseHpBar.y = base.y - 190;
// Update turrets
for (var i = 0; i < turrets.length; i++) {
turrets[i].update();
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Check if reached base
if (e.direction === 1 && e.x >= base.x - 90 || e.direction === -1 && e.x <= base.x + 90) {
base.hp -= 1;
base.hit();
e.destroy();
enemies.splice(i, 1);
if (base.hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
continue;
}
// Check if dead
if (e.hp <= 0) {
e.destroy();
enemies.splice(i, 1);
if (Math.random() < 0.4) {
// 40% chance to get gold per enemy
gold += 5;
goldTxt.setText("Gold: " + gold);
}
continue;
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
if (b.destroyed) {
b.destroy();
bullets.splice(i, 1);
}
}
// Wave logic
if (waveInProgress) {
if (enemiesSpawned < enemiesToSpawn) {
if (spawnTimer <= 0) {
// Spawn enemy from left or right
var side = Math.random() < 0.5 ? -1 : 1;
var EnemyClass = Enemy;
EnemyClass.prototype.direction = side; // Set direction before instantiation for asset selection
var e = new EnemyClass();
e.direction = side;
e.x = side === 1 ? 0 : 2048;
e.y = base.y - 200 + Math.random() * 400;
// Make enemies slower and waves harder: lower base speed, increase HP and spawn more per wave
e.speed = 1.5 + wave * 0.15 + Math.random() * 0.5; // slower base, less scaling
e.hp = 2 + Math.floor(wave * 1.2); // more HP per wave
enemies.push(e);
game.addChild(e);
enemiesSpawned++;
spawnTimer = 36 + Math.floor(Math.random() * 24); // slightly slower spawn rate
} else {
spawnTimer--;
}
} else if (enemies.length === 0) {
endWave();
}
} else {
if (nextWaveTimer > 0) {
nextWaveTimer--;
if (nextWaveTimer === 0) {
startWave();
}
}
}
};
// Start first wave
startWave();
2d turret. In-Game asset. 2d. High contrast. No shadows
a 2d turret base. In-Game asset. 2d. High contrast. No shadows
a 2d upgrade button png. In-Game asset. 2d. High contrast. No shadows
purple zombie 2d. In-Game asset. 2d. High contrast. No shadows
add a grass background, empty middle, grass and trees around. In-Game asset. 2d. High contrast. No shadows