/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (type, pathIndex) {
var self = Container.call(this);
self.type = type;
self.pathIndex = pathIndex || 0;
self.speed = 2;
self.maxHealth = 100;
self.health = self.maxHealth;
self.resources = 10;
self.isDead = false;
// Set enemy properties based on type
if (type === 'goblin') {
self.speed = 3;
self.maxHealth = 50;
self.health = 50;
self.resources = 5;
self.enemyGraphics = self.attachAsset('goblin', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'orc') {
self.speed = 2;
self.maxHealth = 100;
self.health = 100;
self.resources = 10;
self.enemyGraphics = self.attachAsset('orc', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'troll') {
self.speed = 1;
self.maxHealth = 200;
self.health = 200;
self.resources = 20;
self.enemyGraphics = self.attachAsset('troll', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Health bar
self.healthBar = self.addChild(LK.getAsset('pathTile', {
width: 40,
height: 6,
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00FF00
}));
self.healthBar.y = -40;
self.takeDamage = function (damage) {
self.health -= damage;
var healthPercent = self.health / self.maxHealth;
self.healthBar.width = 40 * healthPercent;
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xFFFF00;
} else {
self.healthBar.tint = 0xFF0000;
}
if (self.health <= 0) {
self.isDead = true;
LK.getSound('enemyDeath').play();
return true;
}
LK.getSound('enemyHit').play();
return false;
};
self.update = function () {
if (self.isDead) {
return;
}
// Move along path
if (self.pathIndex < gamePath.length - 1) {
var currentTarget = gamePath[self.pathIndex + 1];
var dx = currentTarget.x - self.x;
var dy = currentTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 10) {
self.pathIndex++;
if (self.pathIndex >= gamePath.length - 1) {
// Reached base
self.reachedBase = true;
}
} else {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
}
};
return self;
});
var Projectile = Container.expand(function (type, startX, startY, targetX, targetY, damage) {
var self = Container.call(this);
self.type = type;
self.damage = damage;
self.speed = 8;
self.targetX = targetX;
self.targetY = targetY;
self.hasHit = false;
self.x = startX;
self.y = startY;
if (type === 'arrow') {
self.projectileGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'fireball') {
self.projectileGraphics = self.attachAsset('fireball', {
anchorX: 0.5,
anchorY: 0.5
});
} else if (type === 'sword') {
self.projectileGraphics = self.attachAsset('sword', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
self.dirX = dx / distance;
self.dirY = dy / distance;
self.update = function () {
if (self.hasHit) {
return;
}
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Check if reached target area
var distanceToTarget = Math.sqrt(Math.pow(self.x - self.targetX, 2) + Math.pow(self.y - self.targetY, 2));
if (distanceToTarget < 20) {
self.hasHit = true;
}
// Remove if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.hasHit = true;
}
};
return self;
});
var Tower = Container.expand(function (type, slotX, slotY) {
var self = Container.call(this);
self.type = type;
self.level = 1;
self.range = 150;
self.damage = 25;
self.attackSpeed = 60; // frames between attacks
self.lastAttack = 0;
self.cost = 50;
self.x = slotX;
self.y = slotY;
if (type === 'archer') {
self.towerGraphics = self.attachAsset('archerTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.projectileType = 'arrow';
self.range = 180;
self.damage = 20;
self.attackSpeed = 40;
self.cost = 30;
} else if (type === 'mage') {
self.towerGraphics = self.attachAsset('mageTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.projectileType = 'fireball';
self.range = 120;
self.damage = 40;
self.attackSpeed = 80;
self.cost = 60;
} else if (type === 'warrior') {
self.towerGraphics = self.attachAsset('warriorTower', {
anchorX: 0.5,
anchorY: 0.5
});
self.projectileType = 'sword';
self.range = 100;
self.damage = 60;
self.attackSpeed = 100;
self.cost = 80;
self.armyCooldown = 60 * 60; // 60 seconds at 60 FPS
self.lastArmyRelease = 0;
}
// Range indicator (invisible by default)
self.rangeIndicator = self.addChild(LK.getAsset('pathTile', {
width: self.range * 2,
height: self.range * 2,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
tint: 0xFFFFFF
}));
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (enemy.isDead) {
continue;
}
var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
if (distance <= self.range && distance < closestDistance) {
closestDistance = distance;
closestEnemy = enemy;
}
}
return closestEnemy;
};
self.releaseArmy = function () {
// Clear all enemies on the path
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (!enemy.isDead) {
// Add visual effect before destroying
tween(enemy, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (enemy && !enemy.isDead) {
gameResources += enemy.resources;
gameResources += 10; // Bonus 10 coins for warrior tower kills
enemy.isDead = true;
enemy.destroy();
enemies.splice(enemies.indexOf(enemy), 1);
}
}
});
}
}
// Visual effect on tower
tween(self.towerGraphics, {
tint: 0xFFD700
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.towerGraphics, {
tint: 0xFFFFFF
}, {
duration: 800,
easing: tween.easeIn
});
}
});
LK.getSound('shoot').play();
};
self.attack = function (target) {
if (self.type === 'warrior') {
// Check if army cooldown is ready
if (LK.ticks - self.lastArmyRelease >= self.armyCooldown) {
self.releaseArmy();
self.lastArmyRelease = LK.ticks;
}
return;
}
if (LK.ticks - self.lastAttack < self.attackSpeed) {
return;
}
var projectile = new Projectile(self.projectileType, self.x, self.y, target.x, target.y, self.damage);
projectiles.push(projectile);
game.addChild(projectile);
self.lastAttack = LK.ticks;
LK.getSound('shoot').play();
};
self.update = function () {
var target = self.findTarget();
if (target) {
self.attack(target);
}
// Update warrior tower cooldown indicator
if (self.type === 'warrior') {
var cooldownRemaining = self.armyCooldown - (LK.ticks - self.lastArmyRelease);
if (cooldownRemaining > 0) {
var cooldownPercent = cooldownRemaining / self.armyCooldown;
self.towerGraphics.tint = 0x888888 + Math.floor((1 - cooldownPercent) * 0x777777);
} else {
self.towerGraphics.tint = 0xFFFFFF;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F2F
});
/****
* Game Code
****/
// Game state variables
// Tower Assets
// Enemy Assets
// Projectile Assets
// UI Assets
// Sound Assets
var gameResources = 100;
var baseHealth = 100;
var currentWave = 1;
var enemiesInWave = 5;
var enemiesSpawned = 0;
var waveInProgress = false;
var gameOver = false;
var selectedTowerType = 'archer';
// Game arrays
var enemies = [];
var towers = [];
var projectiles = [];
var towerSlots = [];
// Different paths for different waves
var allPaths = [
// Path 1 - Straight horizontal then vertical
[{
x: 100,
y: 100
}, {
x: 300,
y: 100
}, {
x: 300,
y: 300
}, {
x: 600,
y: 300
}, {
x: 600,
y: 500
}, {
x: 900,
y: 500
}, {
x: 900,
y: 700
}, {
x: 1200,
y: 700
}, {
x: 1200,
y: 900
}, {
x: 1500,
y: 900
}, {
x: 1500,
y: 1100
}, {
x: 1800,
y: 1100
}],
// Path 2 - Zigzag pattern
[{
x: 100,
y: 200
}, {
x: 500,
y: 200
}, {
x: 500,
y: 400
}, {
x: 200,
y: 400
}, {
x: 200,
y: 600
}, {
x: 800,
y: 600
}, {
x: 800,
y: 800
}, {
x: 400,
y: 800
}, {
x: 400,
y: 1000
}, {
x: 1200,
y: 1000
}, {
x: 1200,
y: 1200
}, {
x: 1800,
y: 1200
}],
// Path 3 - Curved path
[{
x: 100,
y: 300
}, {
x: 400,
y: 300
}, {
x: 400,
y: 150
}, {
x: 700,
y: 150
}, {
x: 700,
y: 450
}, {
x: 1000,
y: 450
}, {
x: 1000,
y: 200
}, {
x: 1300,
y: 200
}, {
x: 1300,
y: 700
}, {
x: 1600,
y: 700
}, {
x: 1600,
y: 1000
}, {
x: 1800,
y: 1000
}],
// Path 4 - Long winding path
[{
x: 100,
y: 400
}, {
x: 200,
y: 400
}, {
x: 200,
y: 200
}, {
x: 600,
y: 200
}, {
x: 600,
y: 600
}, {
x: 400,
y: 600
}, {
x: 400,
y: 800
}, {
x: 1000,
y: 800
}, {
x: 1000,
y: 400
}, {
x: 1400,
y: 400
}, {
x: 1400,
y: 1200
}, {
x: 1800,
y: 1200
}]];
// Current game path - changes each wave
var gamePath = allPaths[0];
// Path tiles array to track them
var pathTiles = [];
// Create path tiles
function createPathTiles() {
for (var i = 0; i < gamePath.length; i++) {
var pathTile = game.addChild(LK.getAsset('pathTile', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.3
}));
pathTile.x = gamePath[i].x;
pathTile.y = gamePath[i].y;
pathTiles.push(pathTile);
}
}
// Function to redraw path for new waves
function redrawPath() {
// Remove old path tiles
for (var i = 0; i < pathTiles.length; i++) {
pathTiles[i].destroy();
}
pathTiles = [];
// Create new path tiles
createPathTiles();
// Update base position to end of new path
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
}
// Initialize path tiles
createPathTiles();
// Create base at end of path
var base = game.addChild(LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
}));
base.x = gamePath[gamePath.length - 1].x;
base.y = gamePath[gamePath.length - 1].y;
// Create tower slots around the path
var baseSlotPositions = [{
x: 200,
y: 200
}, {
x: 400,
y: 200
}, {
x: 500,
y: 400
}, {
x: 700,
y: 400
}, {
x: 800,
y: 600
}, {
x: 1000,
y: 600
}, {
x: 1100,
y: 800
}, {
x: 1300,
y: 800
}, {
x: 1400,
y: 1000
}, {
x: 1600,
y: 1000
}, {
x: 1700,
y: 1200
}];
// Additional slots for wave 2 and higher
var extraSlotPositions = [{
x: 150,
y: 350
}, {
x: 350,
y: 550
}, {
x: 650,
y: 350
}, {
x: 850,
y: 450
}, {
x: 1050,
y: 350
}, {
x: 1250,
y: 550
}, {
x: 1450,
y: 650
}, {
x: 1550,
y: 850
}];
function createTowerSlots() {
// Clear existing slots
for (var i = 0; i < towerSlots.length; i++) {
towerSlots[i].destroy();
}
towerSlots = [];
// Always create base slots
var slotsToCreate = baseSlotPositions.slice();
// Add extra slots for wave 2 and higher
if (currentWave >= 2) {
slotsToCreate = slotsToCreate.concat(extraSlotPositions);
}
for (var i = 0; i < slotsToCreate.length; i++) {
var slot = game.addChild(LK.getAsset('towerSlot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
slot.x = slotsToCreate[i].x;
slot.y = slotsToCreate[i].y;
slot.isEmpty = true;
slot.slotIndex = i;
towerSlots.push(slot);
}
}
// Initialize tower slots
createTowerSlots();
// Create decorative trees and bushes
var decorativeElements = [];
var treePositions = [{
x: 50,
y: 50
}, {
x: 150,
y: 80
}, {
x: 250,
y: 120
}, {
x: 1850,
y: 100
}, {
x: 1750,
y: 150
}, {
x: 1650,
y: 80
}, {
x: 80,
y: 800
}, {
x: 180,
y: 950
}, {
x: 120,
y: 1100
}, {
x: 1880,
y: 1400
}, {
x: 1780,
y: 1500
}, {
x: 1820,
y: 1600
}, {
x: 950,
y: 50
}, {
x: 1050,
y: 80
}, {
x: 850,
y: 120
}, {
x: 50,
y: 1800
}, {
x: 150,
y: 1900
}, {
x: 250,
y: 2000
}];
var bushPositions = [{
x: 350,
y: 80
}, {
x: 450,
y: 120
}, {
x: 550,
y: 90
}, {
x: 1550,
y: 200
}, {
x: 1650,
y: 180
}, {
x: 1750,
y: 220
}, {
x: 80,
y: 1200
}, {
x: 180,
y: 1300
}, {
x: 120,
y: 1400
}, {
x: 1880,
y: 1800
}, {
x: 1780,
y: 1900
}, {
x: 1820,
y: 2000
}, {
x: 750,
y: 100
}, {
x: 850,
y: 180
}, {
x: 950,
y: 140
}, {
x: 1100,
y: 1800
}, {
x: 1200,
y: 1900
}, {
x: 1300,
y: 2000
}, {
x: 300,
y: 1600
}, {
x: 400,
y: 1700
}, {
x: 500,
y: 1800
}];
// Create trees
for (var i = 0; i < treePositions.length; i++) {
var tree = game.addChild(LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
}));
tree.x = treePositions[i].x;
tree.y = treePositions[i].y;
decorativeElements.push(tree);
}
// Create bushes
for (var i = 0; i < bushPositions.length; i++) {
var bush = game.addChild(LK.getAsset('bush', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
}));
bush.x = bushPositions[i].x;
bush.y = bushPositions[i].y;
decorativeElements.push(bush);
}
// UI Elements
var resourcesText = new Text2('Resources: ' + gameResources, {
size: 60,
fill: 0xFFD700
});
resourcesText.anchor.set(0, 0);
LK.gui.top.addChild(resourcesText);
resourcesText.x = 200;
resourcesText.y = 20;
var healthText = new Text2('Base Health: ' + baseHealth, {
size: 60,
fill: 0xFF0000
});
healthText.anchor.set(0, 0);
LK.gui.top.addChild(healthText);
healthText.x = 200;
healthText.y = 90;
var waveText = new Text2('Wave: ' + currentWave, {
size: 60,
fill: 0xFFFFFF
});
waveText.anchor.set(0, 0);
LK.gui.top.addChild(waveText);
waveText.x = 200;
waveText.y = 160;
// Tower selection buttons
var archerButton = LK.gui.bottomLeft.addChild(LK.getAsset('archerTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
archerButton.x = 80;
archerButton.y = -80;
var mageButton = LK.gui.bottomLeft.addChild(LK.getAsset('mageTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
mageButton.x = 200;
mageButton.y = -80;
var warriorButton = LK.gui.bottomLeft.addChild(LK.getAsset('warriorTower', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
}));
warriorButton.x = 320;
warriorButton.y = -80;
// Tower cost labels
var archerCost = new Text2('30', {
size: 30,
fill: 0xFFFFFF
});
archerCost.anchor.set(0.5, 0);
LK.gui.bottomLeft.addChild(archerCost);
archerCost.x = 80;
archerCost.y = -40;
var mageCost = new Text2('60', {
size: 30,
fill: 0xFFFFFF
});
mageCost.anchor.set(0.5, 0);
LK.gui.bottomLeft.addChild(mageCost);
mageCost.x = 200;
mageCost.y = -40;
var warriorCost = new Text2('80', {
size: 30,
fill: 0xFFFFFF
});
warriorCost.anchor.set(0.5, 0);
LK.gui.bottomLeft.addChild(warriorCost);
warriorCost.x = 320;
warriorCost.y = -40;
// Start wave button
var startWaveButton = new Text2('START WAVE', {
size: 50,
fill: 0x00FF00
});
startWaveButton.anchor.set(0.5, 0);
LK.gui.bottomRight.addChild(startWaveButton);
startWaveButton.x = -150;
startWaveButton.y = -80;
// Game functions
function spawnEnemy() {
var enemyTypes = ['goblin', 'orc', 'troll'];
var randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
// Increase difficulty with wave number
if (currentWave > 3) {
randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
} else if (currentWave > 1) {
randomType = enemyTypes[Math.floor(Math.random() * 2)]; // No trolls in early waves
} else {
randomType = 'goblin'; // Only goblins in first wave
}
var enemy = new Enemy(randomType, 0);
enemy.x = gamePath[0].x;
enemy.y = gamePath[0].y;
enemies.push(enemy);
game.addChild(enemy);
enemiesSpawned++;
}
function updateUI() {
resourcesText.setText('Resources: ' + gameResources);
healthText.setText('Base Health: ' + baseHealth);
waveText.setText('Wave: ' + currentWave);
// Update tower selection UI
updateTowerSelectionUI();
}
function getTowerCost(type) {
if (type === 'archer') {
return 30;
}
if (type === 'mage') {
return 60;
}
if (type === 'warrior') {
return 80;
}
return 50;
}
function placeTower(slotIndex, towerType) {
var slot = towerSlots[slotIndex];
if (!slot.isEmpty) {
return false;
}
var cost = getTowerCost(towerType);
if (gameResources < cost) {
return false;
}
var tower = new Tower(towerType, slot.x, slot.y);
towers.push(tower);
game.addChild(tower);
gameResources -= cost;
slot.isEmpty = false;
slot.alpha = 0.3;
LK.getSound('towerPlace').play();
updateUI();
return true;
}
function startWave() {
if (waveInProgress) {
return;
}
waveInProgress = true;
enemiesSpawned = 0;
enemiesInWave = 3 + currentWave * 2; // Increase enemies per wave
startWaveButton.setText('WAVE ' + currentWave);
startWaveButton.tint = 0x888888;
// Change path for each wave
var pathIndex = (currentWave - 1) % allPaths.length;
gamePath = allPaths[pathIndex];
redrawPath();
// Recreate tower slots for new wave
createTowerSlots();
}
function endWave() {
waveInProgress = false;
currentWave++;
gameResources += 20 + currentWave * 5; // Bonus resources between waves
startWaveButton.setText('START WAVE');
startWaveButton.tint = 0x00FF00;
updateUI();
}
// Event handlers
game.down = function (x, y, obj) {
if (gameOver) {
return;
}
// Check tower slot clicks
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
var distance = Math.sqrt(Math.pow(slot.x - x, 2) + Math.pow(slot.y - y, 2));
if (distance < 50 && slot.isEmpty) {
placeTower(i, selectedTowerType);
return;
}
}
};
// Button click handlers
archerButton.down = function () {
if (gameResources >= 30) {
selectedTowerType = 'archer';
updateTowerSelectionUI();
}
};
mageButton.down = function () {
if (gameResources >= 60) {
selectedTowerType = 'mage';
updateTowerSelectionUI();
}
};
warriorButton.down = function () {
if (gameResources >= 80) {
selectedTowerType = 'warrior';
updateTowerSelectionUI();
}
};
// Function to update tower selection UI
function updateTowerSelectionUI() {
// Reset all button colors
archerButton.tint = gameResources >= 30 ? 0xFFFFFF : 0x888888;
mageButton.tint = gameResources >= 60 ? 0xFFFFFF : 0x888888;
warriorButton.tint = gameResources >= 80 ? 0xFFFFFF : 0x888888;
// Highlight selected tower
if (selectedTowerType === 'archer') {
archerButton.tint = 0x00FF00;
} else if (selectedTowerType === 'mage') {
mageButton.tint = 0x00FF00;
} else if (selectedTowerType === 'warrior') {
warriorButton.tint = 0x00FF00;
}
}
startWaveButton.down = function () {
if (!waveInProgress) {
startWave();
}
};
// Main game loop
game.update = function () {
if (gameOver) {
return;
}
// Spawn enemies during wave
if (waveInProgress && enemiesSpawned < enemiesInWave) {
if (LK.ticks % 60 === 0) {
// Spawn every second
spawnEnemy();
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.isDead) {
gameResources += enemy.resources;
enemy.destroy();
enemies.splice(i, 1);
continue;
}
if (enemy.reachedBase) {
baseHealth -= 10;
enemy.destroy();
enemies.splice(i, 1);
if (baseHealth <= 0) {
gameOver = true;
LK.showGameOver();
return;
}
continue;
}
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var projectile = projectiles[i];
if (projectile.hasHit) {
// Check for enemy hits
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
if (!enemy.isDead && enemy.intersects(projectile)) {
enemy.takeDamage(projectile.damage);
break;
}
}
projectile.destroy();
projectiles.splice(i, 1);
}
}
// Check if wave is complete
if (waveInProgress && enemiesSpawned >= enemiesInWave && enemies.length === 0) {
endWave();
}
// Win condition
if (currentWave > 10) {
LK.showYouWin();
return;
}
updateUI();
};
// Selected tower preview
var selectedTowerPreview = null;
// Initialize selected tower
archerButton.tint = 0x00FF00;
updateUI();
// Show preview of selected tower when hovering over valid slots
game.move = function (x, y, obj) {
if (gameOver) {
return;
}
// Remove existing preview
if (selectedTowerPreview) {
selectedTowerPreview.destroy();
selectedTowerPreview = null;
}
// Check if hovering over a valid tower slot
for (var i = 0; i < towerSlots.length; i++) {
var slot = towerSlots[i];
var distance = Math.sqrt(Math.pow(slot.x - x, 2) + Math.pow(slot.y - y, 2));
if (slot.isEmpty && distance < 50) {
var cost = getTowerCost(selectedTowerType);
if (gameResources >= cost) {
// Create preview of selected tower
if (selectedTowerType === 'archer') {
selectedTowerPreview = game.addChild(LK.getAsset('archerTower', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
} else if (selectedTowerType === 'mage') {
selectedTowerPreview = game.addChild(LK.getAsset('mageTower', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
} else if (selectedTowerType === 'warrior') {
selectedTowerPreview = game.addChild(LK.getAsset('warriorTower', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
}));
}
if (selectedTowerPreview) {
selectedTowerPreview.x = slot.x;
selectedTowerPreview.y = slot.y;
selectedTowerPreview.tint = 0x00FF00; // Green tint for valid placement
}
// Highlight the slot
slot.tint = 0x00FF00;
slot.alpha = 1.0;
}
break;
} else {
// Reset slot appearance
slot.tint = 0xFFFFFF;
slot.alpha = 0.7;
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,1087 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+var Enemy = Container.expand(function (type, pathIndex) {
+ var self = Container.call(this);
+ self.type = type;
+ self.pathIndex = pathIndex || 0;
+ self.speed = 2;
+ self.maxHealth = 100;
+ self.health = self.maxHealth;
+ self.resources = 10;
+ self.isDead = false;
+ // Set enemy properties based on type
+ if (type === 'goblin') {
+ self.speed = 3;
+ self.maxHealth = 50;
+ self.health = 50;
+ self.resources = 5;
+ self.enemyGraphics = self.attachAsset('goblin', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (type === 'orc') {
+ self.speed = 2;
+ self.maxHealth = 100;
+ self.health = 100;
+ self.resources = 10;
+ self.enemyGraphics = self.attachAsset('orc', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (type === 'troll') {
+ self.speed = 1;
+ self.maxHealth = 200;
+ self.health = 200;
+ self.resources = 20;
+ self.enemyGraphics = self.attachAsset('troll', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ // Health bar
+ self.healthBar = self.addChild(LK.getAsset('pathTile', {
+ width: 40,
+ height: 6,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ tint: 0x00FF00
+ }));
+ self.healthBar.y = -40;
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ var healthPercent = self.health / self.maxHealth;
+ self.healthBar.width = 40 * healthPercent;
+ if (healthPercent > 0.6) {
+ self.healthBar.tint = 0x00FF00;
+ } else if (healthPercent > 0.3) {
+ self.healthBar.tint = 0xFFFF00;
+ } else {
+ self.healthBar.tint = 0xFF0000;
+ }
+ if (self.health <= 0) {
+ self.isDead = true;
+ LK.getSound('enemyDeath').play();
+ return true;
+ }
+ LK.getSound('enemyHit').play();
+ return false;
+ };
+ self.update = function () {
+ if (self.isDead) {
+ return;
+ }
+ // Move along path
+ if (self.pathIndex < gamePath.length - 1) {
+ var currentTarget = gamePath[self.pathIndex + 1];
+ var dx = currentTarget.x - self.x;
+ var dy = currentTarget.y - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 10) {
+ self.pathIndex++;
+ if (self.pathIndex >= gamePath.length - 1) {
+ // Reached base
+ self.reachedBase = true;
+ }
+ } else {
+ self.x += dx / distance * self.speed;
+ self.y += dy / distance * self.speed;
+ }
+ }
+ };
+ return self;
+});
+var Projectile = Container.expand(function (type, startX, startY, targetX, targetY, damage) {
+ var self = Container.call(this);
+ self.type = type;
+ self.damage = damage;
+ self.speed = 8;
+ self.targetX = targetX;
+ self.targetY = targetY;
+ self.hasHit = false;
+ self.x = startX;
+ self.y = startY;
+ if (type === 'arrow') {
+ self.projectileGraphics = self.attachAsset('arrow', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (type === 'fireball') {
+ self.projectileGraphics = self.attachAsset('fireball', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ } else if (type === 'sword') {
+ self.projectileGraphics = self.attachAsset('sword', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ }
+ // Calculate direction
+ var dx = targetX - startX;
+ var dy = targetY - startY;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ self.dirX = dx / distance;
+ self.dirY = dy / distance;
+ self.update = function () {
+ if (self.hasHit) {
+ return;
+ }
+ self.x += self.dirX * self.speed;
+ self.y += self.dirY * self.speed;
+ // Check if reached target area
+ var distanceToTarget = Math.sqrt(Math.pow(self.x - self.targetX, 2) + Math.pow(self.y - self.targetY, 2));
+ if (distanceToTarget < 20) {
+ self.hasHit = true;
+ }
+ // Remove if off screen
+ if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
+ self.hasHit = true;
+ }
+ };
+ return self;
+});
+var Tower = Container.expand(function (type, slotX, slotY) {
+ var self = Container.call(this);
+ self.type = type;
+ self.level = 1;
+ self.range = 150;
+ self.damage = 25;
+ self.attackSpeed = 60; // frames between attacks
+ self.lastAttack = 0;
+ self.cost = 50;
+ self.x = slotX;
+ self.y = slotY;
+ if (type === 'archer') {
+ self.towerGraphics = self.attachAsset('archerTower', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.projectileType = 'arrow';
+ self.range = 180;
+ self.damage = 20;
+ self.attackSpeed = 40;
+ self.cost = 30;
+ } else if (type === 'mage') {
+ self.towerGraphics = self.attachAsset('mageTower', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.projectileType = 'fireball';
+ self.range = 120;
+ self.damage = 40;
+ self.attackSpeed = 80;
+ self.cost = 60;
+ } else if (type === 'warrior') {
+ self.towerGraphics = self.attachAsset('warriorTower', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.projectileType = 'sword';
+ self.range = 100;
+ self.damage = 60;
+ self.attackSpeed = 100;
+ self.cost = 80;
+ self.armyCooldown = 60 * 60; // 60 seconds at 60 FPS
+ self.lastArmyRelease = 0;
+ }
+ // Range indicator (invisible by default)
+ self.rangeIndicator = self.addChild(LK.getAsset('pathTile', {
+ width: self.range * 2,
+ height: self.range * 2,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0,
+ tint: 0xFFFFFF
+ }));
+ self.findTarget = function () {
+ var closestEnemy = null;
+ var closestDistance = Infinity;
+ for (var i = 0; i < enemies.length; i++) {
+ var enemy = enemies[i];
+ if (enemy.isDead) {
+ continue;
+ }
+ var distance = Math.sqrt(Math.pow(enemy.x - self.x, 2) + Math.pow(enemy.y - self.y, 2));
+ if (distance <= self.range && distance < closestDistance) {
+ closestDistance = distance;
+ closestEnemy = enemy;
+ }
+ }
+ return closestEnemy;
+ };
+ self.releaseArmy = function () {
+ // Clear all enemies on the path
+ for (var i = enemies.length - 1; i >= 0; i--) {
+ var enemy = enemies[i];
+ if (!enemy.isDead) {
+ // Add visual effect before destroying
+ tween(enemy, {
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 500,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ if (enemy && !enemy.isDead) {
+ gameResources += enemy.resources;
+ gameResources += 10; // Bonus 10 coins for warrior tower kills
+ enemy.isDead = true;
+ enemy.destroy();
+ enemies.splice(enemies.indexOf(enemy), 1);
+ }
+ }
+ });
+ }
+ }
+ // Visual effect on tower
+ tween(self.towerGraphics, {
+ tint: 0xFFD700
+ }, {
+ duration: 200,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(self.towerGraphics, {
+ tint: 0xFFFFFF
+ }, {
+ duration: 800,
+ easing: tween.easeIn
+ });
+ }
+ });
+ LK.getSound('shoot').play();
+ };
+ self.attack = function (target) {
+ if (self.type === 'warrior') {
+ // Check if army cooldown is ready
+ if (LK.ticks - self.lastArmyRelease >= self.armyCooldown) {
+ self.releaseArmy();
+ self.lastArmyRelease = LK.ticks;
+ }
+ return;
+ }
+ if (LK.ticks - self.lastAttack < self.attackSpeed) {
+ return;
+ }
+ var projectile = new Projectile(self.projectileType, self.x, self.y, target.x, target.y, self.damage);
+ projectiles.push(projectile);
+ game.addChild(projectile);
+ self.lastAttack = LK.ticks;
+ LK.getSound('shoot').play();
+ };
+ self.update = function () {
+ var target = self.findTarget();
+ if (target) {
+ self.attack(target);
+ }
+ // Update warrior tower cooldown indicator
+ if (self.type === 'warrior') {
+ var cooldownRemaining = self.armyCooldown - (LK.ticks - self.lastArmyRelease);
+ if (cooldownRemaining > 0) {
+ var cooldownPercent = cooldownRemaining / self.armyCooldown;
+ self.towerGraphics.tint = 0x888888 + Math.floor((1 - cooldownPercent) * 0x777777);
+ } else {
+ self.towerGraphics.tint = 0xFFFFFF;
+ }
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x444444
-});
\ No newline at end of file
+ backgroundColor: 0x2F4F2F
+});
+
+/****
+* Game Code
+****/
+// Game state variables
+// Tower Assets
+// Enemy Assets
+// Projectile Assets
+// UI Assets
+// Sound Assets
+var gameResources = 100;
+var baseHealth = 100;
+var currentWave = 1;
+var enemiesInWave = 5;
+var enemiesSpawned = 0;
+var waveInProgress = false;
+var gameOver = false;
+var selectedTowerType = 'archer';
+// Game arrays
+var enemies = [];
+var towers = [];
+var projectiles = [];
+var towerSlots = [];
+// Different paths for different waves
+var allPaths = [
+// Path 1 - Straight horizontal then vertical
+[{
+ x: 100,
+ y: 100
+}, {
+ x: 300,
+ y: 100
+}, {
+ x: 300,
+ y: 300
+}, {
+ x: 600,
+ y: 300
+}, {
+ x: 600,
+ y: 500
+}, {
+ x: 900,
+ y: 500
+}, {
+ x: 900,
+ y: 700
+}, {
+ x: 1200,
+ y: 700
+}, {
+ x: 1200,
+ y: 900
+}, {
+ x: 1500,
+ y: 900
+}, {
+ x: 1500,
+ y: 1100
+}, {
+ x: 1800,
+ y: 1100
+}],
+// Path 2 - Zigzag pattern
+[{
+ x: 100,
+ y: 200
+}, {
+ x: 500,
+ y: 200
+}, {
+ x: 500,
+ y: 400
+}, {
+ x: 200,
+ y: 400
+}, {
+ x: 200,
+ y: 600
+}, {
+ x: 800,
+ y: 600
+}, {
+ x: 800,
+ y: 800
+}, {
+ x: 400,
+ y: 800
+}, {
+ x: 400,
+ y: 1000
+}, {
+ x: 1200,
+ y: 1000
+}, {
+ x: 1200,
+ y: 1200
+}, {
+ x: 1800,
+ y: 1200
+}],
+// Path 3 - Curved path
+[{
+ x: 100,
+ y: 300
+}, {
+ x: 400,
+ y: 300
+}, {
+ x: 400,
+ y: 150
+}, {
+ x: 700,
+ y: 150
+}, {
+ x: 700,
+ y: 450
+}, {
+ x: 1000,
+ y: 450
+}, {
+ x: 1000,
+ y: 200
+}, {
+ x: 1300,
+ y: 200
+}, {
+ x: 1300,
+ y: 700
+}, {
+ x: 1600,
+ y: 700
+}, {
+ x: 1600,
+ y: 1000
+}, {
+ x: 1800,
+ y: 1000
+}],
+// Path 4 - Long winding path
+[{
+ x: 100,
+ y: 400
+}, {
+ x: 200,
+ y: 400
+}, {
+ x: 200,
+ y: 200
+}, {
+ x: 600,
+ y: 200
+}, {
+ x: 600,
+ y: 600
+}, {
+ x: 400,
+ y: 600
+}, {
+ x: 400,
+ y: 800
+}, {
+ x: 1000,
+ y: 800
+}, {
+ x: 1000,
+ y: 400
+}, {
+ x: 1400,
+ y: 400
+}, {
+ x: 1400,
+ y: 1200
+}, {
+ x: 1800,
+ y: 1200
+}]];
+// Current game path - changes each wave
+var gamePath = allPaths[0];
+// Path tiles array to track them
+var pathTiles = [];
+// Create path tiles
+function createPathTiles() {
+ for (var i = 0; i < gamePath.length; i++) {
+ var pathTile = game.addChild(LK.getAsset('pathTile', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.3
+ }));
+ pathTile.x = gamePath[i].x;
+ pathTile.y = gamePath[i].y;
+ pathTiles.push(pathTile);
+ }
+}
+// Function to redraw path for new waves
+function redrawPath() {
+ // Remove old path tiles
+ for (var i = 0; i < pathTiles.length; i++) {
+ pathTiles[i].destroy();
+ }
+ pathTiles = [];
+ // Create new path tiles
+ createPathTiles();
+ // Update base position to end of new path
+ base.x = gamePath[gamePath.length - 1].x;
+ base.y = gamePath[gamePath.length - 1].y;
+}
+// Initialize path tiles
+createPathTiles();
+// Create base at end of path
+var base = game.addChild(LK.getAsset('base', {
+ anchorX: 0.5,
+ anchorY: 0.5
+}));
+base.x = gamePath[gamePath.length - 1].x;
+base.y = gamePath[gamePath.length - 1].y;
+// Create tower slots around the path
+var baseSlotPositions = [{
+ x: 200,
+ y: 200
+}, {
+ x: 400,
+ y: 200
+}, {
+ x: 500,
+ y: 400
+}, {
+ x: 700,
+ y: 400
+}, {
+ x: 800,
+ y: 600
+}, {
+ x: 1000,
+ y: 600
+}, {
+ x: 1100,
+ y: 800
+}, {
+ x: 1300,
+ y: 800
+}, {
+ x: 1400,
+ y: 1000
+}, {
+ x: 1600,
+ y: 1000
+}, {
+ x: 1700,
+ y: 1200
+}];
+// Additional slots for wave 2 and higher
+var extraSlotPositions = [{
+ x: 150,
+ y: 350
+}, {
+ x: 350,
+ y: 550
+}, {
+ x: 650,
+ y: 350
+}, {
+ x: 850,
+ y: 450
+}, {
+ x: 1050,
+ y: 350
+}, {
+ x: 1250,
+ y: 550
+}, {
+ x: 1450,
+ y: 650
+}, {
+ x: 1550,
+ y: 850
+}];
+function createTowerSlots() {
+ // Clear existing slots
+ for (var i = 0; i < towerSlots.length; i++) {
+ towerSlots[i].destroy();
+ }
+ towerSlots = [];
+ // Always create base slots
+ var slotsToCreate = baseSlotPositions.slice();
+ // Add extra slots for wave 2 and higher
+ if (currentWave >= 2) {
+ slotsToCreate = slotsToCreate.concat(extraSlotPositions);
+ }
+ for (var i = 0; i < slotsToCreate.length; i++) {
+ var slot = game.addChild(LK.getAsset('towerSlot', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ }));
+ slot.x = slotsToCreate[i].x;
+ slot.y = slotsToCreate[i].y;
+ slot.isEmpty = true;
+ slot.slotIndex = i;
+ towerSlots.push(slot);
+ }
+}
+// Initialize tower slots
+createTowerSlots();
+// Create decorative trees and bushes
+var decorativeElements = [];
+var treePositions = [{
+ x: 50,
+ y: 50
+}, {
+ x: 150,
+ y: 80
+}, {
+ x: 250,
+ y: 120
+}, {
+ x: 1850,
+ y: 100
+}, {
+ x: 1750,
+ y: 150
+}, {
+ x: 1650,
+ y: 80
+}, {
+ x: 80,
+ y: 800
+}, {
+ x: 180,
+ y: 950
+}, {
+ x: 120,
+ y: 1100
+}, {
+ x: 1880,
+ y: 1400
+}, {
+ x: 1780,
+ y: 1500
+}, {
+ x: 1820,
+ y: 1600
+}, {
+ x: 950,
+ y: 50
+}, {
+ x: 1050,
+ y: 80
+}, {
+ x: 850,
+ y: 120
+}, {
+ x: 50,
+ y: 1800
+}, {
+ x: 150,
+ y: 1900
+}, {
+ x: 250,
+ y: 2000
+}];
+var bushPositions = [{
+ x: 350,
+ y: 80
+}, {
+ x: 450,
+ y: 120
+}, {
+ x: 550,
+ y: 90
+}, {
+ x: 1550,
+ y: 200
+}, {
+ x: 1650,
+ y: 180
+}, {
+ x: 1750,
+ y: 220
+}, {
+ x: 80,
+ y: 1200
+}, {
+ x: 180,
+ y: 1300
+}, {
+ x: 120,
+ y: 1400
+}, {
+ x: 1880,
+ y: 1800
+}, {
+ x: 1780,
+ y: 1900
+}, {
+ x: 1820,
+ y: 2000
+}, {
+ x: 750,
+ y: 100
+}, {
+ x: 850,
+ y: 180
+}, {
+ x: 950,
+ y: 140
+}, {
+ x: 1100,
+ y: 1800
+}, {
+ x: 1200,
+ y: 1900
+}, {
+ x: 1300,
+ y: 2000
+}, {
+ x: 300,
+ y: 1600
+}, {
+ x: 400,
+ y: 1700
+}, {
+ x: 500,
+ y: 1800
+}];
+// Create trees
+for (var i = 0; i < treePositions.length; i++) {
+ var tree = game.addChild(LK.getAsset('tree', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.8
+ }));
+ tree.x = treePositions[i].x;
+ tree.y = treePositions[i].y;
+ decorativeElements.push(tree);
+}
+// Create bushes
+for (var i = 0; i < bushPositions.length; i++) {
+ var bush = game.addChild(LK.getAsset('bush', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.7
+ }));
+ bush.x = bushPositions[i].x;
+ bush.y = bushPositions[i].y;
+ decorativeElements.push(bush);
+}
+// UI Elements
+var resourcesText = new Text2('Resources: ' + gameResources, {
+ size: 60,
+ fill: 0xFFD700
+});
+resourcesText.anchor.set(0, 0);
+LK.gui.top.addChild(resourcesText);
+resourcesText.x = 200;
+resourcesText.y = 20;
+var healthText = new Text2('Base Health: ' + baseHealth, {
+ size: 60,
+ fill: 0xFF0000
+});
+healthText.anchor.set(0, 0);
+LK.gui.top.addChild(healthText);
+healthText.x = 200;
+healthText.y = 90;
+var waveText = new Text2('Wave: ' + currentWave, {
+ size: 60,
+ fill: 0xFFFFFF
+});
+waveText.anchor.set(0, 0);
+LK.gui.top.addChild(waveText);
+waveText.x = 200;
+waveText.y = 160;
+// Tower selection buttons
+var archerButton = LK.gui.bottomLeft.addChild(LK.getAsset('archerTower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.8,
+ scaleY: 0.8
+}));
+archerButton.x = 80;
+archerButton.y = -80;
+var mageButton = LK.gui.bottomLeft.addChild(LK.getAsset('mageTower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.8,
+ scaleY: 0.8
+}));
+mageButton.x = 200;
+mageButton.y = -80;
+var warriorButton = LK.gui.bottomLeft.addChild(LK.getAsset('warriorTower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ scaleX: 0.8,
+ scaleY: 0.8
+}));
+warriorButton.x = 320;
+warriorButton.y = -80;
+// Tower cost labels
+var archerCost = new Text2('30', {
+ size: 30,
+ fill: 0xFFFFFF
+});
+archerCost.anchor.set(0.5, 0);
+LK.gui.bottomLeft.addChild(archerCost);
+archerCost.x = 80;
+archerCost.y = -40;
+var mageCost = new Text2('60', {
+ size: 30,
+ fill: 0xFFFFFF
+});
+mageCost.anchor.set(0.5, 0);
+LK.gui.bottomLeft.addChild(mageCost);
+mageCost.x = 200;
+mageCost.y = -40;
+var warriorCost = new Text2('80', {
+ size: 30,
+ fill: 0xFFFFFF
+});
+warriorCost.anchor.set(0.5, 0);
+LK.gui.bottomLeft.addChild(warriorCost);
+warriorCost.x = 320;
+warriorCost.y = -40;
+// Start wave button
+var startWaveButton = new Text2('START WAVE', {
+ size: 50,
+ fill: 0x00FF00
+});
+startWaveButton.anchor.set(0.5, 0);
+LK.gui.bottomRight.addChild(startWaveButton);
+startWaveButton.x = -150;
+startWaveButton.y = -80;
+// Game functions
+function spawnEnemy() {
+ var enemyTypes = ['goblin', 'orc', 'troll'];
+ var randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
+ // Increase difficulty with wave number
+ if (currentWave > 3) {
+ randomType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
+ } else if (currentWave > 1) {
+ randomType = enemyTypes[Math.floor(Math.random() * 2)]; // No trolls in early waves
+ } else {
+ randomType = 'goblin'; // Only goblins in first wave
+ }
+ var enemy = new Enemy(randomType, 0);
+ enemy.x = gamePath[0].x;
+ enemy.y = gamePath[0].y;
+ enemies.push(enemy);
+ game.addChild(enemy);
+ enemiesSpawned++;
+}
+function updateUI() {
+ resourcesText.setText('Resources: ' + gameResources);
+ healthText.setText('Base Health: ' + baseHealth);
+ waveText.setText('Wave: ' + currentWave);
+ // Update tower selection UI
+ updateTowerSelectionUI();
+}
+function getTowerCost(type) {
+ if (type === 'archer') {
+ return 30;
+ }
+ if (type === 'mage') {
+ return 60;
+ }
+ if (type === 'warrior') {
+ return 80;
+ }
+ return 50;
+}
+function placeTower(slotIndex, towerType) {
+ var slot = towerSlots[slotIndex];
+ if (!slot.isEmpty) {
+ return false;
+ }
+ var cost = getTowerCost(towerType);
+ if (gameResources < cost) {
+ return false;
+ }
+ var tower = new Tower(towerType, slot.x, slot.y);
+ towers.push(tower);
+ game.addChild(tower);
+ gameResources -= cost;
+ slot.isEmpty = false;
+ slot.alpha = 0.3;
+ LK.getSound('towerPlace').play();
+ updateUI();
+ return true;
+}
+function startWave() {
+ if (waveInProgress) {
+ return;
+ }
+ waveInProgress = true;
+ enemiesSpawned = 0;
+ enemiesInWave = 3 + currentWave * 2; // Increase enemies per wave
+ startWaveButton.setText('WAVE ' + currentWave);
+ startWaveButton.tint = 0x888888;
+ // Change path for each wave
+ var pathIndex = (currentWave - 1) % allPaths.length;
+ gamePath = allPaths[pathIndex];
+ redrawPath();
+ // Recreate tower slots for new wave
+ createTowerSlots();
+}
+function endWave() {
+ waveInProgress = false;
+ currentWave++;
+ gameResources += 20 + currentWave * 5; // Bonus resources between waves
+ startWaveButton.setText('START WAVE');
+ startWaveButton.tint = 0x00FF00;
+ updateUI();
+}
+// Event handlers
+game.down = function (x, y, obj) {
+ if (gameOver) {
+ return;
+ }
+ // Check tower slot clicks
+ for (var i = 0; i < towerSlots.length; i++) {
+ var slot = towerSlots[i];
+ var distance = Math.sqrt(Math.pow(slot.x - x, 2) + Math.pow(slot.y - y, 2));
+ if (distance < 50 && slot.isEmpty) {
+ placeTower(i, selectedTowerType);
+ return;
+ }
+ }
+};
+// Button click handlers
+archerButton.down = function () {
+ if (gameResources >= 30) {
+ selectedTowerType = 'archer';
+ updateTowerSelectionUI();
+ }
+};
+mageButton.down = function () {
+ if (gameResources >= 60) {
+ selectedTowerType = 'mage';
+ updateTowerSelectionUI();
+ }
+};
+warriorButton.down = function () {
+ if (gameResources >= 80) {
+ selectedTowerType = 'warrior';
+ updateTowerSelectionUI();
+ }
+};
+// Function to update tower selection UI
+function updateTowerSelectionUI() {
+ // Reset all button colors
+ archerButton.tint = gameResources >= 30 ? 0xFFFFFF : 0x888888;
+ mageButton.tint = gameResources >= 60 ? 0xFFFFFF : 0x888888;
+ warriorButton.tint = gameResources >= 80 ? 0xFFFFFF : 0x888888;
+ // Highlight selected tower
+ if (selectedTowerType === 'archer') {
+ archerButton.tint = 0x00FF00;
+ } else if (selectedTowerType === 'mage') {
+ mageButton.tint = 0x00FF00;
+ } else if (selectedTowerType === 'warrior') {
+ warriorButton.tint = 0x00FF00;
+ }
+}
+startWaveButton.down = function () {
+ if (!waveInProgress) {
+ startWave();
+ }
+};
+// Main game loop
+game.update = function () {
+ if (gameOver) {
+ return;
+ }
+ // Spawn enemies during wave
+ if (waveInProgress && enemiesSpawned < enemiesInWave) {
+ if (LK.ticks % 60 === 0) {
+ // Spawn every second
+ spawnEnemy();
+ }
+ }
+ // Update enemies
+ for (var i = enemies.length - 1; i >= 0; i--) {
+ var enemy = enemies[i];
+ if (enemy.isDead) {
+ gameResources += enemy.resources;
+ enemy.destroy();
+ enemies.splice(i, 1);
+ continue;
+ }
+ if (enemy.reachedBase) {
+ baseHealth -= 10;
+ enemy.destroy();
+ enemies.splice(i, 1);
+ if (baseHealth <= 0) {
+ gameOver = true;
+ LK.showGameOver();
+ return;
+ }
+ continue;
+ }
+ }
+ // Update projectiles
+ for (var i = projectiles.length - 1; i >= 0; i--) {
+ var projectile = projectiles[i];
+ if (projectile.hasHit) {
+ // Check for enemy hits
+ for (var j = 0; j < enemies.length; j++) {
+ var enemy = enemies[j];
+ if (!enemy.isDead && enemy.intersects(projectile)) {
+ enemy.takeDamage(projectile.damage);
+ break;
+ }
+ }
+ projectile.destroy();
+ projectiles.splice(i, 1);
+ }
+ }
+ // Check if wave is complete
+ if (waveInProgress && enemiesSpawned >= enemiesInWave && enemies.length === 0) {
+ endWave();
+ }
+ // Win condition
+ if (currentWave > 10) {
+ LK.showYouWin();
+ return;
+ }
+ updateUI();
+};
+// Selected tower preview
+var selectedTowerPreview = null;
+// Initialize selected tower
+archerButton.tint = 0x00FF00;
+updateUI();
+// Show preview of selected tower when hovering over valid slots
+game.move = function (x, y, obj) {
+ if (gameOver) {
+ return;
+ }
+ // Remove existing preview
+ if (selectedTowerPreview) {
+ selectedTowerPreview.destroy();
+ selectedTowerPreview = null;
+ }
+ // Check if hovering over a valid tower slot
+ for (var i = 0; i < towerSlots.length; i++) {
+ var slot = towerSlots[i];
+ var distance = Math.sqrt(Math.pow(slot.x - x, 2) + Math.pow(slot.y - y, 2));
+ if (slot.isEmpty && distance < 50) {
+ var cost = getTowerCost(selectedTowerType);
+ if (gameResources >= cost) {
+ // Create preview of selected tower
+ if (selectedTowerType === 'archer') {
+ selectedTowerPreview = game.addChild(LK.getAsset('archerTower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.6
+ }));
+ } else if (selectedTowerType === 'mage') {
+ selectedTowerPreview = game.addChild(LK.getAsset('mageTower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.6
+ }));
+ } else if (selectedTowerType === 'warrior') {
+ selectedTowerPreview = game.addChild(LK.getAsset('warriorTower', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0.6
+ }));
+ }
+ if (selectedTowerPreview) {
+ selectedTowerPreview.x = slot.x;
+ selectedTowerPreview.y = slot.y;
+ selectedTowerPreview.tint = 0x00FF00; // Green tint for valid placement
+ }
+ // Highlight the slot
+ slot.tint = 0x00FF00;
+ slot.alpha = 1.0;
+ }
+ break;
+ } else {
+ // Reset slot appearance
+ slot.tint = 0xFFFFFF;
+ slot.alpha = 0.7;
+ }
+ }
+};
\ No newline at end of file