User prompt
Add wave 4 after wave 3 is finished same for other waves check always when boss is dead.
User prompt
Add wave 4 as wave 1, wave 5 as wave 2, wave 6 as wave 3, but they are hard hard a bit. Change color healthbar of enemies to yellow.
User prompt
the wave 3 text appeared with wave 2 text fix it!
User prompt
the healthbar of serpent1 is gone!
User prompt
Add health bar for serpent2. Add wave 3 mix with serpent1 and serpent2 with 4 big enemies & 2 bosses.
User prompt
Do wave 2 blinking animation then start respawning enemies of serpent2
User prompt
Show the wave text in the middle of the screen blinking then start respawning the enemies. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
start wave 02 when wave 01 boss is killed
User prompt
Add orange text of waves with blinking animation when waves starts βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add serpent2 as enemy of wave 02. make the 2 big enemies in first wave by serpent1 only, and same for wave 02 by serpent2 only.
User prompt
Slow the waves
User prompt
Make each wave have 20 enemies, wave 01 enemy is serpent1, wave 02 enemy serpent2, make big enemies of the serpent1 and serpent2 at 18 and 19 enemies the 20th is the boss.
User prompt
Make each wave have 20 enemies, wave 01 enemy is serpent1, wave 02 enemy serpent2, make big enemies of the serpent1 and serpent2 at 18 and 19 enemies the 20th is the boss.
User prompt
rename 'enemy' asset 'Serpent1'
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Reorder towers with their uiButtons backgrounds
User prompt
make the towers buttons to be appearing before the uiButtons
User prompt
make the towers buttons after the uiButtons
User prompt
uiButtons are hiding the towers they can't be seen to buy it fix them!
User prompt
The towers appearing behind the UiButtons fix that!
User prompt
Add rotation for the star shot βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
fix the pathTile its shuffled with buildabletile!
User prompt
There is below and above the road a double buildabletile!
User prompt
remove the unessecery BuildableTile let only around the road from the sides.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Enemy = Container.expand(function (path) { var self = Container.call(this); self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.speed = 3; self.path = path; self.currentWaypointIndex = 1; self.value = 10; // Score awarded on defeat // Create health bar self.healthBar = self.addChild(LK.getAsset('healthBar', { anchorX: 0.5, anchorY: 0.5 })); self.healthBar.y = -80; // Position above enemy self.update = function () { if (self.currentWaypointIndex >= self.path.length) { // Reached the end LK.showGameOver(); self.destroy(); // Self-destruct to avoid multiple game overs return; } var targetWaypoint = self.path[self.currentWaypointIndex]; var dx = targetWaypoint.x - self.x; var dy = targetWaypoint.y - self.y; var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { self.currentWaypointIndex++; } }; self.takeDamage = function (amount) { self.health -= amount; // Update health bar width based on remaining health var healthPercentage = self.health / self.maxHealth; self.healthBar.scaleX = healthPercentage; if (self.health <= 0) { return true; // Is dead } LK.effects.flashObject(self, 0xffffff, 100); return false; // Is alive }; return self; }); var Projectile = Container.expand(function (start, target, damage, projectileType) { var self = Container.call(this); projectileType = projectileType || 'projectile'; self.attachAsset(projectileType, { anchorX: 0.5, anchorY: 0.5 }); self.x = start.x; self.y = start.y; self.target = target; self.damage = damage; self.speed = 20; self.update = function () { if (!self.target || self.target.health <= 0) { // Target is gone projectiles.splice(projectiles.indexOf(self), 1); self.destroy(); return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { // Hit target if (self.target.takeDamage(self.damage)) { // Enemy died LK.getSound('enemy_die').play(); LK.setScore(LK.getScore() + self.target.value); scoreTxt.setText('SCORE: ' + LK.getScore()); enemies.splice(enemies.indexOf(self.target), 1); self.target.destroy(); } projectiles.splice(projectiles.indexOf(self), 1); self.destroy(); } else { // Move towards target var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } }; return self; }); var Tower = Container.expand(function (config) { var self = Container.call(this); self.config = config; self.attachAsset(config.asset, { anchorX: 0.5, anchorY: 0.5 }); self.cost = config.cost; self.damage = config.damage; self.range = 400; // All towers have same range for now self.fireRate = config.fireRate; // shots per second self.cooldown = 0; self.isTower = true; self.down = function (x, y, obj) { // This makes the tower a "button" console.log("Tower clicked! Damage: " + self.damage); LK.effects.flashObject(self, 0xffffff, 200); }; self.update = function () { if (self.cooldown > 0) { self.cooldown--; return; } var target = findClosestEnemy(self.x, self.y, self.range); if (target) { fireProjectile(self, target); self.cooldown = 60 / self.fireRate; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1e272e }); /**** * Game Code ****/ // The engine will automatically create these assets based on their usage below. // --- Constants & Game State --- var MAP_WIDTH = 4000; var MAP_HEIGHT = 4800; var CELL_SIZE = 200; var GRID_WIDTH = MAP_WIDTH / CELL_SIZE; var GRID_HEIGHT = MAP_HEIGHT / CELL_SIZE; // Grid states: 0=empty, 1=path, 2=buildable, 3=occupied var grid = []; var pathWaypoints = [{ x: 2000 + CELL_SIZE / 2, y: -100 }, { x: 2000 + CELL_SIZE / 2, y: 800 + CELL_SIZE / 2 }, { x: 800 + CELL_SIZE / 2, y: 800 + CELL_SIZE / 2 }, { x: 800 + CELL_SIZE / 2, y: 1600 + CELL_SIZE / 2 }, { x: 3200 + CELL_SIZE / 2, y: 1600 + CELL_SIZE / 2 }, { x: 3200 + CELL_SIZE / 2, y: 2400 + CELL_SIZE / 2 }, { x: 1200 + CELL_SIZE / 2, y: 2400 + CELL_SIZE / 2 }, { x: 1200 + CELL_SIZE / 2, y: 3200 + CELL_SIZE / 2 }, { x: 2800 + CELL_SIZE / 2, y: 3200 + CELL_SIZE / 2 }, { x: 2800 + CELL_SIZE / 2, y: 4000 + CELL_SIZE / 2 }, { x: 2800 + CELL_SIZE / 2, y: 4900 }]; var enemies = []; var towers = []; var projectiles = []; var mapContainer = game.addChild(new Container()); mapContainer.width = MAP_WIDTH; mapContainer.height = MAP_HEIGHT; // Add background var gameBackground = mapContainer.addChild(LK.getAsset('gameBackground', { anchorX: 0, anchorY: 0 })); gameBackground.x = 0; gameBackground.y = 0; gameBackground.width = MAP_WIDTH; gameBackground.height = MAP_HEIGHT; var placingTowerConfig = null; var ghostTower = null; var isDraggingMap = false; var dragStart = { x: 0, y: 0 }; var mapStart = { x: 0, y: 0 }; // --- Tower Configurations --- var towerTypes = { '1': { asset: 'tower_1', cost: 50, damage: 25, fireRate: 1.5, projectileTypes: ['star'] }, '2': { asset: 'tower_2', cost: 100, damage: 60, fireRate: 1, projectileTypes: ['arrow'] }, '3': { asset: 'tower_3', cost: 150, damage: 100, fireRate: 0.75, projectileTypes: ['star', 'arrow'] } }; // --- UI --- LK.setScore(80); // Starting cash var scoreTxt = new Text2('SCORE: ' + LK.getScore(), { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 20; LK.gui.top.addChild(scoreTxt); var uiContainer = LK.gui.bottom.addChild(new Container()); uiContainer.y = -20; // Move it up slightly from the absolute bottom var towerButton1 = uiContainer.addChild(LK.getAsset('uiButton', { anchorX: 0.5, anchorY: 1 })); towerButton1.x = -300; var towerIcon1 = towerButton1.addChild(LK.getAsset('tower_1', { scaleX: 0.8, scaleY: 0.8, anchorX: 0.5, anchorY: 0.5, y: -110 })); towerIcon1.down = function (x, y, obj) { startPlacingTower('1'); if (placingTowerConfig) { towerIcon1.isDragging = true; game.move(x, y, obj); } }; var towerText1 = towerButton1.addChild(new Text2('50', { size: 50, fill: '#fff', anchorX: 0.5, anchorY: 0.5 })); towerText1.y = -50; towerText1.x = -25; var towerButton2 = uiContainer.addChild(LK.getAsset('uiButton', { anchorX: 0.5, anchorY: 1 })); var towerIcon2 = towerButton2.addChild(LK.getAsset('tower_2', { scaleX: 0.8, scaleY: 0.8, anchorX: 0.5, anchorY: 0.5, y: -110 })); towerIcon2.down = function (x, y, obj) { startPlacingTower('2'); if (placingTowerConfig) { towerIcon2.isDragging = true; game.move(x, y, obj); } }; var towerText2 = towerButton2.addChild(new Text2('100', { size: 50, fill: '#fff', anchorX: 0.5, anchorY: 0.5 })); towerText2.y = -50; towerText2.x = -25; var towerButton3 = uiContainer.addChild(LK.getAsset('uiButton', { anchorX: 0.5, anchorY: 1 })); towerButton3.x = 300; var towerIcon3 = towerButton3.addChild(LK.getAsset('tower_3', { scaleX: 0.8, scaleY: 0.8, anchorX: 0.5, anchorY: 0.5, y: -110 })); towerIcon3.down = function (x, y, obj) { startPlacingTower('3'); if (placingTowerConfig) { towerIcon3.isDragging = true; game.move(x, y, obj); } }; var towerText3 = towerButton3.addChild(new Text2('150', { size: 50, fill: '#fff', anchorX: 0.5, anchorY: 0.5 })); towerText3.y = -50; towerText3.x = -25; // --- Initialization Functions --- function initializeGrid() { for (var i = 0; i < GRID_WIDTH; i++) { grid[i] = []; for (var j = 0; j < GRID_HEIGHT; j++) { grid[i][j] = 0; // Empty } } // Mark path tiles for (var k = 0; k < pathWaypoints.length - 1; k++) { var start = pathWaypoints[k]; var end = pathWaypoints[k + 1]; var p1 = { x: Math.floor(start.x / CELL_SIZE), y: Math.floor(start.y / CELL_SIZE) }; var p2 = { x: Math.floor(end.x / CELL_SIZE), y: Math.floor(end.y / CELL_SIZE) }; var x = p1.x; var y = p1.y; var dx = Math.abs(p2.x - x); var dy = Math.abs(p2.y - y); var sx = x < p2.x ? 1 : -1; var sy = y < p2.y ? 1 : -1; var err = dx - dy; while (true) { if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT) { grid[x][y] = 1; } if (x === p2.x && y === p2.y) { break; } var e2 = 2 * err; if (e2 > -dy) { err -= dy; x += sx; } if (e2 < dx) { err += dx; y += sy; } } } // Mark buildable tiles - only those on the sides of the road segments for (var k = 0; k < pathWaypoints.length - 1; k++) { var start = pathWaypoints[k]; var end = pathWaypoints[k + 1]; var p1 = { x: Math.floor(start.x / CELL_SIZE), y: Math.floor(start.y / CELL_SIZE) }; var p2 = { x: Math.floor(end.x / CELL_SIZE), y: Math.floor(end.y / CELL_SIZE) }; // Determine if this segment is horizontal or vertical var isHorizontal = p1.y === p2.y; var isVertical = p1.x === p2.x; if (isVertical) { // For vertical segments, place buildable tiles on left and right var x = p1.x; var minY = Math.min(p1.y, p2.y); var maxY = Math.max(p1.y, p2.y); for (var y = minY; y <= maxY; y++) { // Left side if (x - 1 >= 0 && grid[x - 1][y] === 0) { grid[x - 1][y] = 2; } // Right side if (x + 1 < GRID_WIDTH && grid[x + 1][y] === 0) { grid[x + 1][y] = 2; } } } else if (isHorizontal) { // For horizontal segments, place buildable tiles on top and bottom var y = p1.y; var minX = Math.min(p1.x, p2.x); var maxX = Math.max(p1.x, p2.x); for (var x = minX; x <= maxX; x++) { // Top side if (y - 1 >= 0 && grid[x][y - 1] === 0) { grid[x][y - 1] = 2; } // Bottom side if (y + 1 < GRID_HEIGHT && grid[x][y + 1] === 0) { grid[x][y + 1] = 2; } } } } // Draw visual grid for (var i = 0; i < GRID_WIDTH; i++) { for (var j = 0; j < GRID_HEIGHT; j++) { var tile; if (grid[i][j] === 1) { tile = mapContainer.addChild(LK.getAsset('pathTile', {})); } else if (grid[i][j] === 2) { tile = mapContainer.addChild(LK.getAsset('buildableTile', {})); tile.alpha = 0.5; } if (tile) { tile.x = i * CELL_SIZE; tile.y = j * CELL_SIZE; } } } } initializeGrid(); function startPlacingTower(type) { var config = towerTypes[type]; if (LK.getScore() >= config.cost) { placingTowerConfig = config; ghostTower = mapContainer.addChild(LK.getAsset(config.asset, { anchorX: 0, anchorY: 0 })); ghostTower.alpha = 0.7; } } // --- Event Handlers --- game.down = function (x, y, obj) { var localPoint = uiContainer.toLocal({ x: x, y: y }); // If the clicked object is a tower on the map, duplicate it for placement if (obj && obj.target && (obj.target.isTower || obj.target.parent && obj.target.parent.isTower)) { var clickedTower = obj.target.isTower ? obj.target : obj.target.parent; // Find tower type from its config var myType = null; if (clickedTower.config) { for (var key in towerTypes) { if (towerTypes[key].asset === clickedTower.config.asset) { myType = key; break; } } } if (myType) { startPlacingTower(myType); if (placingTowerConfig) { game.move(x, y, obj); } } return; // Stop further processing (like map dragging) } if (!placingTowerConfig) { isDraggingMap = true; dragStart.x = x; dragStart.y = y; mapStart.x = mapContainer.x; mapStart.y = mapContainer.y; } }; game.move = function (x, y, obj) { if (placingTowerConfig) { var mapPos = mapContainer.toLocal({ x: x, y: y }); // Position ghost tower directly at cursor position without anchor offset ghostTower.x = mapPos.x; ghostTower.y = mapPos.y; var gridX = Math.floor(mapPos.x / CELL_SIZE); var gridY = Math.floor(mapPos.y / CELL_SIZE); if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT && grid[gridX][gridY] === 2) { ghostTower.tint = 0x00ff00; // Green for valid } else { ghostTower.tint = 0xff0000; // Red for invalid } } else if (isDraggingMap) { var dx = x - dragStart.x; var dy = y - dragStart.y; // Clamp map position var newX = mapStart.x + dx; var newY = mapStart.y + dy; var gameWidth = 2048; var gameHeight = 2732; if (newX > 0) { newX = 0; } if (newY > 0) { newY = 0; } if (newX < gameWidth - MAP_WIDTH) { newX = gameWidth - MAP_WIDTH; } if (newY < gameHeight - MAP_HEIGHT) { newY = gameHeight - MAP_HEIGHT; } mapContainer.x = newX; mapContainer.y = newY; } }; game.up = function (x, y, obj) { if (placingTowerConfig) { var mapPos = mapContainer.toLocal({ x: x, y: y }); var gridX = Math.floor(mapPos.x / CELL_SIZE); var gridY = Math.floor(mapPos.y / CELL_SIZE); if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT && grid[gridX][gridY] === 2) { if (LK.getScore() >= placingTowerConfig.cost) { var newTower = new Tower(placingTowerConfig); newTower.x = gridX * CELL_SIZE + CELL_SIZE / 2; newTower.y = gridY * CELL_SIZE + CELL_SIZE / 2; mapContainer.addChild(newTower); towers.push(newTower); grid[gridX][gridY] = 3; // Mark as occupied LK.setScore(LK.getScore() - placingTowerConfig.cost); scoreTxt.setText('SCORE: ' + LK.getScore()); LK.getSound('place_tower').play(); } } ghostTower.destroy(); ghostTower = null; placingTowerConfig = null; // Reset drag states towerIcon1.isDragging = false; towerIcon2.isDragging = false; towerIcon3.isDragging = false; } isDraggingMap = false; }; // --- Game Logic Functions --- function findClosestEnemy(x, y, range) { var closestEnemy = null; var minDistanceSq = range * range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - x; var dy = enemy.y - y; var distanceSq = dx * dx + dy * dy; if (distanceSq < minDistanceSq) { minDistanceSq = distanceSq; closestEnemy = enemy; } } return closestEnemy; } function fireProjectile(tower, target) { var projectileTypes = tower.config.projectileTypes; for (var i = 0; i < projectileTypes.length; i++) { var projectile = new Projectile({ x: tower.x, y: tower.y }, target, tower.damage, projectileTypes[i]); // Flip arrow if enemy is to the left of tower_2 and projectile is arrow if (tower.config.asset === 'tower_2' && projectileTypes[i] === 'arrow' && target.x < tower.x) { projectile.scaleX = -1; } projectiles.push(projectile); mapContainer.addChild(projectile); } LK.getSound('shoot').play(); } LK.setInterval(function () { var enemy = new Enemy(pathWaypoints); enemy.x = pathWaypoints[0].x; enemy.y = pathWaypoints[0].y; enemy.health += LK.getScore() / 10; // Scale health with score enemy.maxHealth = enemy.health; // Update max health for health bar enemies.push(enemy); mapContainer.addChild(enemy); }, 2000); game.update = function () { for (var i = enemies.length - 1; i >= 0; i--) { enemies[i].update(); } for (var i = towers.length - 1; i >= 0; i--) { towers[i].update(); } for (var i = projectiles.length - 1; i >= 0; i--) { projectiles[i].update(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Enemy = Container.expand(function (path) {
var self = Container.call(this);
self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 3;
self.path = path;
self.currentWaypointIndex = 1;
self.value = 10; // Score awarded on defeat
// Create health bar
self.healthBar = self.addChild(LK.getAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5
}));
self.healthBar.y = -80; // Position above enemy
self.update = function () {
if (self.currentWaypointIndex >= self.path.length) {
// Reached the end
LK.showGameOver();
self.destroy(); // Self-destruct to avoid multiple game overs
return;
}
var targetWaypoint = self.path[self.currentWaypointIndex];
var dx = targetWaypoint.x - self.x;
var dy = targetWaypoint.y - self.y;
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
self.currentWaypointIndex++;
}
};
self.takeDamage = function (amount) {
self.health -= amount;
// Update health bar width based on remaining health
var healthPercentage = self.health / self.maxHealth;
self.healthBar.scaleX = healthPercentage;
if (self.health <= 0) {
return true; // Is dead
}
LK.effects.flashObject(self, 0xffffff, 100);
return false; // Is alive
};
return self;
});
var Projectile = Container.expand(function (start, target, damage, projectileType) {
var self = Container.call(this);
projectileType = projectileType || 'projectile';
self.attachAsset(projectileType, {
anchorX: 0.5,
anchorY: 0.5
});
self.x = start.x;
self.y = start.y;
self.target = target;
self.damage = damage;
self.speed = 20;
self.update = function () {
if (!self.target || self.target.health <= 0) {
// Target is gone
projectiles.splice(projectiles.indexOf(self), 1);
self.destroy();
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.speed) {
// Hit target
if (self.target.takeDamage(self.damage)) {
// Enemy died
LK.getSound('enemy_die').play();
LK.setScore(LK.getScore() + self.target.value);
scoreTxt.setText('SCORE: ' + LK.getScore());
enemies.splice(enemies.indexOf(self.target), 1);
self.target.destroy();
}
projectiles.splice(projectiles.indexOf(self), 1);
self.destroy();
} else {
// Move towards target
var angle = Math.atan2(dy, dx);
self.x += Math.cos(angle) * self.speed;
self.y += Math.sin(angle) * self.speed;
}
};
return self;
});
var Tower = Container.expand(function (config) {
var self = Container.call(this);
self.config = config;
self.attachAsset(config.asset, {
anchorX: 0.5,
anchorY: 0.5
});
self.cost = config.cost;
self.damage = config.damage;
self.range = 400; // All towers have same range for now
self.fireRate = config.fireRate; // shots per second
self.cooldown = 0;
self.isTower = true;
self.down = function (x, y, obj) {
// This makes the tower a "button"
console.log("Tower clicked! Damage: " + self.damage);
LK.effects.flashObject(self, 0xffffff, 200);
};
self.update = function () {
if (self.cooldown > 0) {
self.cooldown--;
return;
}
var target = findClosestEnemy(self.x, self.y, self.range);
if (target) {
fireProjectile(self, target);
self.cooldown = 60 / self.fireRate;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1e272e
});
/****
* Game Code
****/
// The engine will automatically create these assets based on their usage below.
// --- Constants & Game State ---
var MAP_WIDTH = 4000;
var MAP_HEIGHT = 4800;
var CELL_SIZE = 200;
var GRID_WIDTH = MAP_WIDTH / CELL_SIZE;
var GRID_HEIGHT = MAP_HEIGHT / CELL_SIZE;
// Grid states: 0=empty, 1=path, 2=buildable, 3=occupied
var grid = [];
var pathWaypoints = [{
x: 2000 + CELL_SIZE / 2,
y: -100
}, {
x: 2000 + CELL_SIZE / 2,
y: 800 + CELL_SIZE / 2
}, {
x: 800 + CELL_SIZE / 2,
y: 800 + CELL_SIZE / 2
}, {
x: 800 + CELL_SIZE / 2,
y: 1600 + CELL_SIZE / 2
}, {
x: 3200 + CELL_SIZE / 2,
y: 1600 + CELL_SIZE / 2
}, {
x: 3200 + CELL_SIZE / 2,
y: 2400 + CELL_SIZE / 2
}, {
x: 1200 + CELL_SIZE / 2,
y: 2400 + CELL_SIZE / 2
}, {
x: 1200 + CELL_SIZE / 2,
y: 3200 + CELL_SIZE / 2
}, {
x: 2800 + CELL_SIZE / 2,
y: 3200 + CELL_SIZE / 2
}, {
x: 2800 + CELL_SIZE / 2,
y: 4000 + CELL_SIZE / 2
}, {
x: 2800 + CELL_SIZE / 2,
y: 4900
}];
var enemies = [];
var towers = [];
var projectiles = [];
var mapContainer = game.addChild(new Container());
mapContainer.width = MAP_WIDTH;
mapContainer.height = MAP_HEIGHT;
// Add background
var gameBackground = mapContainer.addChild(LK.getAsset('gameBackground', {
anchorX: 0,
anchorY: 0
}));
gameBackground.x = 0;
gameBackground.y = 0;
gameBackground.width = MAP_WIDTH;
gameBackground.height = MAP_HEIGHT;
var placingTowerConfig = null;
var ghostTower = null;
var isDraggingMap = false;
var dragStart = {
x: 0,
y: 0
};
var mapStart = {
x: 0,
y: 0
};
// --- Tower Configurations ---
var towerTypes = {
'1': {
asset: 'tower_1',
cost: 50,
damage: 25,
fireRate: 1.5,
projectileTypes: ['star']
},
'2': {
asset: 'tower_2',
cost: 100,
damage: 60,
fireRate: 1,
projectileTypes: ['arrow']
},
'3': {
asset: 'tower_3',
cost: 150,
damage: 100,
fireRate: 0.75,
projectileTypes: ['star', 'arrow']
}
};
// --- UI ---
LK.setScore(80); // Starting cash
var scoreTxt = new Text2('SCORE: ' + LK.getScore(), {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
scoreTxt.y = 20;
LK.gui.top.addChild(scoreTxt);
var uiContainer = LK.gui.bottom.addChild(new Container());
uiContainer.y = -20; // Move it up slightly from the absolute bottom
var towerButton1 = uiContainer.addChild(LK.getAsset('uiButton', {
anchorX: 0.5,
anchorY: 1
}));
towerButton1.x = -300;
var towerIcon1 = towerButton1.addChild(LK.getAsset('tower_1', {
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5,
y: -110
}));
towerIcon1.down = function (x, y, obj) {
startPlacingTower('1');
if (placingTowerConfig) {
towerIcon1.isDragging = true;
game.move(x, y, obj);
}
};
var towerText1 = towerButton1.addChild(new Text2('50', {
size: 50,
fill: '#fff',
anchorX: 0.5,
anchorY: 0.5
}));
towerText1.y = -50;
towerText1.x = -25;
var towerButton2 = uiContainer.addChild(LK.getAsset('uiButton', {
anchorX: 0.5,
anchorY: 1
}));
var towerIcon2 = towerButton2.addChild(LK.getAsset('tower_2', {
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5,
y: -110
}));
towerIcon2.down = function (x, y, obj) {
startPlacingTower('2');
if (placingTowerConfig) {
towerIcon2.isDragging = true;
game.move(x, y, obj);
}
};
var towerText2 = towerButton2.addChild(new Text2('100', {
size: 50,
fill: '#fff',
anchorX: 0.5,
anchorY: 0.5
}));
towerText2.y = -50;
towerText2.x = -25;
var towerButton3 = uiContainer.addChild(LK.getAsset('uiButton', {
anchorX: 0.5,
anchorY: 1
}));
towerButton3.x = 300;
var towerIcon3 = towerButton3.addChild(LK.getAsset('tower_3', {
scaleX: 0.8,
scaleY: 0.8,
anchorX: 0.5,
anchorY: 0.5,
y: -110
}));
towerIcon3.down = function (x, y, obj) {
startPlacingTower('3');
if (placingTowerConfig) {
towerIcon3.isDragging = true;
game.move(x, y, obj);
}
};
var towerText3 = towerButton3.addChild(new Text2('150', {
size: 50,
fill: '#fff',
anchorX: 0.5,
anchorY: 0.5
}));
towerText3.y = -50;
towerText3.x = -25;
// --- Initialization Functions ---
function initializeGrid() {
for (var i = 0; i < GRID_WIDTH; i++) {
grid[i] = [];
for (var j = 0; j < GRID_HEIGHT; j++) {
grid[i][j] = 0; // Empty
}
}
// Mark path tiles
for (var k = 0; k < pathWaypoints.length - 1; k++) {
var start = pathWaypoints[k];
var end = pathWaypoints[k + 1];
var p1 = {
x: Math.floor(start.x / CELL_SIZE),
y: Math.floor(start.y / CELL_SIZE)
};
var p2 = {
x: Math.floor(end.x / CELL_SIZE),
y: Math.floor(end.y / CELL_SIZE)
};
var x = p1.x;
var y = p1.y;
var dx = Math.abs(p2.x - x);
var dy = Math.abs(p2.y - y);
var sx = x < p2.x ? 1 : -1;
var sy = y < p2.y ? 1 : -1;
var err = dx - dy;
while (true) {
if (x >= 0 && x < GRID_WIDTH && y >= 0 && y < GRID_HEIGHT) {
grid[x][y] = 1;
}
if (x === p2.x && y === p2.y) {
break;
}
var e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x += sx;
}
if (e2 < dx) {
err += dx;
y += sy;
}
}
}
// Mark buildable tiles - only those on the sides of the road segments
for (var k = 0; k < pathWaypoints.length - 1; k++) {
var start = pathWaypoints[k];
var end = pathWaypoints[k + 1];
var p1 = {
x: Math.floor(start.x / CELL_SIZE),
y: Math.floor(start.y / CELL_SIZE)
};
var p2 = {
x: Math.floor(end.x / CELL_SIZE),
y: Math.floor(end.y / CELL_SIZE)
};
// Determine if this segment is horizontal or vertical
var isHorizontal = p1.y === p2.y;
var isVertical = p1.x === p2.x;
if (isVertical) {
// For vertical segments, place buildable tiles on left and right
var x = p1.x;
var minY = Math.min(p1.y, p2.y);
var maxY = Math.max(p1.y, p2.y);
for (var y = minY; y <= maxY; y++) {
// Left side
if (x - 1 >= 0 && grid[x - 1][y] === 0) {
grid[x - 1][y] = 2;
}
// Right side
if (x + 1 < GRID_WIDTH && grid[x + 1][y] === 0) {
grid[x + 1][y] = 2;
}
}
} else if (isHorizontal) {
// For horizontal segments, place buildable tiles on top and bottom
var y = p1.y;
var minX = Math.min(p1.x, p2.x);
var maxX = Math.max(p1.x, p2.x);
for (var x = minX; x <= maxX; x++) {
// Top side
if (y - 1 >= 0 && grid[x][y - 1] === 0) {
grid[x][y - 1] = 2;
}
// Bottom side
if (y + 1 < GRID_HEIGHT && grid[x][y + 1] === 0) {
grid[x][y + 1] = 2;
}
}
}
}
// Draw visual grid
for (var i = 0; i < GRID_WIDTH; i++) {
for (var j = 0; j < GRID_HEIGHT; j++) {
var tile;
if (grid[i][j] === 1) {
tile = mapContainer.addChild(LK.getAsset('pathTile', {}));
} else if (grid[i][j] === 2) {
tile = mapContainer.addChild(LK.getAsset('buildableTile', {}));
tile.alpha = 0.5;
}
if (tile) {
tile.x = i * CELL_SIZE;
tile.y = j * CELL_SIZE;
}
}
}
}
initializeGrid();
function startPlacingTower(type) {
var config = towerTypes[type];
if (LK.getScore() >= config.cost) {
placingTowerConfig = config;
ghostTower = mapContainer.addChild(LK.getAsset(config.asset, {
anchorX: 0,
anchorY: 0
}));
ghostTower.alpha = 0.7;
}
}
// --- Event Handlers ---
game.down = function (x, y, obj) {
var localPoint = uiContainer.toLocal({
x: x,
y: y
});
// If the clicked object is a tower on the map, duplicate it for placement
if (obj && obj.target && (obj.target.isTower || obj.target.parent && obj.target.parent.isTower)) {
var clickedTower = obj.target.isTower ? obj.target : obj.target.parent;
// Find tower type from its config
var myType = null;
if (clickedTower.config) {
for (var key in towerTypes) {
if (towerTypes[key].asset === clickedTower.config.asset) {
myType = key;
break;
}
}
}
if (myType) {
startPlacingTower(myType);
if (placingTowerConfig) {
game.move(x, y, obj);
}
}
return; // Stop further processing (like map dragging)
}
if (!placingTowerConfig) {
isDraggingMap = true;
dragStart.x = x;
dragStart.y = y;
mapStart.x = mapContainer.x;
mapStart.y = mapContainer.y;
}
};
game.move = function (x, y, obj) {
if (placingTowerConfig) {
var mapPos = mapContainer.toLocal({
x: x,
y: y
});
// Position ghost tower directly at cursor position without anchor offset
ghostTower.x = mapPos.x;
ghostTower.y = mapPos.y;
var gridX = Math.floor(mapPos.x / CELL_SIZE);
var gridY = Math.floor(mapPos.y / CELL_SIZE);
if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT && grid[gridX][gridY] === 2) {
ghostTower.tint = 0x00ff00; // Green for valid
} else {
ghostTower.tint = 0xff0000; // Red for invalid
}
} else if (isDraggingMap) {
var dx = x - dragStart.x;
var dy = y - dragStart.y;
// Clamp map position
var newX = mapStart.x + dx;
var newY = mapStart.y + dy;
var gameWidth = 2048;
var gameHeight = 2732;
if (newX > 0) {
newX = 0;
}
if (newY > 0) {
newY = 0;
}
if (newX < gameWidth - MAP_WIDTH) {
newX = gameWidth - MAP_WIDTH;
}
if (newY < gameHeight - MAP_HEIGHT) {
newY = gameHeight - MAP_HEIGHT;
}
mapContainer.x = newX;
mapContainer.y = newY;
}
};
game.up = function (x, y, obj) {
if (placingTowerConfig) {
var mapPos = mapContainer.toLocal({
x: x,
y: y
});
var gridX = Math.floor(mapPos.x / CELL_SIZE);
var gridY = Math.floor(mapPos.y / CELL_SIZE);
if (gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT && grid[gridX][gridY] === 2) {
if (LK.getScore() >= placingTowerConfig.cost) {
var newTower = new Tower(placingTowerConfig);
newTower.x = gridX * CELL_SIZE + CELL_SIZE / 2;
newTower.y = gridY * CELL_SIZE + CELL_SIZE / 2;
mapContainer.addChild(newTower);
towers.push(newTower);
grid[gridX][gridY] = 3; // Mark as occupied
LK.setScore(LK.getScore() - placingTowerConfig.cost);
scoreTxt.setText('SCORE: ' + LK.getScore());
LK.getSound('place_tower').play();
}
}
ghostTower.destroy();
ghostTower = null;
placingTowerConfig = null;
// Reset drag states
towerIcon1.isDragging = false;
towerIcon2.isDragging = false;
towerIcon3.isDragging = false;
}
isDraggingMap = false;
};
// --- Game Logic Functions ---
function findClosestEnemy(x, y, range) {
var closestEnemy = null;
var minDistanceSq = range * range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - x;
var dy = enemy.y - y;
var distanceSq = dx * dx + dy * dy;
if (distanceSq < minDistanceSq) {
minDistanceSq = distanceSq;
closestEnemy = enemy;
}
}
return closestEnemy;
}
function fireProjectile(tower, target) {
var projectileTypes = tower.config.projectileTypes;
for (var i = 0; i < projectileTypes.length; i++) {
var projectile = new Projectile({
x: tower.x,
y: tower.y
}, target, tower.damage, projectileTypes[i]);
// Flip arrow if enemy is to the left of tower_2 and projectile is arrow
if (tower.config.asset === 'tower_2' && projectileTypes[i] === 'arrow' && target.x < tower.x) {
projectile.scaleX = -1;
}
projectiles.push(projectile);
mapContainer.addChild(projectile);
}
LK.getSound('shoot').play();
}
LK.setInterval(function () {
var enemy = new Enemy(pathWaypoints);
enemy.x = pathWaypoints[0].x;
enemy.y = pathWaypoints[0].y;
enemy.health += LK.getScore() / 10; // Scale health with score
enemy.maxHealth = enemy.health; // Update max health for health bar
enemies.push(enemy);
mapContainer.addChild(enemy);
}, 2000);
game.update = function () {
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].update();
}
for (var i = towers.length - 1; i >= 0; i--) {
towers[i].update();
}
for (var i = projectiles.length - 1; i >= 0; i--) {
projectiles[i].update();
}
};