/****
* 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;
}
}
}; /****
* 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;
}
}
};