User prompt
rename the embedded asset dotGraphics to upgradelevelIndicator
User prompt
Enemies seems to render by half a cell_size to the left and up, please fix this
User prompt
Enemies now seem to render misaligned from the debug grid, please fix this
User prompt
Enemies no longer seem to correctly be removed from the game, please fix this
User prompt
Create layers in the game such that debug tiles are always rendering at the bottom, towers are a separate layers and enemies are always rendered in front of everything else
User prompt
Enemies should always be in front, currently they sometimes render below towers. Make sure that enemies always render above towers, even when we place new towers
User prompt
Enemies should always be in front, currently they sometimes render below towers
User prompt
Update the update level system on towers such that the dots render below the gun graphics.
User prompt
Use a separate dotGraphics asset type for these. Also make sure they are spaced exactly such that the upgrade level of a tower takes up the full width of the tower base
User prompt
In the tower class don't scale elements programmatically. Scale of assets are handled by the engine.
Code edit (2 edits merged)
Please save this source code
User prompt
Preview tower and tower placement themself are off by half a cell_size. This happens because the cell's are have a center anchor point. Please fix this by making sure that tower preview and towers are placed half a cell_size to the left and up
User prompt
Both preview tower and towers themself seem to be misaligned by half a cell_size. I think it's because the game grid uses elements that are center aligned. Please fix this.
User prompt
Ok we now have a nice preview system. Let's add a towers class to the game. I think we only want one tower class for simplicity where we simply pass an id into it on creation to set art and attributes. Towers are upgradable, we show the upgrade level with a simple dots across the bottom of the tower. All towers start at level 1 and can be upgraded to level 6. Visually towers consist of a square base and a gun at the top of the tower that can rotate to point towards the nearest targeted enemy. Towers also have attributes such as (note these can change for each level) * range (how far they can fire) * fire speed * bullet speed/type Implement this class, only creating one tower type for now. When we release our preview tower, and we can place a tower place an instance of this tower on our game board.
User prompt
In the tower preview class we also want to show the range of the tower. We do this with a simple white semi opaque circle. Add that to the tower preview
User prompt
For the preview cell, towers can only be places inside the main square of the board. E.g. where j > 4 and j < gridHeight - 4 in the definition. Please update the preview system to handle this
Code edit (4 edits merged)
Please save this source code
User prompt
We have 76 as cell size and derivatives of this size hardcoded all over the place in the code, please use a single variable instead.
Code edit (1 edits merged)
Please save this source code
User prompt
The tower preview seems misaligned with the actual grid, I think this is because cells are center aligned. Please update the tower preview class accordingly
User prompt
We need to start building the infrastructure to place towers. The very first thing we want to add is a tower placement preview class. Note that Towers are 2x2 grid tiles large and can only be places on the playing grid. Tower preview have a tower sized square that is either green if the tower can be placed or red if it cannot be placed. The preview element should snap to the grid. Please implement this class and create a single instance that we always drag. Do nothing else for now. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
Code edit (6 edits merged)
Please save this source code
User prompt
Add a currency to this game, that can be used to build and upgrade towers. Only implement the currency infrastructure for now. Show my coins in the top right corner with a label + a gold coin. Start the game with 80 coins.
Code edit (1 edits merged)
Please save this source code
/**** * Classes ****/ // 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 () { 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); }; }); // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = Math.random() * .2 + .03; self.cellX = 0; self.cellY = 0; self.currentCellX = 0; self.currentCellY = 0; self.currentTarget = undefined; // Add health bar to the enemy var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0.5, anchorY: 0.5 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0.5, 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 }; } } /* 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++) { var debugCell = new DebugCell(); // 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 - 2 || j <= 1 || j === gridHeight - 1 ? 1 : 0; if (i > 11 - 3 && i <= 11 + 4) { if (j === 0) { cellType = 2; self.spawns.push(cell); } else if (j === gridHeight - 1) { cellType = 3; self.goals.push(cell); } } cell.type = cellType; cell.debugCell = debugCell; 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 = []; self.addChild(debugCell); debugCell.cell = cell; debugCell.x = i * 76; debugCell.y = j * 76; } } 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; processNode(node.upLeft, targetScore, node); processNode(node.upRight, targetScore, node); processNode(node.downRight, targetScore, node); 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; 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 = enemy.currentCellX * 75; enemy.y = enemy.currentCellY * 75; }; }); // 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(); } }; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 //Init game with black background }); /**** * Game Code ****/ var pathId = 1; var maxScore = 0; var enemies = []; var defenses = []; var grid = new Grid(25, 29); grid.x = 112; grid.y = 112; grid.pathFind(); grid.renderDebug(); game.addChild(grid); var offset = 0; game.update = function () { if (++offset % 100 == 0) { isFirst = false; var target = grid.spawns[Math.random() * grid.spawns.length >> 0]; var enemy = grid.addChild(new Enemy()); enemy.currentCellX = enemy.cellX = target.x; enemy.currentCellY = enemy.cellY = target.y; enemies.push(enemy); } for (var a = enemies.length - 1; a >= 0; a--) { if (grid.updateEnemy(enemies[a])) { grid.removeChild(enemies[a]); enemies.splice(a--, 1); } } };
===================================================================
--- original.js
+++ change.js
@@ -131,15 +131,22 @@
pathId: 0
};
}
}
+ /*
+ 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++) {
var debugCell = new DebugCell();
// 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 === 0 || j === gridHeight - 1 ? 1 : 0;
- if (i > 11 - 3 && i <= 11 + 3) {
+ var cellType = i === 0 || i >= gridWidth - 2 || j <= 1 || j === gridHeight - 1 ? 1 : 0;
+ if (i > 11 - 3 && i <= 11 + 4) {
if (j === 0) {
cellType = 2;
self.spawns.push(cell);
} else if (j === gridHeight - 1) {
@@ -168,10 +175,10 @@
cell.neighbors = [cell.upLeft, cell.up, cell.upRight, cell.right, cell.downRight, cell.down, cell.downLeft, cell.left];
cell.targets = [];
self.addChild(debugCell);
debugCell.cell = cell;
- debugCell.x = i * 75;
- debugCell.y = j * 75;
+ debugCell.x = i * 76;
+ debugCell.y = j * 76;
}
}
self.getCell = function (x, y) {
return self.cells[x] && self.cells[x][y];
@@ -306,11 +313,8 @@
self.destroy();
}
};
});
-var Tower = Container.expand(function (type) {
- var self = Container.call(this);
-});
/****
* Initialize Game
****/
@@ -324,11 +328,11 @@
var pathId = 1;
var maxScore = 0;
var enemies = [];
var defenses = [];
-var grid = new Grid(24, 28);
-grid.x = 160;
-grid.y = 150;
+var grid = new Grid(25, 29);
+grid.x = 112;
+grid.y = 112;
grid.pathFind();
grid.renderDebug();
game.addChild(grid);
var offset = 0;
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