User prompt
Make the towers in the middle of their grids cells buttons
User prompt
fix the dragging of towers to be dragged to the cursor not in the top left corner!
User prompt
Please fix the bug: 'Uncaught TypeError: towerButton1.containsPoint is not a function' in or related to this line: 'if (towerButton1.containsPoint(uiContainer.toLocal({' Line Number: 378
Code edit (1 edits merged)
Please save this source code
User prompt
Serpent Siege
Initial prompt
Make a game about tower defense, do a background for the game can be dragged to see max distance of it, size of the gamebackground 3000x4000, do the road with many squares of 200x200 from top of background to bottom as serpent shape where enemies walk, do tower button can be clicked and dragged to the grid 1x1 around the road like one grid cell for one tower in each corner, towers can bought by score each tower by 50 score,do 2 other buttons of towers to buy by score but they are hard a bit from first to 3rd and by more score secondtower by 100 score, 3rd by 150 score.
/**** * 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.speed = 3; self.path = path; self.currentWaypointIndex = 1; self.value = 10; // Score awarded on defeat 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; 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) { var self = Container.call(this); self.attachAsset('projectile', { 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.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 = 3000; var MAP_HEIGHT = 4000; 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: 1500, y: -100 }, { x: 1500, y: 800 }, { x: 600, y: 800 }, { x: 600, y: 1600 }, { x: 2400, y: 1600 }, { x: 2400, y: 2400 }, { x: 1000, y: 2400 }, { x: 1000, y: 3200 }, { x: 2000, y: 3200 }, { x: 2000, y: 4100 }]; var enemies = []; var towers = []; var projectiles = []; var mapContainer = game.addChild(new Container()); mapContainer.width = MAP_WIDTH; mapContainer.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 }, '2': { asset: 'tower_2', cost: 100, damage: 60, fireRate: 1 }, '3': { asset: 'tower_3', cost: 150, damage: 100, fireRate: 0.75 } }; // --- 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 })); var towerText1 = towerButton1.addChild(new Text2('50', { size: 50, fill: '#fff', anchorX: 0.5, anchorY: 0.5 })); towerText1.y = 60; 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 })); var towerText2 = towerButton2.addChild(new Text2('100', { size: 50, fill: '#fff', anchorX: 0.5, anchorY: 0.5 })); towerText2.y = 60; 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 })); var towerText3 = towerButton3.addChild(new Text2('150', { size: 50, fill: '#fff', anchorX: 0.5, anchorY: 0.5 })); towerText3.y = 60; // --- 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 for (var i = 0; i < GRID_WIDTH; i++) { for (var j = 0; j < GRID_HEIGHT; j++) { if (grid[i][j] === 1) { // is a path tile for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nx = i + dx; var ny = j + dy; if (nx >= 0 && nx < GRID_WIDTH && ny >= 0 && ny < GRID_HEIGHT && grid[nx][ny] === 0) { grid[nx][ny] = 2; // buildable } } } } } } // 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.5, anchorY: 0.5 })); ghostTower.alpha = 0.7; } } // --- Event Handlers --- game.down = function (x, y, obj) { var localPoint = uiContainer.toLocal({ x: x, y: y }); // Tower Button 1 Check var b1_topLeft_x = towerButton1.x - towerButton1.width * 0.5; var b1_topLeft_y = towerButton1.y - towerButton1.height * 1; if (localPoint.x >= b1_topLeft_x && localPoint.x <= b1_topLeft_x + towerButton1.width && localPoint.y >= b1_topLeft_y && localPoint.y <= b1_topLeft_y + towerButton1.height) { startPlacingTower('1'); if (placingTowerConfig) game.move(x, y, obj); return; } // Tower Button 2 Check var b2_topLeft_x = towerButton2.x - towerButton2.width * 0.5; var b2_topLeft_y = towerButton2.y - towerButton2.height * 1; if (localPoint.x >= b2_topLeft_x && localPoint.x <= b2_topLeft_x + towerButton2.width && localPoint.y >= b2_topLeft_y && localPoint.y <= b2_topLeft_y + towerButton2.height) { startPlacingTower('2'); if (placingTowerConfig) game.move(x, y, obj); return; } // Tower Button 3 Check var b3_topLeft_x = towerButton3.x - towerButton3.width * 0.5; var b3_topLeft_y = towerButton3.y - towerButton3.height * 1; if (localPoint.x >= b3_topLeft_x && localPoint.x <= b3_topLeft_x + towerButton3.width && localPoint.y >= b3_topLeft_y && localPoint.y <= b3_topLeft_y + towerButton3.height) { startPlacingTower('3'); if (placingTowerConfig) game.move(x, y, obj); return; } // If the clicked object is a tower, let its own handler manage it and stop further processing. if (obj && obj.target && obj.target.parent && obj.target.parent.isTower) { return; } 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 }); 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; } 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 projectile = new Projectile({ x: tower.x, y: tower.y }, target, tower.damage); 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 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(); } };
===================================================================
--- original.js
+++ change.js
@@ -97,8 +97,14 @@
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;
@@ -383,8 +389,12 @@
startPlacingTower('3');
if (placingTowerConfig) game.move(x, y, obj);
return;
}
+ // If the clicked object is a tower, let its own handler manage it and stop further processing.
+ if (obj && obj.target && obj.target.parent && obj.target.parent.isTower) {
+ return;
+ }
if (!placingTowerConfig) {
isDraggingMap = true;
dragStart.x = x;
dragStart.y = y;