User prompt
The tower drag system seems to take precedence over upgrading towers. It should not
User prompt
Clicking a tower directly should show an upgrade menu rather than triggering the place new tower dragger.
User prompt
Cost of placing the base tower should be 5 gold. You should only get one gold for killing an enemy.
User prompt
Attach the labels to topCenter. And align elements accordingly
User prompt
The 3 labels should be centered on the screen. Lives should be at the center
User prompt
The core label is off the screen on the right. Make sure that these 3 labels are visible on the screen and centered. But make sure it does not overlap on the left.
User prompt
Add the following tracked elements to the game. 1. Gold (Golden color, default: 80) 2. Lives (Green, default: 20) 3. Score (Red, default: 0) Create labels at the top of the screen for each of these values. Please take care to not put anything in the top left corner as that's where the pause button is For now simply add the labels and the logic to update their values.
User prompt
Add the following tracked elements to the game. 1. Gold (Golden color, default: 80) 2. Lives (Green, default: 20) 3. Score (Red, default: 0) Create labels at the top of the screen for each of these values. Please take care to not put anything in the top left corner as that's where the pause button is
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'self.down = function (x, y, obj) { __$(546); self.upgrade(); }')' in or related to this line: 'self.down = function (x, y, obj) {' Line Number: 946
User prompt
Add the following tracked elements to the game. 1. Gold (Golden color, default: 80) 2. Lives (Green, default: 20) 3. Score (Red, default: 0) Create labels at the top of the screen for each of these values. Please take care to not put anything in the top left corner as that's where the pause button is
Code edit (6 edits merged)
Please save this source code
User prompt
Use alpha instead of tint to indicate the state of upgrade levels
Code edit (3 edits merged)
Please save this source code
User prompt
The enemy health bar shrinks toward the center. Instead it should shrink towards the left. Do note that the location of the health bar should remain the same.
User prompt
Enemy health bars are currently center aligned. We want it to be left aligned. Take care to ensure the background is still correctly aligned behind the red progressbar
User prompt
For whatever reason tracking of enemies seem to fail over time. Please debug this and fig it. This seems to be a state issue where towers somehow break and stops tracking enemies.
User prompt
Targeting seems to fail sometimes. Towers should always target and shoot at the enemy in range that is the closet to escaping.
Code edit (1 edits merged)
Please save this source code
Code edit (2 edits merged)
Please save this source code
User prompt
Enemies can currently outrun bullets. Find a clever way to solve this issue. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Our towers should be able to shoot at our enemies. The current tower type is a simple bullet type tower. So make it just shoot bullets at our enemies. Make sure that we only shoot at enemies that are still alive after having been hit by all bullets that have already been fired at said enemy
User prompt
Sometimes when we try to build a tower by releasing the screen, the tower fails to build because an enemy is in the way. In those cases please show a notification that it failed and why.
User prompt
Sometimes when we try to build a tower by releasing the screen, the tower fails to build because an enemy is in the way. In those cases please show a notification that it failed and why.
User prompt
When a tower fails to build because an enemy was in the way, show a notification informing the user of that it happened and why
User prompt
When a tower fails to build because an enemy was in the way, show a notification informing the user of that it happened and why
/**** * Classes ****/ // Enemy class // Bullet class var Bullet = Container.expand(function (startX, startY, targetEnemy, damage, speed) { var self = Container.call(this); // Bullet properties self.targetEnemy = targetEnemy; self.damage = damage || 10; self.speed = speed || 5; self.x = startX; self.y = startY; // Visual representation of bullet var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); // Update method - called every frame self.update = function () { // If target is gone, destroy this bullet if (!self.targetEnemy || !self.targetEnemy.parent) { self.destroy(); return; } // Move toward target var dx = self.targetEnemy.x - self.x; var dy = self.targetEnemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If we hit the target if (distance < self.speed) { // Deal damage to enemy self.targetEnemy.health -= self.damage; // Update health bar visual if (self.targetEnemy.health <= 0) { // Enemy is defeated self.targetEnemy.health = 0; } else { // Update health bar width based on remaining health percentage self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70; } // Remove this bullet self.destroy(); } else { // Move toward target var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } }; return self; }); // DebugCell class var DebugCell = Container.expand(function () { var self = Container.call(this); var cellGraphics = self.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); cellGraphics.tint = Math.random() * 0xffffff; // Add a debug arrow to the debug cells var debugArrows = []; // Add a number label to the debug cells var numberLabel = new Text2('0', { size: 30, fill: 0xFFFFFF, weight: 800 }); numberLabel.anchor.set(.5, .5); self.addChild(numberLabel); self.update = function () {}; self.down = function () { return; if (self.cell.type == 0 || self.cell.type == 1) { self.cell.type = self.cell.type == 1 ? 0 : 1; if (grid.pathFind()) { self.cell.type = self.cell.type == 1 ? 0 : 1; grid.pathFind(); var notification = game.addChild(new Notification("Path is blocked!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } grid.renderDebug(); } }; self.removeArrows = function () { while (debugArrows.length) { self.removeChild(debugArrows.pop()); } }; self.render = function (data) { switch (data.type) { case 0: case 2: { if (data.pathId != pathId) { self.removeArrows(); numberLabel.setText("-"); cellGraphics.tint = 0x880000; return; } numberLabel.visible = true; var tint = Math.floor(data.score / maxScore * 0x88); cellGraphics.tint = 0x88 - tint << 8 | tint; while (debugArrows.length > data.targets.length) { self.removeChild(debugArrows.pop()); } for (var a = 0; a < data.targets.length; a++) { var destination = data.targets[a]; var ox = destination.x - data.x; var oy = destination.y - data.y; var angle = Math.atan2(oy, ox); if (!debugArrows[a]) { debugArrows[a] = LK.getAsset('arrow', { anchorX: -.5, anchorY: 0.5 }); debugArrows[a].alpha = .5; self.addChildAt(debugArrows[a], 1); // debugArrows = } debugArrows[a].rotation = angle; // debugArrow.rotation = angle; } break; } case 1: { self.removeArrows(); cellGraphics.tint = 0xaaaaaa; numberLabel.visible = false; break; } case 3: { self.removeArrows(); cellGraphics.tint = 0x008800; numberLabel.visible = false; break; } } numberLabel.setText(Math.floor(data.score / 1000) / 10); }; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = .05; self.cellX = 0; self.cellY = 0; self.currentCellX = 0; self.currentCellY = 0; self.currentTarget = undefined; self.maxHealth = 100; // Starting max health self.health = self.maxHealth; // Current health self.bulletsTargetingThis = []; // Track bullets targeting this enemy // Add health bar to the enemy var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.0, anchorY: 0.5 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.0, anchorY: 0.5 }); healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10; // Position the health bar above the enemy self.healthBar = healthBar; }); //Class for the grid, handles all logic related to grid movement and interaction var Grid = Container.expand(function (gridWidth, gridHeight) { var self = Container.call(this); self.cells = []; self.spawns = []; self.goals = []; for (var i = 0; i < gridWidth; i++) { self.cells[i] = []; for (var j = 0; j < gridHeight; j++) { self.cells[i][j] = { score: 0, pathId: 0, towersInRange: [] // Cache for towers in range }; } } /* Cell Types 0: Transparent floor 1: Wall 2: Spawn 3: Goal */ for (var i = 0; i < gridWidth; i++) { for (var j = 0; j < gridHeight; j++) { // Set the type to 1 (wall) if the cell is on the edge of the grid, otherwise set it to 0 (ground) var cell = self.cells[i][j]; var cellType = i === 0 || i === gridWidth - 1 || j <= 4 || j >= gridHeight - 4 ? 1 : 0; if (i > 11 - 3 && i <= 11 + 3) { if (j === 0) { cellType = 2; self.spawns.push(cell); } else if (j <= 4) { cellType = 0; } else if (j === gridHeight - 1) { cellType = 3; self.goals.push(cell); } else if (j >= gridHeight - 4) { cellType = 0; } } cell.type = cellType; cell.x = i; cell.y = j; /*if (cell.type == 0) { if (Math.random() > .5) { cell.type = 1; } }*/ //Cache neighbours for speed cell.upLeft = self.cells[i - 1] && self.cells[i - 1][j - 1]; cell.up = self.cells[i - 1] && self.cells[i - 1][j]; cell.upRight = self.cells[i - 1] && self.cells[i - 1][j + 1]; cell.left = self.cells[i][j - 1]; cell.right = self.cells[i][j + 1]; cell.downLeft = self.cells[i + 1] && self.cells[i + 1][j - 1]; cell.down = self.cells[i + 1] && self.cells[i + 1][j]; cell.downRight = self.cells[i + 1] && self.cells[i + 1][j + 1]; cell.neighbors = [cell.upLeft, cell.up, cell.upRight, cell.right, cell.downRight, cell.down, cell.downLeft, cell.left]; cell.targets = []; if (j > 3 && j <= gridHeight - 4) { var debugCell = new DebugCell(); self.addChild(debugCell); debugCell.cell = cell; debugCell.x = i * CELL_SIZE; debugCell.y = j * CELL_SIZE; cell.debugCell = debugCell; } } } self.getCell = function (x, y) { return self.cells[x] && self.cells[x][y]; }; self.pathFind = function () { var before = new Date().getTime(); var toProcess = self.goals.concat([]); maxScore = 0; pathId += 1; for (var a = 0; a < toProcess.length; a++) { toProcess[a].pathId = pathId; } function processNode(node, targetValue, targetNode) { if (node && node.type != 1) { if (node.pathId < pathId || targetValue < node.score) { node.targets = [targetNode]; } else if (node.pathId == pathId && targetValue == node.score) { node.targets.push(targetNode); } if (node.pathId < pathId || targetValue < node.score) { node.score = targetValue; if (node.pathId != pathId) { toProcess.push(node); } node.pathId = pathId; if (targetValue > maxScore) { maxScore = targetValue; } } } } while (toProcess.length) { var nodes = toProcess; toProcess = []; for (var a = 0; a < nodes.length; a++) { var node = nodes[a]; var targetScore = node.score + 14142; // Check for diagonal movement and ensure both adjacent cells are not walls if (node.up && node.left && node.up.type != 1 && node.left.type != 1) { processNode(node.upLeft, targetScore, node); } if (node.up && node.right && node.up.type != 1 && node.right.type != 1) { processNode(node.upRight, targetScore, node); } if (node.down && node.right && node.down.type != 1 && node.right.type != 1) { processNode(node.downRight, targetScore, node); } if (node.down && node.left && node.down.type != 1 && node.left.type != 1) { processNode(node.downLeft, targetScore, node); } targetScore = node.score + 10000; processNode(node.up, targetScore, node); processNode(node.right, targetScore, node); processNode(node.down, targetScore, node); processNode(node.left, targetScore, node); } } for (var a = 0; a < self.spawns.length; a++) { if (self.spawns[a].pathId != pathId) { console.warn("Spawn blocked"); return true; } } for (var a = 0; a < enemies.length; a++) { var enemy = enemies[a]; var target = self.getCell(enemy.cellX, enemy.cellY); if (enemy.currentTarget) { if (enemy.currentTarget.pathId != pathId) { if (!target || target.pathId != pathId) { console.warn("Enemy blocked"); return true; } } } else if (!target || target.pathId != pathId) { console.warn("Enemy blocked"); return true; } } console.log("Speed", new Date().getTime() - before); }; self.renderDebug = function () { for (var i = 0; i < gridWidth; i++) { for (var j = 0; j < gridHeight; j++) { var debugCell = self.cells[i][j].debugCell; if (debugCell) { debugCell.render(self.cells[i][j]); } } } }; self.updateEnemy = function (enemy) { var cell = grid.getCell(enemy.cellX, enemy.cellY); if (cell.type == 3) { return true; } if (!enemy.currentTarget) { enemy.currentTarget = cell.targets[0]; } if (enemy.currentTarget) { //Allows enemies to change direction right away. if (cell.score < enemy.currentTarget.score) { enemy.currentTarget = cell; } var ox = enemy.currentTarget.x - enemy.currentCellX; var oy = enemy.currentTarget.y - enemy.currentCellY; var dist = Math.sqrt(ox * ox + oy * oy); if (dist < enemy.speed) { enemy.cellX = Math.round(enemy.currentCellX); enemy.cellY = Math.round(enemy.currentCellY); enemy.currentTarget = undefined; return; } var angle = Math.atan2(oy, ox); enemy.currentCellX += Math.cos(angle) * enemy.speed; enemy.currentCellY += Math.sin(angle) * enemy.speed; } enemy.x = grid.x + enemy.currentCellX * CELL_SIZE; enemy.y = grid.y + enemy.currentCellY * CELL_SIZE; }; }); // Notification class var Notification = Container.expand(function (message) { var self = Container.call(this); var notificationGraphics = self.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); var notificationText = new Text2(message, { size: 50, fill: 0x000000, weight: 800 }); notificationText.anchor.set(0.5, 0.5); notificationGraphics.width = notificationText.width + 30; self.addChild(notificationText); self.alpha = 1; var fadeOutTime = 120; // 2 seconds at 60FPS self.update = function () { if (fadeOutTime > 0) { fadeOutTime--; self.alpha = Math.min(fadeOutTime / 120 * 2, 1); } else { self.destroy(); } }; return self; }); // Tower class that can be placed on the grid var Tower = Container.expand(function (id) { var self = Container.call(this); // Tower attributes self.id = id || 'default'; self.level = 1; self.maxLevel = 6; self.gridX = 0; self.gridY = 0; self.range = 3 * CELL_SIZE; // initial range (3 grid cells) self.cellsInRange = []; // Cache for cells in range self.fireRate = 60; // frames between shots self.bulletSpeed = 5; self.damage = 10; self.lastFired = 0; self.targetEnemy = null; // Create the tower base (a square) var baseGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); // Level indicators (dots at bottom of tower) var levelIndicators = []; var maxDots = self.maxLevel; var dotSpacing = baseGraphics.width / (maxDots + 1); var dotSize = CELL_SIZE / 6; for (var i = 0; i < maxDots; i++) { var dot = new Container(); var towerLevelIndicator = dot.attachAsset('towerLevelIndicator', { anchorX: 0.5, anchorY: 0.5 }); towerLevelIndicator.width = dotSize; towerLevelIndicator.height = dotSize; towerLevelIndicator.tint = 0xCCCCCC; // Gray dots by default // Position dot at bottom of tower dot.x = -CELL_SIZE + dotSpacing * (i + 1); dot.y = CELL_SIZE * 0.7; self.addChild(dot); levelIndicators.push(dot); } // Create the gun turret that will rotate var gunContainer = new Container(); self.addChild(gunContainer); var gunGraphics = gunContainer.attachAsset('defense', { anchorX: 0.5, anchorY: 0.5 }); //gunGraphics.y = -CELL_SIZE * 0.3; // Position the gun slightly up from center // Update the level indicators self.updateLevelIndicators = function () { for (var i = 0; i < maxDots; i++) { var dot = levelIndicators[i]; var towerLevelIndicator = dot.children[0]; // Light up dots up to current level if (i < self.level) { towerLevelIndicator.tint = 0xFFFF00; // Yellow for active levels } else { towerLevelIndicator.tint = 0xCCCCCC; // Gray for inactive levels } } }; // Initialize level indicators self.updateLevelIndicators(); // Method to upgrade the tower self.upgrade = function () { if (self.level < self.maxLevel) { self.level++; // Increase stats based on level self.range = (3 + self.level * 0.5) * CELL_SIZE; self.fireRate = Math.max(20, 60 - self.level * 8); // Faster firing rate self.damage = 10 + self.level * 5; self.bulletSpeed = 5 + self.level * 0.5; // Update level indicators self.updateLevelIndicators(); return true; } return false; }; // Method to find the nearest enemy within range self.findTarget = function () { var closestEnemy = null; var closestScore = Infinity; // Use pathfinding score to determine proximity to goal for (var i = 0; i < self.cellsInRange.length; i++) { var cell = self.cellsInRange[i]; for (var j = 0; j < enemies.length; j++) { var enemy = enemies[j]; if (enemy.cellX === cell.x && enemy.cellY === cell.y) { // Check if this enemy is closer to the goal based on pathfinding score if (cell.score < closestScore) { closestScore = cell.score; closestEnemy = enemy; } } } } // Reset targetEnemy if no valid target is found if (!closestEnemy) { self.targetEnemy = null; } return closestEnemy; }; // Update method self.update = function () { // Always find the target every frame to ensure state consistency self.targetEnemy = self.findTarget(); // Rotate gun toward target if (self.targetEnemy) { var dx = self.targetEnemy.x - self.x; var dy = self.targetEnemy.y - self.y; var angle = Math.atan2(dy, dx); // Rotate the gun container gunContainer.rotation = angle; // Fire at target if cooldown is complete if (LK.ticks - self.lastFired >= self.fireRate) { self.fire(); self.lastFired = LK.ticks; } } }; // Check if enemy is in range self.isInRange = function (enemy) { if (!enemy) { return false; } var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); return distance <= self.range; }; // Fire at the current target self.fire = function () { if (self.targetEnemy) { // Verify the target is alive and calculate damage including future damage var potentialDamage = 0; for (var i = 0; i < self.targetEnemy.bulletsTargetingThis.length; i++) { potentialDamage += self.targetEnemy.bulletsTargetingThis[i].damage; } // Only shoot if enemy would still be alive after all current bullets hit if (self.targetEnemy.health > potentialDamage) { // Calculate bullet starting position var bulletX = self.x + Math.cos(gunContainer.rotation) * 40; var bulletY = self.y + Math.sin(gunContainer.rotation) * 40; // Create a new bullet var bullet = new Bullet(bulletX, bulletY, self.targetEnemy, self.damage, self.bulletSpeed); game.addChild(bullet); // Add bullet to global bullets array and to enemy's tracking array bullets.push(bullet); self.targetEnemy.bulletsTargetingThis.push(bullet); } } }; // Place tower on the grid self.placeOnGrid = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; // Adjust by half a cell size to align with grid cell centers self.x = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2; self.y = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2; // Mark these grid cells as occupied for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(gridX + i, gridY + j); if (cell) { cell.type = 1; // Mark as wall/occupied } } } // Calculate cells in range self.cellsInRange = []; for (var i = -Math.floor(self.range / CELL_SIZE); i <= Math.floor(self.range / CELL_SIZE); i++) { for (var j = -Math.floor(self.range / CELL_SIZE); j <= Math.floor(self.range / CELL_SIZE); j++) { var cell = grid.getCell(gridX + i, gridY + j); if (cell) { self.cellsInRange.push(cell); cell.towersInRange.push(self); } } } }; return self; }); // TowerPreview class for showing where towers can be placed var TowerPreview = Container.expand(function () { var self = Container.call(this); // Define tower range (in grid cells) var towerRange = 3; var rangeInPixels = towerRange * CELL_SIZE; // Create the range indicator (circle) var rangeIndicator = new Container(); self.addChild(rangeIndicator); // Since we don't have direct access to Graphics, // we'll create a white semi-transparent circle shape asset var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); rangeGraphics.alpha = 0.3; // Make it semi-transparent // Create the preview square (2x2 grid cells) var previewGraphics = self.attachAsset('towerpreview', { anchorX: 0.5, anchorY: 0.5 }); // Set the size to cover 2x2 grid cells previewGraphics.width = CELL_SIZE * 2; previewGraphics.height = CELL_SIZE * 2; // Initial state self.canPlace = false; self.gridX = 0; self.gridY = 0; self.blockedByEnemy = false; // Track if placement is blocked by enemy // Update the preview color based on placement possibility self.updatePlacementStatus = function () { // First check if placement is valid from a grid perspective var validGridPlacement = true; // Check if we're in the valid placement area (main square of the board) if (self.gridY <= 4 || self.gridY + 1 >= grid.cells[0].length - 4) { validGridPlacement = false; } else { // Check each of the 2x2 cells for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(self.gridX + i, self.gridY + j); // Can't place if any cell is undefined or not of type 0 (empty ground) if (!cell || cell.type !== 0) { validGridPlacement = false; break; } } if (!validGridPlacement) { break; } } } // Now check if any enemy is occupying the space where the tower would be placed self.blockedByEnemy = false; if (validGridPlacement) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.cellX >= self.gridX && enemy.cellX < self.gridX + 2 && enemy.cellY >= self.gridY && enemy.cellY < self.gridY + 2) { self.blockedByEnemy = true; break; } } } // Set final placement status self.canPlace = validGridPlacement && !self.blockedByEnemy; // If we can place, tint green, otherwise red previewGraphics.tint = self.canPlace ? 0x00FF00 : 0xFF0000; }; // Check if the tower can be placed at the current position self.checkPlacement = function () { self.updatePlacementStatus(); }; // Snap the preview to the grid self.snapToGrid = function (x, y) { // Convert screen coordinates to grid coordinates // Offset by the grid's position var gridPosX = x - grid.x; var gridPosY = y - grid.y; // Convert to grid coordinates and round down to get the top-left cell self.gridX = Math.floor(gridPosX / CELL_SIZE); self.gridY = Math.floor(gridPosY / CELL_SIZE); // Position the preview at the intersection of the four cells // Adding CELL_SIZE to gridX and gridY gives us the center point // Adjust by half a cell size to align with grid cell centers self.x = grid.x + self.gridX * CELL_SIZE + CELL_SIZE / 2; // Center of the 2x2 grid self.y = grid.y + self.gridY * CELL_SIZE + CELL_SIZE / 2; // Center of the 2x2 grid // Check if we can place the tower here self.checkPlacement(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 //Init game with black background }); /**** * Game Code ****/ // Define cell size as a global constant var CELL_SIZE = 76; var pathId = 1; var maxScore = 0; var enemies = []; var towers = []; var bullets = []; // Array to store active bullets var defenses = []; // Create separate layers for debug tiles, towers, and enemies var debugLayer = new Container(); var towerLayer = new Container(); var enemyLayer = new Container(); var grid = new Grid(24, 29 + 6); grid.x = 150; grid.y = 112 - CELL_SIZE * 4; grid.pathFind(); grid.renderDebug(); debugLayer.addChild(grid); // Add layers to the game in the correct order game.addChild(debugLayer); game.addChild(towerLayer); game.addChild(enemyLayer); var offset = 0; // Create tower preview var towerPreview = new TowerPreview(); game.addChild(towerPreview); towerPreview.visible = false; // Handle drag events for tower placement var isDragging = false; // Check if placing a tower would block the path function wouldBlockPath(gridX, gridY) { // Temporarily mark cells as walls var cells = []; for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(gridX + i, gridY + j); if (cell) { cells.push({ cell: cell, originalType: cell.type }); cell.type = 1; // Mark as wall } } } // Check if path is still valid var blocked = grid.pathFind(); // Restore original cell types for (var i = 0; i < cells.length; i++) { cells[i].cell.type = cells[i].originalType; } // Re-find path with original cells grid.pathFind(); grid.renderDebug(); return blocked; } // Place a tower at the grid position function placeTower(gridX, gridY) { var tower = new Tower('default'); tower.placeOnGrid(gridX, gridY); towerLayer.addChild(tower); towers.push(tower); // Update grid pathing grid.pathFind(); grid.renderDebug(); } game.down = function (x, y, obj) { towerPreview.visible = true; isDragging = true; towerPreview.snapToGrid(x, y); }; game.move = function (x, y, obj) { if (isDragging) { towerPreview.snapToGrid(x, y); } }; game.up = function (x, y, obj) { isDragging = false; // Check placement possibilities if (towerPreview.canPlace) { // Check if placing here would block the path if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) { // Place the tower placeTower(towerPreview.gridX, towerPreview.gridY); } else { // Show notification that we can't place here var notification = game.addChild(new Notification("Tower would block the path!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } } else if (towerPreview.blockedByEnemy) { // Show notification that an enemy is in the way var notification = game.addChild(new Notification("Cannot build: Enemy in the way!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } else if (towerPreview.visible) { // Generic notification for other reasons var notification = game.addChild(new Notification("Cannot build here!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } towerPreview.visible = false; }; game.update = function () { // Spawn enemies periodically if (++offset % 100 == 0) { var target = grid.spawns[Math.random() * grid.spawns.length >> 0]; var enemy = enemyLayer.addChild(new Enemy()); enemy.currentCellX = enemy.cellX = target.x; enemy.currentCellY = enemy.cellY = target.y; enemies.push(enemy); } // Update enemies for (var a = enemies.length - 1; a >= 0; a--) { var enemy = enemies[a]; // Check if enemy is defeated if (enemy.health <= 0) { // Clear bullet targeting references for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) { var bullet = enemy.bulletsTargetingThis[i]; bullet.targetEnemy = null; // This will cause bullet to destroy itself on next update } // Remove enemy enemyLayer.removeChild(enemy); enemies.splice(a, 1); continue; } // Update enemy movement if (grid.updateEnemy(enemy)) { // Enemy reached goal enemyLayer.removeChild(enemy); enemies.splice(a, 1); } } // Update towers for (var i = 0; i < towers.length; i++) { towers[i].update(); } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].update(); // Remove destroyed bullets if (!bullets[i].parent) { // If this bullet has a target, remove the bullet from the target's tracking array if (bullets[i].targetEnemy) { var targetEnemy = bullets[i].targetEnemy; var bulletIndex = targetEnemy.bulletsTargetingThis.indexOf(bullets[i]); if (bulletIndex !== -1) { targetEnemy.bulletsTargetingThis.splice(bulletIndex, 1); } } // Ensure bullet is removed from global bullets array bullets.splice(i, 1); } } // Update tower preview placement status on every frame // This ensures the placement status updates in real-time as enemies move if (towerPreview.visible) { towerPreview.checkPlacement(); } };
===================================================================
--- original.js
+++ change.js
@@ -158,13 +158,13 @@
self.health = self.maxHealth; // Current health
self.bulletsTargetingThis = []; // Track bullets targeting this enemy
// Add health bar to the enemy
var healthBarOutline = self.attachAsset('healthBarOutline', {
- anchorX: 0.5,
+ anchorX: 0.0,
anchorY: 0.5
});
var healthBar = self.attachAsset('healthBar', {
- anchorX: 0.5,
+ anchorX: 0.0,
anchorY: 0.5
});
healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10; // Position the health bar above the enemy
self.healthBar = healthBar;
White circle with two eyes, seen from above.. In-Game asset. 2d. High contrast. No shadows
White simple circular enemy seen from above, black outline. Black eyes, with a single shield in-font of it. Black and white only. Blue background.
White circle with black outline. Blue background.. In-Game asset. 2d. High contrast. No shadows