Code edit (20 edits merged)
Please save this source code
User prompt
in unitharvester stopOtherActions, user unit player tint instead of 0xFFFFFF
User prompt
implent fog of war
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Update the findPath A* algorithm to take into account the range when openSet.length === 0
User prompt
Update the findPath A* algorithm to take into account the range
Code edit (5 edits merged)
Please save this source code
User prompt
add a new 'range' argument to findPath that give the desired range to destination. default is 0
User prompt
add a new 'range' argument to findPath and return the path when distance to destination is <= range
Code edit (2 edits merged)
Please save this source code
User prompt
in guard, only add units and building of other player in allTargets
Code edit (1 edits merged)
Please save this source code
User prompt
in guard function, also search for buildings
Code edit (1 edits merged)
Please save this source code
Code edit (4 edits merged)
Please save this source code
User prompt
in prepareMap, add 2 WindTraps for player 2
Code edit (1 edits merged)
Please save this source code
User prompt
if displayActionItems, also check if unit health > 0 before displaying actions
Code edit (1 edits merged)
Please save this source code
User prompt
in fire(), if target element health <=0 return to attackMode = 0
User prompt
in setOnElement, if element health <=0 hide the selector
User prompt
in findPath return the best path even if it doest reach the destination
Code edit (3 edits merged)
Please save this source code
User prompt
in findPath pathfinding algorithm avoid also cells with units
/****
* Classes
****/
var ActionItemIcon = Container.expand(function (action, x, y) {
var self = Container.call(this);
self.action = action;
self.asset = self.attachAsset(action.icon, {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
width: 200,
height: 200
});
self.asset.on('down', function () {
if (self.action && typeof self.action.handler === 'function') {
currentSelection.selectedAction = self.action.code;
if (self.action.noTarget) {
self.action.handler(currentSelection);
highlightBorder(self.asset.x);
LK.setTimeout(function () {
highlightBorder();
}, 1000);
} else {
currentUserActionState = UserActionState.SET_ORDER_TARGET;
console.log('Wait target for action ' + self.action.name);
if (!targetMoveCursor) {
targetMoveCursor = game.addChild(new TargetMoveCursor());
}
highlightBorder(self.asset.x);
}
} else {
currentSelection.selectedAction = null;
}
});
self.label = new Text2(action.name, {
size: 30,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
self.label.x = self.asset.x - action.name.length / 2 * 13;
self.label.y = self.asset.y + 120;
self.addChild(self.label);
return self;
});
var ActionRepository = Container.expand(function () {
var self = Container.call(this);
self.actions = {
'harvest': {
'code': 'harvest',
'name': 'Harvest',
'icon': 'harvestIcon',
'handler': harvestActionLogic
},
'move': {
'code': 'move',
'name': 'Move',
'icon': 'moveIcon',
'handler': moveActionLogic
},
'return': {
'code': 'return',
'noTarget': true,
'name': 'Retreat',
'icon': 'returnIcon',
'handler': retreatActionLogic
},
'attack': {
'code': 'attack',
'name': 'Attack',
'icon': 'attackIcon',
'handler': attackActionLogic
}
};
self.getActionInfo = function (actionType) {
return self.actions[actionType];
};
return self;
});
var BuildableItemIcon = Container.expand(function (type, x, y, sourceFactory) {
var self = Container.call(this);
self.type = type;
self.sourceFactory = sourceFactory;
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
width: 200,
height: 200
});
self.asset.on('down', function () {
if (!self.asset.interactive) {
return;
}
if (currentBuildingForPlacement) {
currentUserActionState = UserActionState.PLACING_BUILDINGS;
console.log("User is placing ", currentBuildingForPlacement);
game.addChild(currentBuildingForPlacement);
return;
}
var progressDisplay = new BuildingProgressDisplay(self);
game.addChild(progressDisplay);
enqueueBuildable(self, progressDisplay);
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.alpha = 0.75;
}
});
});
var buildableInfo = buildableRepository.getBuildableInfo(type);
if (buildableInfo) {
self.label = new Text2(buildableInfo.name, {
size: 30,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
self.label.x = self.asset.x - (buildableInfo.name ? buildableInfo.name.length / 2 * 13 : 0);
self.label.y = self.asset.y + 120;
self.addChild(self.label);
}
self.setLabelToPlace = function () {
self.label.setText('Place');
self.label.x = self.asset.x - 38; // Recalculate x position for new text
};
self.restoreLabel = function () {
self.label.setText(buildableInfo.name);
self.label.x = self.asset.x - buildableInfo.name.length / 2 * 13; // Recalculate x position for new text
};
return self;
});
// BuildableRepository class to store reference information about all buildables
var BuildableRepository = Container.expand(function () {
var self = Container.call(this);
self.buildables = {
'constructionYard': {
'name': 'Construction Yard',
'cost': 300,
'energy': 0,
'health': 2,
//100, TEMP DEBUG !!!! TEMP DEBUG !!!! TEMP DEBUG !!!! TEMP DEBUG !!!!
'constructionTime': 10,
'className': 'ConstructionYard',
'buildable': ['windTrap', 'spiceRefinery', 'lightFactory']
},
'windTrap': {
'name': 'Wind Trap',
'cost': 300,
'energy': 100,
'health': 50,
'constructionTime': 2,
'className': 'WindTrap',
'buildable': []
},
'spiceRefinery': {
'name': 'Spice Refinery',
'cost': 600,
'energy': -50,
'health': 75,
'constructionTime': 1,
'className': 'SpiceRefinery',
'buildable': []
},
'lightFactory': {
'name': 'Light Factory',
'cost': 400,
'energy': -30,
'health': 60,
'constructionTime': 1,
'className': 'LightFactory',
'buildable': ['unitQuad'],
'requires': '' //spiceRefinery
},
'unitQuad': {
'name': 'Quad',
'cost': 200,
'energy': 0,
'constructionTime': 1,
'className': 'UnitQuad',
'buildable': []
}
// Add more buildables as needed
};
self.getBuildableInfo = function (type) {
return self.buildables[type];
};
return self;
});
// Base Building class
var Building = Container.expand(function (x, y, type, name, playerId, cellW, cellH, buildable) {
var self = Container.call(this);
self.cellX = x;
self.cellY = y;
self.cellW = cellW || 1; // Default to 1x1 if not specified
self.cellH = cellH || 1; // Default to 1x1 if not specified
self.x = x * tileSize;
self.y = y * tileSize;
self.type = type;
self.isBuilding = true;
self.playerId = playerId;
self.name = name;
var buildableInfo = buildableRepository.getBuildableInfo(type);
self.health = buildableInfo.health;
self.buildable = buildable || []; // New property to store what the building can build
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.asset.x = self.x;
self.asset.y = self.y;
self.buildingQueue = [];
self.damage = function (points) {
console.log("Building " + self.name + " hit! " + points);
self.health -= points;
self.asset.tint = 0xff0000;
LK.setTimeout(function () {
self.asset.tint = 0xFFFFFF;
}, 500);
if (self.health <= 0) {
console.log("Building " + self.name + " destroyed");
self.assetEffect = self.attachAsset('buildingExplosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + self.cellW / 2 * tileSize,
y: self.y + self.cellH / 2 * tileSize,
width: 20,
height: 20
});
var growExplosion = function growExplosion(size) {
if (size < 300) {
self.assetEffect.width = size;
self.assetEffect.height = size;
LK.setTimeout(function () {
growExplosion(size + 3);
}, 10);
} else {
// Remove building from gameMap cells and game
for (var w = 0; w < self.cellW; w++) {
for (var h = 0; h < self.cellH; h++) {
if (gameMap.cells[self.cellX + w] && gameMap.cells[self.cellX + w][self.cellY + h]) {
gameMap.cells[self.cellX + w][self.cellY + h].building = null;
}
}
}
game.removeChild(self);
self.destroy();
}
};
growExplosion(1);
}
};
return self;
});
var WindTrap = Building.expand(function (x, y, playerId) {
var self = Building.call(this, x, y, 'windTrap', 'Wind Trap', playerId, 2, 2);
// Additional properties and methods for WindTrap can be added here
return self;
});
var SpiceRefinery = Building.expand(function (x, y, playerId) {
var self = Building.call(this, x, y, 'spiceRefinery', 'Spice Refinery', playerId, 3, 2);
// Additional properties and methods for SpiceRefinery can be added here
return self;
});
// LightFactory class to represent the Light Factory building
var LightFactory = Building.expand(function (x, y, playerId) {
var self = Building.call(this, x, y, 'lightFactory', 'Light Factory', playerId, 2, 2, buildableRepository.getBuildableInfo('lightFactory').buildable);
// Additional properties and methods for LightFactory can be added here
return self;
});
var ConstructionYard = Building.expand(function (x, y, playerId) {
var self = Building.call(this, x, y, 'constructionYard', 'Construction Yard', playerId, 2, 2, buildableRepository.getBuildableInfo('constructionYard').buildable);
// Additional properties and methods for ConstructionYard can be added here
return self;
});
var BuildingProgressDisplay = Container.expand(function (parentIcon) {
var self = Container.call(this);
self.blinkRed = function () {
self.progressAsset.tint = 0xFF0000; // Set progress display color to red
self.setProgress(1);
self.progressAsset.alpha = 0.5; // Make it fully visible
self.visible = true; // Ensure it's visible
self.blinkInterval = LK.setInterval(function () {
self.progressAsset.alpha = self.progressAsset.alpha > 0 ? 0 : 0.5;
}, 250); // Blink effect
LK.setTimeout(function () {
if (self.blinkInterval) {
LK.clearInterval(self.blinkInterval);
self.progressAsset.alpha = 0.5;
self.progressAsset.tint = 0x00FF00;
self.hide();
}
}, 1500); // Blink effect
};
self.parentIcon = parentIcon;
self.progressAsset = self.attachAsset('buildingProgress', {
anchorX: 0,
anchorY: .5,
alpha: 0.5,
width: 0,
tint: 0x00FF00,
x: parentIcon.asset.x - parentIcon.asset.width / 2,
y: parentIcon.asset.y
});
self.setProgress = function (progress) {
self.progressAsset.width = parentIcon.asset.width * progress;
};
self.hide = function () {
self.visible = false;
};
self.show = function () {
self.visible = true;
};
return self;
});
var CancelActionButton = Container.expand(function (x, y) {
var self = Container.call(this);
self.asset = self.attachAsset('cancelAction', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y
});
// Add a label 'Cancel' below the cancel action button
self.label = new Text2('Cancel', {
size: 30,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
self.label.x = self.asset.x - 40;
self.label.y = self.asset.y + 120;
self.addChild(self.label);
self.asset.on('down', function () {
// Handle cancel action logic here
if (currentBuildingForPlacement) {
player1.spice += buildableRepository.getBuildableInfo(currentBuildingForPlacement.type).cost;
updateBaseInfo();
currentBuildingForPlacement.destroy();
currentBuildingForPlacement = null;
currentUserActionState = UserActionState.NAVIGATING;
self.visible = false; // Hide the CancelActionButton
}
// Restore labels on all BuildableItemIcon instances
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.restoreLabel();
}
});
console.log('Cancel action button pressed and building placement cancelled.');
});
return self;
});
// Command Panel class
var CommandPanel = Container.expand(function () {
var self = Container.call(this);
self.graphics = self.attachAsset('boardBackground', {
width: game.width,
height: game.height * 0.22,
color: 0x333333
});
// Set the center box position to the center of the screen
self.x = game.width / 2 - self.graphics.width / 2;
self.y = game.height - self.height + game.height * 0.01;
});
var IconBorder = Container.expand(function (x, y) {
var self = Container.call(this);
self.asset = self.attachAsset('iconBorder', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y
});
return self;
});
// Initialize game elements
// Map class to represent the grid-based map system
var Map = Container.expand(function () {
var self = Container.call(this);
// Method to clear spice from a specified cell
self.clearCellSpice = function (x, y) {
if (self.cells[x] && self.cells[x][y]) {
self.cells[x][y].spice = false;
if (self.cells[x][y].spiceAsset) {
self.cells[x][y].spiceAsset.destroy();
self.cells[x][y].spiceAsset = null;
}
// Update spiceSpots array
spiceSpots = spiceSpots.filter(function (spot) {
return spot.x !== x || spot.y !== y;
});
}
};
self.cells = [];
self.init = function (width, height) {
for (var x = 0; x < width; x++) {
self.cells[x] = [];
for (var y = 0; y < height; y++) {
// Initialize each cell with default terrain, height, resources
self.cells[x][y] = {
height: 0,
resources: null,
terrain: null,
asset: null,
building: null,
spiceAsset: null
};
}
}
};
self.initTerrain = function (width, height) {
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
// Initialize each cell with default terrain, height, resources
self.cells[x][y].terrain = globalTerrain[x][y] === 1 ? 'rock' : 'sand';
self.cells[x][y].asset = globalTerrain[x][y] === 1 ? new Rock(x, y) : new Sand(x, y);
}
}
};
self.currentViewCenter = {
x: 15,
y: 20
};
// Create a Text2 object to display the currentViewCenter coordinates
self.viewCenterText = new Text2('Center', {
size: 50,
fill: '#ffffff',
align: 'center'
});
// Position the text at the top center of the screen
LK.gui.top.addChild(self.viewCenterText);
self.render = function () {
self.removeChildren();
// Move tiles instead of the view
for (var x = viewPort.wCellX; x < viewPort.wCellX + viewPort.cellW; x++) {
for (var y = viewPort.wCellY; y < viewPort.wCellY + viewPort.cellH; y++) {
if (x >= 0 && x < self.cells.length && y >= 0 && y < self.cells[x].length) {
var cell = self.cells[x][y];
var asset = cell.asset;
var screenCoord = worldCellToScreenCoord(x, y);
var screenX = screenCoord.x;
var screenY = screenCoord.y;
asset.x = screenX;
asset.y = screenY;
if (!asset.parent) {
self.addChild(asset);
}
// Render spice terrain
if (cell.spice) {
var spiceAsset = cell.spiceAsset;
spiceAsset.x = screenX;
spiceAsset.y = screenY;
if (!spiceAsset.parent) {
self.addChild(spiceAsset);
}
}
// Render buildings
var building = cell.building;
if (building) {
building.asset.x = screenX - (building.cellW - 2) / 2 * tileSize;
building.asset.y = screenY - (building.cellH - 2) / 2 * tileSize;
self.addChild(building.asset);
if (building.assetEffect) {
self.addChild(building.assetEffect);
}
}
}
}
}
};
self.getNavigationMesh = function () {
// Create and return the navigation mesh for pathfinding
};
self.findPath = function (start, end, ignoreSpecificBuilding) {
console.log("findPath ", start, end);
// A* pathfinding algorithm implementation
// Avoid buildings by checking if the cell is occupied
var openSet = [];
var closedSet = [];
var path = [];
var startNode = {
x: start.x,
y: start.y,
f: 0,
g: 0,
h: 0,
parent: null
};
var endNode = {
x: end.x,
y: end.y,
f: 0,
g: 0,
h: 0,
parent: null
};
openSet.push(startNode);
while (openSet.length > 0) {
var lowestIndex = 0;
for (var i = 0; i < openSet.length; i++) {
if (openSet[i].f < openSet[lowestIndex].f) {
lowestIndex = i;
}
}
var currentNode = openSet[lowestIndex];
if (currentNode.x === endNode.x && currentNode.y === endNode.y) {
var curr = currentNode;
while (curr.parent) {
var prev = curr.parent;
if (prev) {
// Calculate relative move
var moveX = curr.x - prev.x;
var moveY = curr.y - prev.y;
console.log("Pushing : " + moveX, ',', moveY);
path.push({
cellX: moveX,
cellY: moveY
});
}
curr = curr.parent;
}
path.reverse();
console.log("Return 1", path);
return path;
}
openSet.splice(lowestIndex, 1);
closedSet.push(currentNode);
var neighbors = getNeighbors(currentNode);
for (i = 0; i < neighbors.length; i++) {
var neighbor = neighbors[i];
if (closedSet.findIndex(function (node) {
return node.x === neighbor.x && node.y === neighbor.y;
}) !== -1 || self.cells[neighbor.x][neighbor.y].building && !ignoreSpecificBuilding || self.cells[neighbor.x][neighbor.y].unit) {
continue;
}
var gScore = currentNode.g + 1; // 1 is the distance from a node to a neighbor
var gScoreIsBest = false;
if (openSet.findIndex(function (node) {
return node.x === neighbor.x && node.y === neighbor.y;
}) === -1) {
gScoreIsBest = true;
neighbor.h = heuristic(neighbor, endNode);
openSet.push(neighbor);
} else if (gScore < neighbor.g) {
gScoreIsBest = true;
}
if (gScoreIsBest) {
neighbor.parent = currentNode;
neighbor.g = gScore;
neighbor.f = neighbor.g + neighbor.h;
}
}
}
console.log("Return 2", path);
return path;
};
function heuristic(node, endNode) {
// Use Manhattan distance as heuristic
return Math.abs(node.x - endNode.x) + Math.abs(node.y - endNode.y);
}
function getNeighbors(node) {
// Get all valid neighbors (up, down, left, right)
var neighbors = [];
var directions = [{
x: -1,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: -1
}, {
x: 0,
y: 1
}, {
x: -1,
y: -1
}, {
x: -1,
y: 1
}, {
x: 1,
y: -1
}, {
x: 1,
y: 1
}];
directions.forEach(function (dir) {
var newX = node.x + dir.x;
var newY = node.y + dir.y;
if (newX >= 0 && newX < self.cells.length && newY >= 0 && newY < self.cells[0].length) {
neighbors.push({
x: newX,
y: newY
});
}
});
return neighbors;
}
self.lastInputPosition = null;
self.handleDrag = function (input) {
viewPort.wCellX = Math.max(0, Math.min(mapXSize - viewPort.cellW, viewPort.wCellX + input.x));
viewPort.wCellY = Math.max(0, Math.min(mapYSize - viewPort.cellH, viewPort.wCellY + input.y));
viewPort.wX = viewPort.wCellX * tileSize;
viewPort.wY = viewPort.wCellY * tileSize;
// Update the Text2 object with the new coordinates
self.viewCenterText.setText(viewPort.wCellX + ',' + viewPort.wCellY);
};
self.checkCollisions = function () {
// Handle collisions between units and obstacles
};
self.serialize = function () {
// Serialize map data to a file
};
self.load = function (data) {
// Load map data from a file
};
});
var Player = Container.expand(function (playerId, tint) {
var self = Container.call(this);
self.playerId = playerId;
self.resources = 0;
self.tint = 0xFFFFFF;
self.spice = 1000; // Initialize spice property with 1000
self.energy = 0; // Initialize spice property with 1000
// Initialize player-specific properties here
self.buildings = []; // Array to store all the player's buildings
self.units = []; // Array to store all the player's units
// ...
self.addUnit = function (unit) {
// Add the unit to the player's units array
self.units.push(unit);
// Update any unit-related properties or behaviors
// ...
};
self.addBuilding = function (building) {
// Add the building to the player's buildings array
self.buildings.push(building);
// Update the energy production based on the new building
self.updateEnergy();
// If the building can produce or store resources, update those as well
// ... (additional resource management code can be added here)
};
self.updateEnergy = function () {
var counter = 0;
// Iterate through all buildings owned by the player
self.buildings.forEach(function (building) {
// Sum the energy provided by the building
var buildingInfo = buildableRepository.getBuildableInfo(building.type);
console.log("Count energy of " + buildingInfo.name);
var buildingInfo = buildableRepository.getBuildableInfo(building.type);
counter = Math.min(Math.max(counter + buildingInfo.energy, 0), 100);
});
self.energy = counter;
console.log("Player " + self.playerId + " energy updated: " + self.energy);
};
return self;
});
var Rock = Container.expand(function (x, y) {
var self = Container.call(this);
self.asset = LK.getAsset('rock', {
x: x * 100,
y: y * 100
}); // Pre-create the asset for this cell
return self.asset;
});
var Sand = Container.expand(function (x, y) {
var self = Container.call(this);
self.asset = LK.getAsset('sand', {
x: x * 100,
y: y * 100
}); // Pre-create the asset for this cell
return self.asset;
});
var SelectionMarker = Container.expand(function () {
var self = Container.call(this);
self.selectionCross = self.attachAsset('selectionCross', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
self.selectionCircle = self.attachAsset('selectionCircle', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.alpha = 0.5;
self.visible = false;
// Initially, we display the selectionCross
self.addChild(self.selectionCross);
self.currentElement = null;
self.setOnElement = function (element) {
//console.log('SelectionMarker setOnElement:', element);
self.currentElement = element;
if (!element) {
self.visible = false;
return;
}
var elementDeltaX = 0;
var elementDeltaY = 0;
if (element.isBuilding) {
elementDeltaX = element.cellW / 2 * tileSize;
elementDeltaY = element.cellH / 2 * tileSize;
self.selectionCross.alpha = 1;
self.selectionCircle.alpha = 0;
}
if (element.isUnit) {
elementDeltaX = tileSize / 2;
elementDeltaY = tileSize / 2;
self.selectionCross.alpha = 0;
self.selectionCircle.alpha = 0.9;
}
var eltCoord = worldCellToScreenCell(element.cellX, element.cellY);
self.x = eltCoord.cellX * tileSize + elementDeltaX;
self.y = eltCoord.cellY * tileSize + elementDeltaY;
//console.log("setOnElement => " + element.x + ',' + element.y, ' => ' + self.x + ',' + self.y);
if (self.y > mapHeight) {
self.visible = false;
} else {
self.visible = true;
}
};
self.hide = function () {
console.log('SelectionMarker hidden');
self.visible = false;
};
return self;
});
var TargetMoveCursor = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('targetMoveCursor', {
anchorX: 0.5,
anchorY: 0.5
});
self.updatePosition = function (position) {
self.x = position.x;
self.y = position.y;
};
game.on('move', function (obj) {
if (currentUserActionState != UserActionState.SET_ORDER_TARGET) {
return;
}
//console.log("moving cursor");
var pos = obj.event.getLocalPosition(game);
if (pos.y > game.height * 0.80) {
self.visible = false;
} else {
self.visible = true;
self.updatePosition(pos);
}
});
return self;
});
var Unit = Container.expand(function (cellX, cellY, playerId, type) {
var self = Container.call(this);
self.playerId = playerId;
self.type = type;
self.isUnit = true;
var unitInfo = unitRepository.getUnitInfo(type);
self.name = unitInfo.name;
self.health = unitInfo.health; // Range for attacking
self.isDestroyed = false;
self.sightRange = unitInfo.sightRange;
self.attackRange = unitInfo.attackRange; // Range for attacking
self.attackDamage = unitInfo.attackDamage;
self.attackSpeed = unitInfo.attackSpeed; // Speed of attack (time between attacks)
self.attackMode = 0; // 0 = not attacking, 1 = attacking, 2 = moving to target
self.lastAttackTime = 0;
self.actions = unitInfo.actions;
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
tint: getPlayerTint(playerId)
});
self.speed = 200; // Set the unit speed
self.cellX = cellX;
self.cellY = cellY;
self.x = cellX * tileSize + tileSize / 2;
self.y = cellY * tileSize + tileSize / 2;
self.cellW = 1;
self.cellH = 1;
self.onArrivedCallback = null;
console.log("New unit " + self.name + ' at ', self.x, ',', self.y, ' [' + self.cellX + ',' + self.cellY + ']');
self.asset.on('down', function () {
if (currentUserActionState === UserActionState.NAVIGATING) {
console.log("Unit selected");
currentSelection = self;
selectionMarker.setOnElement(currentSelection);
currentUserActionState = UserActionState.GIVING_ORDERS;
updateActionBoard();
}
});
self.moveAlongPath = function (path, callback) {
console.log("moveAlongPath", path, callback);
self.path = path;
self.pathIndex = 0;
self.isMoving = true;
self.onArrivedCallback = callback;
};
self.updateMovement = function (delta) {
if (!self.isMoving) {
return;
}
if (!self.path || self.pathIndex >= self.path.length) {
console.log("updateMovement will stop ", self.path, self.pathIndex, self.onArrivedCallback);
self.isMoving = false;
if (self.onArrivedCallback) {
console.log("updateMovement call onArrivedCallback 1");
self.onArrivedCallback();
self.onArrivedCallback = null;
}
return;
}
var targetPathStep = self.path[self.pathIndex];
var targetCell = {
cellX: self.cellX + targetPathStep.cellX,
cellY: self.cellY + targetPathStep.cellY
};
var targetCoords = worldCellToScreenCoord(targetCell.cellX, targetCell.cellY);
var dx = targetCoords.x + tileSize / 2 - self.x;
var dy = targetCoords.y + tileSize / 2 - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var step = self.speed * delta;
if (distance < step) {
self.x = targetCoords.x + tileSize / 2;
self.y = targetCoords.y + tileSize / 2;
if (self.y > mapHeight) {
self.visible = false;
} else {
self.visible = true;
}
// Update map
gameMap.cells[self.cellX][self.cellY].unit = null;
self.cellX = targetCell.cellX;
self.cellY = targetCell.cellY; // Update map
gameMap.cells[self.cellX][self.cellY].unit = self;
self.pathIndex++;
if (self.pathIndex >= self.path.length) {
self.isMoving = false;
if (self.onArrivedCallback) {
console.log("updateMovement call onArrivedCallback 2");
self.onArrivedCallback();
self.onArrivedCallback = null;
}
}
} else {
var angle = Math.atan2(dy, dx);
self.asset.rotation = angle - Math.PI / 2;
self.x += Math.cos(angle) * step;
self.y += Math.sin(angle) * step;
if (self.y > mapHeight) {
self.visible = false;
} else {
self.visible = true;
}
}
if (selectionMarker.currentElement == self) {
selectionMarker.setOnElement(self);
}
};
self.handleDrag = function () {
// OK
var newCoord = worldCellToScreenCoord(self.cellX, self.cellY);
self.x = newCoord.x + tileSize / 2;
self.y = newCoord.y + tileSize / 2;
};
self.guard = function () {
//console.log("Guard area");
var enemies = [];
var allUnits = player1.units.concat(player2.units);
allUnits.forEach(function (unit) {
if (unit.playerId !== self.playerId) {
var distance = calculateDistance(self, unit);
if (distance <= self.sightRange) {
enemies.push(unit);
}
}
});
if (enemies.length > 0) {
console.log(enemies.length + " enemies spotted.");
self.startAttack(enemies[0]); // Start attacking the first enemy spotted
}
};
self.attackTarget = null; // Target unit to attack
self.startAttack = function (target) {
self.attackTarget = target;
self.attackMode = 1;
self.lastAttackTime = 0;
};
self.update = function () {
if (self.attackMode && self.attackTarget && !self.attackTarget.destroyed) {
// Wait for attack speed before firing again
if (Date.now() - self.lastAttackTime < self.attackSpeed * 1000) {
return;
}
self.lastAttackTime = Date.now();
// Check if target is in range
var distanceToTarget = calculateDistance(self, self.attackTarget);
if (distanceToTarget <= self.attackRange) {
// Rotate towards the target
var angleToTarget = calculateAngle(self, self.attackTarget);
self.asset.rotation = angleToTarget;
// Fire at the target
self.fire();
} else {
self.attackMode = 2;
// Move towards the target if it's out of range
var path = gameMap.findPath({
x: self.cellX,
y: self.cellY
}, {
x: self.attackTarget.cellX,
y: self.attackTarget.cellY
});
self.moveAlongPath(path, function () {
self.attackMode = 1;
});
}
} else if (!self.isMoving) {
self.guard();
}
};
self.fire = function () {
// Code to perform firing action
console.log("Firing at target!", self.attackTarget);
if (self.attackTarget) {
// Reduce target's health
self.attackTarget.damage(self.attackDamage);
if (!self.attackTarget || self.attackTarget.health <= 0) {
self.attackMode = 0;
}
}
};
self.damage = function (points) {
console.log("Unit " + self.name + " hit! " + points);
self.health -= points;
self.asset.tint = 0xff0000;
LK.setTimeout(function () {
self.asset.tint = 0xFFFFFF;
}, 300);
if (self.health <= 0) {
console.log("Unit " + self.name + " destroyed");
self.assetEffect = self.attachAsset('buildingExplosion', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
width: 10,
height: 10
});
var growExplosion = function growExplosion(size) {
if (size < 150) {
self.assetEffect.width = size;
self.assetEffect.height = size;
LK.setTimeout(function () {
growExplosion(size + 3);
}, 5);
} else {
// Remove building from gameMap cells and game
console.log("unit destruction...");
self.isDestroyed = true;
gameMap.cells[self.cellX][self.cellY].unit = null;
game.removeChild(self);
if (self.assetEffect) {
game.removeChild(self.assetEffect);
self.assetEffect.destroy();
}
// Remove the unit from the player's units array
var playerUnits = self.playerId === player1.playerId ? player1.units : player2.units;
var unitIndex = playerUnits.indexOf(self);
if (unitIndex !== -1) {
playerUnits.splice(unitIndex, 1);
}
self.destroy();
}
};
growExplosion(1);
}
};
self.stopAttack = function () {
self.attackMode = false;
self.attackTarget = null;
};
self.stopOtherActions = function () {
console.log("Unit stopOtherActions :");
self.attackMode = false; // Stop attacking
};
return self;
});
var UnitQuad = Unit.expand(function (x, y, playerId) {
var self = Unit.call(this, x, y, playerId, 'unitQuad');
// Additional properties and methods for UnitQuad can be added here
return self;
});
var UnitHarvester = Unit.expand(function (x, y, playerId) {
var self = Unit.call(this, x, y, playerId, 'unitHarvester');
self.harvestedSpiceCapacity = 600;
self.harvestedSpiceOnTripCounter = 0;
self.harvestedSpiceOnCellCounter = 0;
self.harvestingMode = 0; // 0 : Idle / 1 : harvesting / 2 : returning / 3 : unload
self.harvestSpeed = 2; // TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!! 0.25; // Adjust this value to control the speed of harvesting
self.unloadSpeed = 1; // TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!! 0.25; // Adjust this value to control the speed of harvesting
self.startHarvesting = function () {
console.log("startHarvesting...");
var closestSpiceSpot = findClosestSpiceSpot(self.cellX, self.cellY);
if (closestSpiceSpot) {
console.log("closestSpiceSpot :", closestSpiceSpot);
var path = gameMap.findPath({
x: self.cellX,
y: self.cellY
}, {
x: closestSpiceSpot.x,
y: closestSpiceSpot.y
});
self.moveAlongPath(path);
self.harvestingMode = 1;
}
};
self.startUnloading = function () {
self.harvestingMode = 3;
console.log("Unloading spice...");
};
self.update = function () {
if (self.harvestingMode == 1 && self.harvestedSpiceOnCellCounter < 200 && gameMap.cells[self.cellX][self.cellY].spice) {
self.harvestedSpiceOnTripCounter += self.harvestSpeed;
self.harvestedSpiceOnCellCounter += self.harvestSpeed;
if (self.harvestedSpiceOnCellCounter >= 200) {
self.asset.tint = 0xFFFFFF;
gameMap.clearCellSpice(self.cellX, self.cellY);
if (self.harvestedSpiceOnTripCounter >= self.harvestedSpiceCapacity) {
self.harvestingMode = 2; // Returning
self.harvestedSpiceOnCellCounter = 0;
var closestRefinery = findClosestRefinery(self);
if (closestRefinery) {
var path = gameMap.findPath({
x: self.cellX,
y: self.cellY
}, {
x: closestRefinery.cellX,
y: closestRefinery.cellY
}, closestRefinery);
if (path.length > 0) {
console.log("Returning to Refiniery...");
self.moveAlongPath(path, self.startUnloading);
} else {
console.warn("No path to Refinery !");
}
}
} else {
self.harvestedSpiceOnCellCounter = 0;
self.startHarvesting();
}
} else {
// Make the unit blink in orange more smoothly
var blinkPhase = Math.sin(Date.now() * 0.01) * 0.5 + 0.5;
self.asset.tint = blinkPhase < 0.5 ? 0xFFA500 : 0xFFFFFF;
self.asset.rotation += 45 / 400 * (Math.PI / 180);
}
}
if (self.harvestingMode == 3) {
self.harvestedSpiceOnTripCounter -= self.unloadSpeed;
// Add the unloaded spice to the player's spice counter
player1.spice += self.unloadSpeed; // TODO : update for Player 2
updateBaseInfo();
// Make the unit blink in green to indicate unloading
var blinkPhase = Math.sin(Date.now() * 0.01) * 0.5 + 0.5;
self.asset.tint = blinkPhase < 0.5 ? 0x00FFA5 : 0xFFFFFF;
if (self.harvestedSpiceOnTripCounter <= 0) {
console.log("Unloading finished...");
self.asset.tint = 0xFFFFFF;
self.harvestedSpiceOnTripCounter = 0;
self.harvestingMode = 0; // Reset harvesting mode
self.startHarvesting(); // Start a new harvesting cycle
}
}
};
self.stopOtherActions = function () {
console.log("UnitHarvester stopOtherActions :");
self.harvestingMode = 0; // Reset harvesting mode
self.asset.tint = 0xFFFFFF; // Restore the unit tint
};
// Additional properties and methods for UnitHarvester can be added here
return self;
});
//{15.1}
var UnitRepository = Container.expand(function () {
var self = Container.call(this);
self.units = {
'unitHarvester': {
'name': 'Spice Harvester',
'cost': 400,
'energy': 0,
'constructionTime': 5,
'className': 'UnitHarvester',
'actions': ['harvest', 'move', 'return'],
'health': 50,
'sightRange': 4,
'attackRange': 0,
'attackSpeed': 0,
'attackDamage': 0
},
'unitQuad': {
'name': 'Quad',
'cost': 300,
'energy': 0,
'constructionTime': 3,
'className': 'UnitQuad',
'actions': ['move', 'attack', 'return'],
'health': 3,
//10, TEMP DEBUG !!!
'sightRange': 4,
'attackRange': 2,
'attackSpeed': 2,
'attackDamage': 1
}
// Add more units as needed
};
self.getUnitInfo = function (type) {
return self.units[type];
};
return self;
});
/****
* Initialize Game
****/
// Event listeners
// Instantiate and initialize the Map
var game = new LK.Game({
backgroundColor: 0x000000 // Init game with black background
});
/****
* Game Code
****/
// Global function to calculate the angle between two points
function calculateAngle(element1, element2) {
var dx = element2.cellX - element1.cellX;
var dy = element2.cellY - element1.cellY;
if (dx === 0 && dy === 0) {
return 0; // Default angle or another appropriate value
}
return Math.atan2(dy, dx) - Math.PI / 2;
}
// Global function to calculate the distance between two points
function calculateDistance(element1, element2) {
var dx = element1.cellX - element2.cellX;
var dy = element1.cellY - element2.cellY;
return Math.floor(Math.sqrt(dx * dx + dy * dy));
}
// Global function to find the closest refinery to a given unit
function findClosestRefinery(unit) {
var closestRefinery = null;
var minDistance = Number.MAX_VALUE;
var playerBuildings = unit.playerId === player1.playerId ? player1.buildings : player2.buildings;
console.log("Player has " + playerBuildings.length + " buildings");
playerBuildings.forEach(function (building) {
if (building instanceof SpiceRefinery) {
console.log("Found Refinery at " + building.cellX + "," + building.cellY);
var dx = unit.cellX - building.cellX;
var dy = unit.cellY - building.cellY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
closestRefinery = building;
minDistance = distance;
}
}
});
console.log("OK result ", closestRefinery);
return closestRefinery ? {
cellX: closestRefinery.cellX,
cellY: closestRefinery.cellY
} : null;
}
function findClosestConstructionYard(unit) {
var closestBuilding = null;
var minDistance = Number.MAX_VALUE;
var playerBuildings = unit.playerId === player1.playerId ? player1.buildings : player2.buildings;
console.log("Player has " + playerBuildings.length + " buildings");
playerBuildings.forEach(function (building) {
if (building instanceof ConstructionYard) {
console.log("Found ConstructionYard at " + building.cellX + "," + building.cellY);
var dx = unit.cellX - building.cellX;
var dy = unit.cellY - building.cellY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
closestBuilding = building;
minDistance = distance;
}
}
});
console.log("OK result ", closestBuilding);
return closestBuilding ? {
cellX: closestBuilding.cellX,
cellY: closestBuilding.cellY
} : null;
}
function spawnUnit(unitType, x, y, playerId) {
var unitInfo = unitRepository.getUnitInfo(unitType);
var unit;
switch (unitInfo.className) {
case 'UnitHarvester':
unit = new UnitHarvester(x, y, playerId);
// Find the first available place around the refinery
var path = findAvailablePositionAroundBuilding(x, y);
if (path.length > 0) {
unit.moveAlongPath(path, function () {
harvestActionLogic(unit);
});
}
break;
case 'UnitQuad':
unit = new UnitQuad(x, y, playerId);
var path = findAvailablePositionAroundBuilding(x, y);
if (path.length > 0) {
unit.moveAlongPath(path, function () {});
}
break;
// Add additional cases for other unit classes as needed
default:
throw new Error('Unknown unit class: ' + unitInfo.className);
}
gameMap.cells[x][y].unit = unit;
if (playerId === player1.playerId) {
player1.addUnit(unit);
} else if (playerId === player2.playerId) {
player2.addUnit(unit);
}
game.addChild(unit);
return unit;
}
function findAvailablePositionAroundBuilding(buildingX, buildingY) {
var directions = [{
x: -1,
y: 0
}, {
x: 1,
y: 0
}, {
x: 0,
y: -1
}, {
x: 0,
y: 1
}, {
x: -1,
y: -1
}, {
x: 1,
y: 1
}, {
x: -1,
y: 1
}, {
x: 1,
y: -1
}];
for (var i = 0; i < directions.length; i++) {
var checkX = buildingX + directions[i].x;
var checkY = buildingY + directions[i].y;
if (gameMap.cells[checkX] && gameMap.cells[checkX][checkY] && !gameMap.cells[checkX][checkY].unit && !gameMap.cells[checkX][checkY].building) {
return [{
cellX: directions[i].x,
cellY: directions[i].y
}]; // Return relative move to the first available cell
}
}
return []; // No available position found
}
function findAvailableCellAtRange(targetX, targetY, range) {
console.log("findAvailableCellAtRange", targetX, targetY, range);
range = range || 1;
for (var dx = -range; dx <= range; dx++) {
for (var dy = -range; dy <= range; dy++) {
var checkX = targetX + dx;
var checkY = targetY + dy;
if (gameMap.cells[checkX] && gameMap.cells[checkX][checkY] && !gameMap.cells[checkX][checkY].unit && !gameMap.cells[checkX][checkY].building && (dx !== 0 || dy !== 0)) {
console.log("Found available cell at", checkX, checkY);
return {
cellX: checkX,
cellY: checkY
}; // Return the absolute cell
}
}
}
console.log("No available position found");
return []; // No available position found
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function moveActionLogic(targetUnit, destinationX, destinationY) {
console.log("MoveActionLogic...", destinationX, ',', destinationY);
if (targetUnit && targetUnit.isUnit) {
targetUnit.stopOtherActions();
var path = gameMap.findPath({
x: targetUnit.cellX,
y: targetUnit.cellY
}, {
x: destinationX,
y: destinationY
});
targetUnit.moveAlongPath(path);
}
}
function attackActionLogic(theUnit, targetCellX, targetCellY) {
console.log("AttackActionLogic...", targetCellX, ',', targetCellY);
var target = gameMap.cells[targetCellX][targetCellY].unit || gameMap.cells[targetCellX][targetCellY].building;
if (theUnit && theUnit.isUnit && target) {
theUnit.stopOtherActions();
var attackPosition = findAvailableCellAtRange(targetCellX, targetCellY, theUnit.range);
var path = gameMap.findPath({
x: theUnit.cellX,
y: theUnit.cellY
}, {
x: attackPosition.cellX,
y: attackPosition.cellY
});
theUnit.moveAlongPath(path, function () {
theUnit.startAttack(target);
});
}
}
function harvestActionLogic(targetUnit, destinationX, destinationY) {
console.log("HarvestActionLogic...", targetUnit, ' pos=', destinationX, ',', destinationY);
if (targetUnit && targetUnit.isUnit && targetUnit.type === "unitHarvester") {
var path = [];
if (destinationX && destinationY) {
path = gameMap.findPath({
x: targetUnit.cellX,
y: targetUnit.cellY
}, {
x: destinationX,
y: destinationY
});
}
if (path.length > 0) {
console.log("Valid harvesting, path:", path);
targetUnit.moveAlongPath(path, targetUnit.startHarvesting);
} else {
console.log("No path, direct harvesting ");
targetUnit.startHarvesting();
}
}
}
function retreatActionLogic(targetUnit) {
console.log("Retreat Action Logic...");
if (targetUnit && targetUnit.isUnit) {
targetUnit.stopOtherActions();
var baseBuilding = findClosestConstructionYard(targetUnit);
if (!baseBuilding) {
if (targetUnit.player1Id === player1.playerId) {
baseBuilding = {
cellX: rockIlot1Center.x,
cellY: rockIlot1Center.y
};
} else {
baseBuilding = {
cellX: rockIlot2Center.x,
cellY: rockIlot2Center.y
};
}
}
console.log("Building = ", baseBuilding);
var freePosition = findAvailablePositionAroundBuilding(baseBuilding.cellX, baseBuilding.cellY);
console.log("freePosition = ", freePosition);
if (freePosition && freePosition.length === 1) {
baseBuilding.cellX += freePosition[0].cellX;
baseBuilding.cellY += freePosition[0].cellY;
}
var path = gameMap.findPath({
x: targetUnit.cellX,
y: targetUnit.cellY
}, {
x: baseBuilding.cellX,
y: baseBuilding.cellY
}, true);
console.log("Retreat path:", path);
targetUnit.moveAlongPath(path);
}
}
function highlightBorder(borderX) {
var iconBorders = game.children.filter(function (child) {
return child instanceof IconBorder;
});
for (var i = 0; i < iconBorders.length; i++) {
var iconAsset = iconBorders[i].asset;
iconAsset.tint = iconAsset.x === borderX ? 0xffAAAA : 0xFFFFFF;
}
}
function getEnergyColor(energy) {
if (energy < 25) {
return '#ff1100'; // Red color for low energy
} else if (energy < 75) {
return '#d26600'; // Orange color for medium energy
} else {
return '#037d50'; // Original green color for high energy
}
}
function checkBuildingPlacement(newBuilding, cellX, cellY) {
if (newBuilding && 'cellW' in newBuilding) {
for (var w = 0; w < newBuilding.cellW; w++) {
for (var h = 0; h < newBuilding.cellH; h++) {
if (!gameMap.cells[cellX + w] || !gameMap.cells[cellX + w][cellY + h] || gameMap.cells[cellX + w][cellY + h].terrain !== 'rock' || gameMap.cells[cellX + w][cellY + h].building) {
return false;
}
}
}
}
return true;
}
function enqueueBuildable(selectedBuildable, progressDisplay) {
console.log("enqueueBuildable...", selectedBuildable);
if (!currentSelection || !currentSelection.buildable.includes(selectedBuildable.type)) {
console.log("Cannot build this item here.");
return;
}
var buildableInfo = buildableRepository.getBuildableInfo(selectedBuildable.type);
if (buildableInfo && player1.spice < buildableInfo.cost) {
console.log("Not enough resources to build.");
progressDisplay.blinkRed();
return;
}
updateBaseInfo();
var constructionTime = buildableInfo.constructionTime;
var buildable;
switch (buildableInfo.className) {
case 'ConstructionYard':
buildable = new ConstructionYard(0, 0, player1.playerId);
break;
case 'WindTrap':
buildable = new WindTrap(0, 0, player1.playerId);
break;
case 'SpiceRefinery':
buildable = new SpiceRefinery(0, 0, player1.playerId);
break;
case 'LightFactory':
buildable = new LightFactory(0, 0, player1.playerId);
break;
case 'UnitQuad':
buildable = new UnitQuad(0, 0, player1.playerId);
break;
// Add cases for other buildings as needed
}
if (!buildable) {
console.error('Building object is not defined.');
return;
}
buildable.constructionProgress = 0;
buildable.constructionTimer = LK.setInterval(function () {
buildable.constructionProgress += 0.1;
var progressFraction = buildable.constructionProgress / constructionTime;
buildable.progressDisplay.setProgress(progressFraction);
// Deduct spice progressively based on construction progress
var costPerTick = buildableInfo.cost * 0.1 / constructionTime;
player1.spice -= costPerTick;
updateBaseInfo();
// When construction is complete...
if (buildable.constructionProgress >= constructionTime) {
LK.clearInterval(buildable.constructionTimer);
console.log(selectedBuildable.type + " construction completed.");
buildable.constructionTimer = null;
buildable.progressDisplay.hide();
if (buildable.isBuilding) {
console.log("Building Ready for placement ", buildable);
if (progressDisplay.parentIcon && typeof progressDisplay.parentIcon.setLabelToPlace === 'function') {
progressDisplay.parentIcon.setLabelToPlace(); // Change label text to 'Place'
}
currentBuildingForPlacement = buildable;
}
if (buildable.isUnit) {
console.log("Unit Ready to spawn ", buildable);
var spawnCell = {
cellX: 0,
cellY: 0
};
if (selectedBuildable && selectedBuildable.sourceFactory) {
console.log("Found source factory ", selectedBuildable.sourceFactory);
spawnCell.cellX = selectedBuildable.sourceFactory.cellX;
spawnCell.cellY = selectedBuildable.sourceFactory.cellY;
} else {
console.log("No source factory found ", selectedBuildable);
}
spawnUnit(buildable.type, spawnCell.cellX, spawnCell.cellY, player1.playerId);
}
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.asset.interactive = true;
child.alpha = 1.0;
}
});
}
}, 100);
buildable.progressDisplay = progressDisplay;
currentSelection.buildingQueue.push(buildable);
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.asset.interactive = false;
}
});
// Instantiate and display the CancelActionButton on the command panel
cancelActionBtn = new CancelActionButton(commandPanel.x + commandPanel.width - 200, commandPanel.y + 200);
game.addChild(cancelActionBtn);
}
var applyCellOccupation = function applyCellOccupation(cellX, cellY) {
var building = gameMap.cells[cellX][cellY].building;
if (building) {
for (var w = 0; w < building.cellW; w++) {
for (var h = 0; h < building.cellH; h++) {
if (cellX + w < gameMap.cells.length && cellY + h < gameMap.cells[0].length) {
console.log("applyCellOccupation", cellX, cellY, " to ", cellX + w, cellY + h);
gameMap.cells[cellX + w][cellY + h].building = building;
}
}
}
}
};
var getPlayerTint = function getPlayerTint(id) {
return player1.playerId === id ? player1.tint : player2.tint;
};
var gameIsRunning = false;
var mapXSize = 20 * 2;
var mapYSize = 21 * 2;
var tileSize = 100;
var mapHeight = game.height * 0.80;
var viewSize = Math.ceil(Math.max(game.width, game.height) / 2 / 100);
var viewPort = {
wX: 0,
wY: 0,
wCellX: 0,
wCellY: 0,
cellW: Math.floor(game.width / tileSize) + 1,
// ~20
cellH: Math.floor(mapHeight / tileSize) + 1 // ~21
};
// Init view port
viewPort.wCellX = Math.max(0, Math.min(mapXSize - viewPort.cellW, viewPort.wCellX));
viewPort.wCellY = Math.max(0, Math.min(mapYSize - viewPort.cellH, viewPort.wCellY));
viewPort.wX = viewPort.wCellX * tileSize;
viewPort.wY = viewPort.wCellY * tileSize;
var dragSpeed = 0.15;
var gameMap = new Map();
var buildableRepository = new BuildableRepository();
var unitRepository = new UnitRepository();
var actionRepository = new ActionRepository();
// Define user action states
var UserActionState = {
NAVIGATING: 'navigating',
BUILDING: 'building',
PLACING_BUILDINGS: 'placing_buildings',
GIVING_ORDERS: 'giving_orders',
SET_ORDER_TARGET: 'set_order_target'
};
// Current state of user action
var currentUserActionState = UserActionState.NAVIGATING;
var globalTerrain = null;
var player1 = null;
var player2 = null;
var spiceSpots = [];
// Global variable to keep track of the previous energy level
var previousEnergyLevel = 0;
var currentSelection = null;
var currentBuildingForPlacement = null;
var selectionMarker = null;
var tickCounter = 0;
var fps = 0;
// Gui
var commandPanel = null;
var player1SpiceText = null;
var player1EnergyText = null;
var energyOffsetSufix = '% ';
var currentSelectionText = null;
var fpsText = null;
var targetMoveCursor = null;
var cancelActionBtn = null;
var rockIlot1Center = {
x: 0,
y: 0
};
// Prepare the map
function prepareMap() {
globalTerrain = new Array(mapXSize).fill(0).map(function () {
return new Array(mapYSize).fill(0);
});
gameMap = new Map();
gameMap.init(mapXSize, mapYSize); // Initialize with a 20x20 grid
// Define a fixed rock ilot near the center left of the map
rockIlot1Center = {
x: 5,
//Math.floor(mapXSize * 0.15), // TEMP DEBUG !!!// TEMP DEBUG !!!// TEMP DEBUG !!!
y: 7 //Math.floor(mapYSize * 0.25)// TEMP DEBUG !!!// TEMP DEBUG !!!// TEMP DEBUG !!!
};
console.log("rockIlot1Center : ", rockIlot1Center);
var rockIlot1Radius = 4;
for (var x = rockIlot1Center.x - rockIlot1Radius; x <= rockIlot1Center.x + rockIlot1Radius; x++) {
for (var y = rockIlot1Center.y - rockIlot1Radius; y <= rockIlot1Center.y + rockIlot1Radius; y++) {
if (x >= 0 && x < mapXSize && y >= 0 && y < mapYSize) {
globalTerrain[x][y] = 1;
}
}
}
console.log("rockIlot1Center at " + rockIlot1Center.x + ',' + rockIlot1Center.y);
var initialConstructionYard = new ConstructionYard(rockIlot1Center.x, rockIlot1Center.y, 1);
gameMap.cells[rockIlot1Center.x][rockIlot1Center.y].building = initialConstructionYard;
applyCellOccupation(rockIlot1Center.x, rockIlot1Center.y);
player1.addBuilding(initialConstructionYard);
// Add a new harvester to player 1
// Define a fixed rock ilot near the center right of the map
var rockIlot2Center = {
x: 10,
//Math.floor(mapXSize * 0.85),// TEMP DEBUG !!!// TEMP DEBUG !!!// TEMP DEBUG !!!
y: 14 //Math.floor(mapYSize * 0.5)// TEMP DEBUG !!!// TEMP DEBUG !!!// TEMP DEBUG !!!
};
var rockIlot2Radius = 4;
for (var x = rockIlot2Center.x - rockIlot2Radius; x <= rockIlot2Center.x + rockIlot2Radius; x++) {
for (var y = rockIlot2Center.y - rockIlot2Radius; y <= rockIlot2Center.y + rockIlot2Radius; y++) {
if (x >= 0 && x < mapXSize && y >= 0 && y < mapYSize) {
globalTerrain[x][y] = 1;
}
}
}
console.log("rockIlot2Center at " + rockIlot2Center.x + ',' + rockIlot2Center.y);
var initialConstructionYard2 = new ConstructionYard(rockIlot2Center.x, rockIlot2Center.y, 2);
gameMap.cells[rockIlot2Center.x][rockIlot2Center.y].building = initialConstructionYard2;
applyCellOccupation(rockIlot2Center.x, rockIlot2Center.y);
player2.addBuilding(initialConstructionYard2);
// Spawn a Quad for player 2 next to its ConstructionYard
var quadForPlayer2 = new UnitQuad(rockIlot2Center.x + 2, rockIlot2Center.y, 2);
gameMap.cells[rockIlot2Center.x + 2][rockIlot2Center.y].unit = quadForPlayer2;
player2.addUnit(quadForPlayer2);
game.addChild(quadForPlayer2);
gameMap.initTerrain(mapXSize, mapYSize); // Initialize with a 20x20 grid
initSpiceSpots();
console.log('ConstructionYard1 cell :', gameMap.cells[rockIlot1Center.x][rockIlot1Center.y]);
selectionMarker = new SelectionMarker();
// Add the map to the game
game.addChild(gameMap);
game.addChild(selectionMarker);
}
function initSpiceSpots() {
var quarterWidth = Math.floor(mapXSize / 2);
var quarterHeight = Math.floor(mapYSize / 2);
for (var i = 0; i < 4; i++) {
var startX = i % 2 * quarterWidth;
var startY = Math.floor(i / 2) * quarterHeight;
var ilotCenterX = startX + Math.floor(Math.random() * quarterWidth);
var ilotCenterY = startY + Math.floor(Math.random() * quarterHeight);
var ilotRadius = 2 + Math.floor(Math.random() * 3); // Radius between 2 and 4
for (var x = ilotCenterX - ilotRadius; x <= ilotCenterX + ilotRadius; x++) {
for (var y = ilotCenterY - ilotRadius; y <= ilotCenterY + ilotRadius; y++) {
if (x >= 0 && x < mapXSize && y >= 0 && y < mapYSize && globalTerrain[x][y] === 0) {
globalTerrain[x][y] = 2; // Mark as spice
spiceSpots.push({
x: x,
y: y
});
gameMap.cells[x][y].spice = true;
gameMap.cells[x][y].spiceAsset = LK.getAsset('terrainSpice', {
x: x * 100,
y: y * 100
});
}
}
}
}
}
//#region Event listeners
var dragStart = null;
var dragDelta = 0;
function handleDragStart(obj) {
console.log("Drag start...");
dragStart = obj.event.getLocalPosition(game);
}
function handleDragMove(obj) {
var currentPos = obj.event.getLocalPosition(game);
var input = {
x: currentPos.x,
y: currentPos.y
};
updateMouseCoords(input.x, input.y);
if (currentUserActionState === UserActionState.PLACING_BUILDINGS) {
var cellCoord = screenCoordToWorldCell(input.x, input.y);
var cellX = cellCoord.wCellX;
var cellY = cellCoord.wCellY;
var isValidPlacement = checkBuildingPlacement(currentBuildingForPlacement, cellX, cellY);
if (currentBuildingForPlacement && currentBuildingForPlacement.asset) {
currentBuildingForPlacement.asset.tint = isValidPlacement ? 0x00FF00 : 0xFF0000;
currentBuildingForPlacement.visible = input.y + currentBuildingForPlacement.cellH * tileSize < mapHeight;
}
currentBuildingForPlacement.x = (cellX + currentBuildingForPlacement.cellW / 2) * tileSize;
currentBuildingForPlacement.y = (cellY + currentBuildingForPlacement.cellH / 2) * tileSize;
} else if (dragStart) {
var deltaX = currentPos.x - dragStart.x;
var deltaY = currentPos.y - dragStart.y;
var inputPosition = {
x: Math.round(-deltaX / tileSize),
y: Math.round(-deltaY / tileSize)
};
var allUnits = player1.units.concat(player2.units);
allUnits.forEach(function (unit) {
unit.handleDrag(inputPosition);
//unit.visible = false;
});
gameMap.handleDrag(inputPosition);
dragDelta = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
selectionMarker.setOnElement(currentSelection);
}
}
function handleDragEnd(obj) {
var currentPos = obj.event.getLocalPosition(game);
var input = {
x: currentPos.x,
y: currentPos.y
};
console.log("Clicked at " + input.x + ',' + input.y);
if (input.y > game.height * 0.80) {
console.log("Clicked on board at " + input.x + ',' + input.y);
dragStart = null;
dragDelta = 0;
return;
}
if (currentUserActionState === UserActionState.PLACING_BUILDINGS) {
var cellCoord = screenCoordToWorldCell(input.x, input.y);
var cellX = cellCoord.wCellX;
var cellY = cellCoord.wCellY;
var isValidPlacement = checkBuildingPlacement(currentBuildingForPlacement, cellX, cellY);
if (!isValidPlacement) {
return;
}
console.log("Placing building ", currentBuildingForPlacement, " at " + cellX + ',' + cellY);
handleBuildingPlacement(currentBuildingForPlacement, cellX, cellY);
currentUserActionState = UserActionState.NAVIGATING;
dragStart = null;
dragDelta = 0;
} else if (currentUserActionState === UserActionState.SET_ORDER_TARGET) {
if (currentSelection && currentSelection.isUnit) {
var cellCoord = screenCoordToWorldCell(input.x, input.y); //convertCoordsToCell(input.x, input.y);
currentSelection.destinationTarget = {
x: cellCoord.wCellX,
y: cellCoord.wCellY
};
// Call the action handler with the target position
if (currentSelection.actions && currentSelection.selectedAction) {
console.log(currentSelection.selectedAction + ' received ');
var actionInfo = actionRepository.getActionInfo(currentSelection.selectedAction);
if (actionInfo && actionInfo.handler) {
console.log('Call action handler : ', actionInfo);
actionInfo.handler(currentSelection, cellCoord.wCellX, cellCoord.wCellY);
}
}
if (targetMoveCursor) {
console.log('hiding move cursor ...');
targetMoveCursor.visible = false;
}
}
highlightBorder();
currentUserActionState = UserActionState.NAVIGATING;
dragStart = null;
dragDelta = 0;
} else {
// Normal click
var clickCell = screenCoordToWorldCell(input.x, input.y);
var cellX = clickCell.wCellX;
var cellY = clickCell.wCellY;
console.log("Click at cell " + cellX + ',' + cellY);
var allUnits = player1.units.concat(player2.units);
allUnits.forEach(function (unit) {
unit.handleDrag(input);
unit.visible = true;
});
var deltaToSelection = 99;
if (currentSelection && currentSelection.isUnit) {
var selCell = getCellForUnit(currentSelection);
deltaToSelection = Math.sqrt(Math.pow(Math.abs(selCell.cellX - cellX), 2) + Math.pow(Math.abs(selCell.cellY - cellY), 2));
}
if (cellX >= 0 && cellX < gameMap.cells.length && cellY >= 0 && cellY < gameMap.cells[cellX].length) {
var cell = gameMap.cells[cellX][cellY];
if (!cell.building && !cell.unit && deltaToSelection > 1) {
if (dragDelta < tileSize * 0.25) {
currentSelection = null;
selectionMarker.setOnElement();
selectionMarker.hide();
console.log("Nothing selected at " + cellX + ',' + cellY, " deltaToSelection=" + deltaToSelection, " dragDelta=", dragDelta);
updateActionBoard();
}
} else if (cell.building) {
console.log("Selected building at " + cellX + ',' + cellY);
currentSelection = cell.building;
selectionMarker.setOnElement(currentSelection);
updateActionBoard();
} else if (cell.unit) {
console.log("Selected cell with unit at " + cellX + ',' + cellY);
console.log("unit at " + cell.unit.x + ',' + cell.unit.y);
}
}
dragStart = null;
dragDelta = 0;
}
}
game.on('down', handleDragStart);
game.on('move', handleDragMove);
game.on('up', handleDragEnd);
//#endregion Event listeners
// Global function to handle building placement
var handleBuildingPlacement = function handleBuildingPlacement(building, cellX, cellY) {
console.log("handle " + building.name + " Placement at " + cellX + ',' + cellY);
if (!gameMap.cells[cellX] || !gameMap.cells[cellX][cellY]) {
console.log('Invalid cell for building placement.');
return;
}
var cell = gameMap.cells[cellX][cellY];
if (cell.building) {
console.log('Cell is already occupied.');
return;
}
// Place the building on the map
cell.building = building;
building.cellX = cellX;
building.cellY = cellY;
building.x = cellX * tileSize;
building.y = cellY * tileSize;
building.asset.tint = 0xFFFFFF;
gameMap.addChild(building);
// Call addBuilding for the player who owns the building
if (building.playerId === player1.playerId) {
player1.addBuilding(building);
} else if (building.playerId === player2.playerId) {
player2.addBuilding(building);
}
applyCellOccupation(cellX, cellY);
console.log(building.name + ' placed at ' + cellX + ',' + cellY);
if (player1.playerId === building.playerId) {
player1.updateEnergy();
updateBaseInfo();
}
if (building instanceof SpiceRefinery) {
spawnUnit('unitHarvester', cellX, cellY, building.playerId);
}
currentUserActionState = UserActionState.NAVIGATING;
currentBuildingForPlacement = null;
if (cancelActionBtn) {
cancelActionBtn.visible = false;
}
updateActionBoard();
};
// Screen <-> World Coordinates
var screenCoordToWorldCell = function screenCoordToWorldCell(x, y) {
var wCellX = Math.floor(x / tileSize) + viewPort.wCellX;
var wCellY = Math.floor(y / tileSize) + viewPort.wCellY;
return {
wCellX: wCellX,
wCellY: wCellY
};
};
var screenCoordToWorldCoord = function screenCoordToWorldCoord(x, y) {
var wX = x + viewPort.wX;
var wY = y + viewPort.wY;
return {
wX: wX,
wY: wY
};
};
var screenCellToWorldCell = function screenCellToWorldCell(cellX, cellY) {
var wCellX = cellX + viewPort.wCellX;
var wCellY = cellY + viewPort.wCellY;
return {
wCellX: wCellX,
wCellY: wCellY
};
};
var screenCellToWorldCoord = function screenCellToWorldCoord(cellX, cellY) {
var wX = cellX * tileSize + viewPort.wX;
var wY = cellY * tileSize + viewPort.wY;
return {
wX: wX,
wY: wY
};
};
// World <-> Screen Coordinates
var worldCoordToScreenCell = function worldCoordToScreenCell(wX, wY) {
var cellX = Math.floor(wX / tileSize) - viewPort.wCellX;
var cellY = Math.floor(wY / tileSize) - viewPort.wCellY;
return {
cellX: cellX,
cellY: cellY
};
};
var worldCoordToScreenCoord = function worldCoordToScreenCoord(wX, wY) {
var x = wX - viewPort.wX;
var y = wY - viewPort.wY;
return {
x: x,
y: y
};
};
var worldCellToScreenCell = function worldCellToScreenCell(wCellX, wCellY) {
var cellX = wCellX - viewPort.wCellX;
var cellY = wCellY - viewPort.wCellY;
return {
cellX: cellX,
cellY: cellY
};
};
var worldCellToScreenCoord = function worldCellToScreenCoord(wCellX, wCellY) {
var x = wCellX * tileSize - viewPort.wX;
var y = wCellY * tileSize - viewPort.wY;
return {
x: x,
y: y
};
};
var getScreenToWorldCell = function getScreenToWorldCell(x, y) {
var wCellX = Math.floor(x / tileSize) + viewPort.wX;
var wCellY = Math.floor(y / tileSize) + viewPort.wY;
return {
wCellX: wCellX,
wCellY: wCellY
};
};
var getScreenToWorldCoord = function getScreenToWorldCoord(x, y) {
var wX = x + viewPort.wX * tileSize;
var wY = y + viewPort.wY * tileSize;
return {
wX: wX,
wY: wY
};
};
var convertCoordsToCell = function convertCoordsToCell(x, y) {
return {
cellX: Math.floor(x / 100) + gameMap.currentViewCenter.x - Math.ceil(game.width / 2 / 100) - 3,
cellY: Math.floor(y / 100) + gameMap.currentViewCenter.y - Math.ceil(game.height / 2 / 100)
};
};
var getCellForUnit = function getCellForUnit(unit) {
var centerDeltaX = gameMap.currentViewCenter.x - Math.ceil(game.width / 2 / 100);
var centerDeltaY = gameMap.currentViewCenter.y - Math.ceil(game.height / 2 / 100);
var cX = Math.floor(unit.x / 100) + centerDeltaX + unit.cellW / 2 + Math.floor(unit.cellW / 2) - 5;
var cY = Math.floor(unit.y / 100) + centerDeltaY + Math.floor(unit.cellH / 2) + unit.cellH / 2 - 7;
return {
cellX: cX,
cellY: cY
};
};
// Global function to find the closest spice spot to a given position
var findClosestSpiceSpot = function findClosestSpiceSpot(x, y) {
var closestSpot = null;
var minDistance = Number.MAX_VALUE;
spiceSpots.forEach(function (spot) {
var dx = x - spot.x;
var dy = y - spot.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
closestSpot = spot;
minDistance = distance;
}
});
return closestSpot;
};
// Global function to handle action board updates
var updateActionBoard = function updateActionBoard() {
console.log('updateActionBoard...' + (currentSelection ? currentSelection.name : 'No selection'));
currentSelectionText.setText(currentSelection ? currentSelection.name : '');
clearIconBorders();
displayBuildableItems(); // Call displayBuildableItems to show items that can be built
displayActionItems(); // Call displayActionItems to show action items
};
function clearIconBorders() {
var iconBorders = game.children.filter(function (child) {
return child instanceof IconBorder;
});
iconBorders.forEach(function (border) {
border.destroy();
});
}
function displayBuildableItems() {
console.log('updateActionBoard...', currentSelection);
var buildableItemIcons = game.children.filter(function (child) {
return child instanceof BuildableItemIcon;
});
buildableItemIcons.forEach(function (icon) {
icon.destroy();
});
if (currentSelection && currentSelection.playerId === player1.playerId && currentSelection.buildable && currentSelection.buildable.length) {
var iconX = commandPanel.x + 200; // Starting X position for the first icon
var iconY = commandPanel.y + 220; // Y position for all icons
currentSelection.buildable.forEach(function (itemType) {
var buildableInfo = buildableRepository.getBuildableInfo(itemType);
if (buildableInfo.requires) {
var requiredBuildingExists = player1.buildings.some(function (building) {
return building.type === buildableInfo.requires;
});
if (!requiredBuildingExists) {
return;
}
}
var border = new IconBorder(iconX, iconY);
game.addChild(border);
var icon = new BuildableItemIcon(itemType, iconX, iconY, currentSelection);
game.addChild(icon);
iconX += 300; // Increment X position for the next icon
});
}
}
// Global function to display action items
var displayActionItems = function displayActionItems() {
console.log('displayActionItems...', currentSelection);
var actionItemIcons = game.children.filter(function (child) {
return child instanceof ActionItemIcon;
});
actionItemIcons.forEach(function (icon) {
console.log('removing...', icon.action.name);
icon.destroy();
});
if (currentSelection && currentSelection.playerId === player1.playerId && currentSelection.isUnit && currentSelection.actions && currentSelection.actions.length) {
var iconX = commandPanel.x + 200; // Starting X position for the first icon
var iconY = commandPanel.y + 220; // Y position for all icons
currentSelection.actions.forEach(function (actionType) {
console.log('Add action button for ', actionType);
var actionInfo = actionRepository.getActionInfo(actionType);
if (actionInfo) {
console.log('ok Action found => display', actionInfo);
var border = new IconBorder(iconX, iconY);
game.addChild(border);
var icon = new ActionItemIcon(actionInfo, iconX, iconY);
game.addChild(icon);
iconX += 300; // Increment X position for the next icon
} else {
console.log('Action not found', actionType);
}
});
}
};
// Global function to update mouse coordinates text
var updateMouseCoords = function updateMouseCoords(x, y) {
var wCell = screenCoordToWorldCell(x, y);
if (game.mouseCoordsText) {
game.mouseCoordsText.setText("S:" + Math.floor(x) + ',' + Math.floor(y) + '\r\n' + Math.floor(x / tileSize) + ',' + Math.floor(y / tileSize) + '\r\nW:' + Math.floor(wCell.wCellX) + ',' + Math.floor(wCell.wCellY));
}
gameMap.viewCenterText.setText(viewPort.wCellX + ',' + viewPort.wCellY);
};
// Global function to update base info text
var updateBaseInfo = function updateBaseInfo() {
player1SpiceText.setText('| ' + player1.spice.toString());
// Animate the change of the energy value
if (previousEnergyLevel !== player1.energy) {
var energyDifference = player1.energy - previousEnergyLevel;
var duration = 2000; // Duration of the animation in milliseconds
var steps = duration / (1000 / 60); // Number of steps based on 60FPS
var stepValue = energyDifference / steps;
var currentStep = 0;
var animateEnergyChange = function animateEnergyChange() {
var energyColor = getEnergyColor(previousEnergyLevel);
player1EnergyText.setStyle({
fill: energyColor
});
if (currentStep < steps) {
previousEnergyLevel += stepValue;
player1EnergyText.setText(previousEnergyLevel.toFixed(0).toString() + energyOffsetSufix);
currentStep++;
LK.setTimeout(animateEnergyChange, 1000 / 60);
} else {
// Ensure the final value is set correctly
player1EnergyText.setText(player1.energy.toString() + energyOffsetSufix);
previousEnergyLevel = player1.energy; // Update the previous energy level
}
};
animateEnergyChange();
} else {
player1EnergyText.setText(player1.energy.toString() + energyOffsetSufix);
}
};
var startFpsCounter = function startFpsCounter() {
var fpsInterval = LK.setInterval(function () {
// Set the FPS value to the tick counter
fps = tickCounter;
// Reset the tick counter
tickCounter = 0;
// Update the FPS text on the screen
fpsText.setText('FPS: ' + fps);
}, 1000); // 1000 milliseconds = 1 second
};
function initGame() {
// Create players
player1 = new Player(1, 0x5e86ff);
player2 = new Player(2, 0xffAAAA);
// Prepare the map
prepareMap();
// Instantiate CommandPanel
commandPanel = game.addChild(new CommandPanel());
// Create a Text2 object for current selection
currentSelectionText = new Text2('', {
size: 50,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
currentSelectionText.anchor.set(0.5, 6.8);
LK.gui.bottom.addChild(currentSelectionText);
// Create a Text2 object to display mouse coordinates
if (!game.mouseCoordsText) {
game.mouseCoordsText = new Text2('0,0\r\n0,0', {
size: 50,
fill: '#ffffff',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
game.mouseCoordsText.anchor.set(0, 0);
LK.gui.topLeft.addChild(game.mouseCoordsText);
}
// Create a Text2 object to display player1's spice level
player1SpiceText = new Text2('| ' + player1.spice.toString(), {
size: 50,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
player1SpiceText.anchor.set(1.0, 0.25);
LK.gui.topRight.addChild(player1SpiceText);
// Create a Text2 object to display player1's energy level
player1EnergyText = new Text2(player1.energy.toString() + energyOffsetSufix, {
size: 50,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
player1EnergyText.anchor.set(1.0, 0.25);
LK.gui.topRight.addChild(player1EnergyText);
// Create a Text2 object to display the FPS in the bottom right corner
fpsText = new Text2('', {
size: 50,
fill: '#000000',
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
fpsText.anchor.set(1.5, 1.0);
LK.gui.bottomRight.addChild(fpsText);
gameIsRunning = true;
startFpsCounter();
}
// Game tick
LK.on('tick', function () {
// Render the map and update unit movement each tick
if (gameIsRunning) {
gameMap.render();
var allUnits = player1.units.concat(player2.units);
allUnits.forEach(function (unit) {
if (unit.isDestroyed) {
//console.log("unit is already destroyed");
//unit.asset.visible = false;
return;
}
unit.updateMovement(1 / 60); // Assuming tick operates at 60FPS
if (unit.update) {
unit.update();
}
game.addChild(unit);
if (unit.assetEffect) {
game.addChild(unit.assetEffect);
}
});
}
tickCounter++;
});
// Start the game with Player 1 construction yard preselected
LK.setTimeout(function () {
initGame();
currentSelection = gameMap.cells[rockIlot1Center.x][rockIlot1Center.y].building;
selectionMarker.setOnElement(currentSelection);
updateActionBoard();
}, 100);
a tileable sand terrain tile.
A square tileable rock terrain tile WITHOUT BBORDER. Single Game Texture. In-Game asset. 2d. No shadows. No Border
Zenith view of Dune's Wind Trap power facility square fence. Ressembles a bit to Sydney's Opera. Zenith view Directly from above.
grey cancel icon. UI
thin white circle empty.
0x5e86ff
Zenith view of a white rectangular Harvester shape of a garbage truck with a triangular head. Harvesting on sand, with flowing spice in the back. inside a square fence. Zenith view. Directly overhead. Plumb view.
Minimal Ui icon of an right sign aside with an icon of a target. sand background
Minimal icon of a home with direction icon pointing to the home. sand background
3 white flat isometric concentric circles like a target.
Remove background
Gray background
Minimal Ui icon of target sign on a fire icon.. sand background
top view of an explosion effect.
Simple heavy army tank factory buiding a tank with a crane. Square fence... Zenith view Directly overhead. Plumb view.
an empty black popup UI. UI