User prompt
in updateBaseInfo, also animate the energy color : red near 0%, orange around 50% and #037d50 near 100%
User prompt
in updateEnergy, clamp the energy between 0 and 100
Code edit (4 edits merged)
Please save this source code
User prompt
in updateBaseInfo, keep track of the previous energyLevel in a global variable and use it to animate the change of the energy value
User prompt
in enqueueBuildable, don't reduce spice by buildableInfo.cost directly, instead reduce it progressively during the building progress
Code edit (1 edits merged)
Please save this source code
User prompt
call addBuilding when a buiding is added in handleBuildingPlacement
User prompt
in updateEnergy, use buildings instead of gameMap
Code edit (1 edits merged)
Please save this source code
User prompt
in displayBuildableItems , only display BuildableItemIcons if currentSelection is the property of player 1
Code edit (5 edits merged)
Please save this source code
User prompt
do the same for player2
User prompt
call addBuilding() after the creation of the initial Construction Yard
User prompt
add a function 'addBuilding()' to Player class
User prompt
add an array property in Player to store all the player's buildings
Code edit (1 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
in updateBaseInfo, also update player1EnergyText
User prompt
in updateEnergy(), check all building of the player and sum their energy
User prompt
in handleBuildingPlacement when player 1 place a new building, call updateEnergy then updateBaseInfo
Code edit (1 edits merged)
Please save this source code
User prompt
add a player1EnergyText after player1SpiceText
User prompt
in enqueueBuildable call updateBaseInfo instead of player1SpiceText.setText(player1.spice.toString());
Code edit (4 edits merged)
Please save this source code
User prompt
at the end of handleBuildingPlacement clear currentBuildingForPlacement
/****
* Classes
****/
var BuildableItemIcon = Container.expand(function (type, x, y) {
var self = Container.call(this);
self.type = type;
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y
});
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(type, progressDisplay);
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.alpha = 0.75;
}
});
});
var buildableInfo = buildableRepository.getBuildableInfo(type);
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.length / 2 * 15;
self.label.y = self.asset.y + 120;
self.addChild(self.label);
self.setLabelToPlace = function () {
self.label.setText('Place');
self.label.x = self.asset.x - 'Place'.length / 2 * 15; // Recalculate x position for new text
};
return self;
});
var BuildingProgressDisplay = Container.expand(function (parentIcon) {
var self = Container.call(this);
self.parentIcon = parentIcon;
self.progressAsset = self.attachAsset('buildingProgress', {
anchorX: 0,
anchorY: .5,
alpha: 0.5,
width: 0,
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;
});
// Initialize game elements
// Map class to represent the grid-based map system
var Map = Container.expand(function () {
var self = Container.call(this);
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
};
}
}
};
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(self.currentViewCenter.x + ', ' + self.currentViewCenter.y, {
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 () {
// Move tiles instead of the view
var viewSize = Math.ceil(Math.max(game.width, game.height) / 2 / 100); // Calculate viewSize based on game dimensions and tile size
for (var x = self.currentViewCenter.x - viewSize - 1; x <= self.currentViewCenter.x + viewSize; x++) {
for (var y = self.currentViewCenter.y - viewSize - 1; y <= self.currentViewCenter.y + viewSize; 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 screenX = (x - self.currentViewCenter.x + viewSize) * asset.width;
var screenY = (y - self.currentViewCenter.y + viewSize) * asset.height;
asset.x = screenX;
asset.y = screenY;
if (!asset.parent) {
self.addChild(asset);
}
// Render buildings
var building = cell.building;
if (building) {
building.asset.x = screenX;
building.asset.y = screenY;
self.addChild(building.asset);
}
}
}
}
};
self.getNavigationMesh = function () {
// Create and return the navigation mesh for pathfinding
};
self.findPath = function (start, end) {
// Implement A* pathfinding algorithm
// Return the path from start to end
};
self.lastInputPosition = null;
self.handleInput = function (input) {
// Clamp the view center to the map boundaries
self.currentViewCenter.x = Math.max(14, Math.min(input.x, mapXSize - 7));
self.currentViewCenter.y = Math.max(14, Math.min(input.y, mapYSize - 9));
// Update the Text2 object with the new coordinates
self.viewCenterText.setText(self.currentViewCenter.x + ', ' + self.currentViewCenter.y);
if (currentSelection) {
selectionMarker.updatePosition(currentSelection.x, currentSelection.y);
}
};
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 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 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 SelectionMarker = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('selectionCross', {
anchorX: 0.5,
anchorY: 0.5
});
self.alpha = 0.6;
self.visible = false;
self.updatePosition = function (x, y) {
console.log('SelectionMarker updated to position:', x, y);
var viewSize = Math.ceil(Math.max(game.width, game.height) / 2 / 100);
self.x = x - (gameMap.currentViewCenter.x - 15) * 100;
self.y = y - (gameMap.currentViewCenter.y - 15) * 100;
self.visible = true;
};
self.hide = function () {
console.log('SelectionMarker hidden');
self.visible = false;
};
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 * 100;
self.y = y * 100;
self.type = type;
self.playerId = playerId;
self.name = name;
self.buildable = buildable || []; // New property to store what the building can build
self.asset = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
tint: getPlayerTint(playerId)
});
self.asset.x = self.x;
self.asset.y = self.y;
self.buildingQueue = [];
// Initialize building-specific properties here
// ...
return self;
});
var ConstructionYard = Building.expand(function (x, y, playerId) {
var self = Building.call(this, x, y, 'constructionYard', 'Construction Yard', playerId, 2, 2, ['windTrap']);
// Additional properties and methods for ConstructionYard can be added here
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;
});
// 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,
'constructionTime': 10,
'className': 'ConstructionYard',
'buildable': ['windTrap']
},
'windTrap': {
'name': 'Wind Trap',
'cost': 300,
'constructionTime': 2,
'className': 'WindTrap',
'buildable': []
}
// Add more buildables as needed
};
self.getBuildableInfo = function (type) {
return self.buildables[type];
};
return self;
});
var Player = Container.expand(function (playerId, tint) {
var self = Container.call(this);
self.playerId = playerId;
self.resources = 0;
self.tint = tint;
self.spice = 1000; // Initialize spice property with 1000
// Initialize player-specific properties here
// ...
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;
});
/****
* Initialize Game
****/
// Event listeners
// Instantiate and initialize the Map
var game = new LK.Game({
backgroundColor: 0x000000 // Init game with black background
});
/****
* Game Code
****/
function checkBuildingPlacement(newBuilding, cellX, cellY) {
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(type, progressDisplay) {
console.log("enqueueBuildable...", type);
if (!currentSelection || !currentSelection.buildable.includes(type)) {
console.log("Cannot build this item here.");
return;
}
var buildableInfo = buildableRepository.getBuildableInfo(type);
if (player1.spice < buildableInfo.cost) {
console.log("Not enough resources to build.");
return;
}
player1.spice -= buildableInfo.cost;
player1SpiceText.setText(player1.spice.toString());
var constructionTime = buildableInfo.constructionTime;
var building;
switch (buildableInfo.className) {
case 'ConstructionYard':
building = new ConstructionYard(currentSelection.cellX, currentSelection.cellY, player1.playerId);
break;
case 'WindTrap':
building = new WindTrap(0, 0, player1.playerId);
break;
// Add cases for other buildings as needed
}
building.constructionProgress = 0;
building.constructionTimer = LK.setInterval(function () {
building.constructionProgress += 0.1;
building.progressDisplay.setProgress(building.constructionProgress / constructionTime);
if (building.constructionProgress >= constructionTime) {
LK.clearInterval(building.constructionTimer);
console.log(type + " construction completed.");
building.constructionTimer = null;
building.progressDisplay.hide();
if (progressDisplay.parentIcon && typeof progressDisplay.parentIcon.setLabelToPlace === 'function') {
progressDisplay.parentIcon.setLabelToPlace(); // Change label text to 'Place'
}
console.log("Building Ready for placement ", building);
currentBuildingForPlacement = building;
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.asset.interactive = true;
child.alpha = 1.0;
}
});
}
}, 100);
building.progressDisplay = progressDisplay;
currentSelection.buildingQueue.push(building);
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.asset.interactive = false;
}
});
}
function displayBuildableItems() {
console.log('updateActionBoard...', currentSelection);
game.children.forEach(function (child) {
if (child instanceof BuildableItemIcon) {
child.destroy();
}
});
/*
if (!currentSelection || !currentSelection.buildable || !currentSelection.buildable.length) {
console.log("Not buildables...");
return;
}
var buildableItems = currentSelection.buildable;*/
if (currentSelection && currentSelection.buildable && currentSelection.buildable.length) {
var iconX = commandPanel.x + 200; // Starting X position for the first icon
var iconY = commandPanel.y + 200; // Y position for all icons
currentSelection.buildable.forEach(function (itemType) {
var icon = new BuildableItemIcon(itemType, iconX, iconY);
game.addChild(icon);
iconX += 100; // Increment X position for the next icon
});
}
}
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) {
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 = 50;
var mapYSize = 30;
var gameMap = new Map();
// Define user action states
var UserActionState = {
NAVIGATING: 'navigating',
BUILDING: 'building',
PLACING_BUILDINGS: 'placing_buildings',
GIVING_ORDERS: 'giving_orders'
};
// Current state of user action
var currentUserActionState = UserActionState.NAVIGATING;
var globalTerrain = null;
var player1 = null;
var player2 = null;
var currentSelection = null;
var currentBuildingForPlacement = null;
var selectionMarker = null;
var buildableRepository = new BuildableRepository();
// Gui
var commandPanel = null;
var player1SpiceText = null;
var currentSelectionText = 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: Math.floor(mapXSize * 0.15),
y: Math.floor(mapYSize * 0.5)
};
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);
gameMap.cells[rockIlot1Center.x][rockIlot1Center.y].building = new ConstructionYard(rockIlot1Center.x, rockIlot1Center.y, 1);
applyCellOccupation(rockIlot1Center.x, rockIlot1Center.y);
// Define a fixed rock ilot near the center right of the map
var rockIlot2Center = {
x: Math.floor(mapXSize * 0.85),
y: Math.floor(mapYSize * 0.5)
};
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);
gameMap.cells[rockIlot2Center.x][rockIlot2Center.y].building = new ConstructionYard(rockIlot2Center.x, rockIlot2Center.y, 2);
applyCellOccupation(rockIlot2Center.x, rockIlot2Center.y);
gameMap.initTerrain(mapXSize, mapYSize); // Initialize with a 20x20 grid
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);
}
//#region Event listeners
var dragStart = null;
function handleDragStart(obj) {
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) {
console.log("User is moving building..." + input.x + ',' + input.y);
var cellCoord = convertCoordsToCell(input.x, input.y);
var cellX = cellCoord.cellX;
var cellY = cellCoord.cellY;
var isValidPlacement = checkBuildingPlacement(currentBuildingForPlacement, cellX - 1, cellY - 1);
currentBuildingForPlacement.asset.tint = isValidPlacement ? 0xFFFFFF : 0xFF0000;
currentBuildingForPlacement.x = Math.floor(input.x / 100) * 100;
currentBuildingForPlacement.y = Math.floor(input.y / 100) * 100;
} else if (dragStart) {
var deltaX = currentPos.x - dragStart.x;
var deltaY = currentPos.y - dragStart.y;
dragStart = currentPos; // Update dragStart to the current position
var inputPosition = {
x: gameMap.currentViewCenter.x - Math.round(deltaX / 100),
y: gameMap.currentViewCenter.y - Math.round(deltaY / 100)
};
gameMap.handleInput(inputPosition);
}
}
function handleDragEnd(obj) {
var currentPos = obj.event.getLocalPosition(game);
var input = {
x: currentPos.x,
y: currentPos.y
};
if (input.y > game.height * 0.80) {
console.log("Clicked on board at " + input.x + ',' + input.y);
dragStart = null;
return;
}
if (currentUserActionState === UserActionState.PLACING_BUILDINGS) {
var cellCoord = convertCoordsToCell(input.x, input.y);
var cellX = cellCoord.cellX - 1;
var cellY = cellCoord.cellY - 1;
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;
} else {
// Existing code for handling drag end without a building placement
var cellX = Math.floor(input.x / 100) + gameMap.currentViewCenter.x - Math.ceil(game.width / 2 / 100) - 3;
var cellY = Math.floor(input.y / 100) + gameMap.currentViewCenter.y - Math.ceil(game.height / 2 / 100);
if (cellX >= 0 && cellX < gameMap.cells.length && cellY >= 0 && cellY < gameMap.cells[cellX].length) {
var cell = gameMap.cells[cellX][cellY];
if (!cell.building) {
currentSelection = null;
selectionMarker.hide();
console.log("Nothing selected at " + cellX + ',' + cellY);
updateActionBoard();
} else {
console.log("Selected building at " + cellX + ',' + cellY);
currentSelection = cell.building;
selectionMarker.updatePosition(currentSelection.x, currentSelection.y);
updateActionBoard();
}
}
dragStart = null;
}
}
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) {
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.x = cellX * 100;
building.y = cellY * 100;
gameMap.addChild(building);
applyCellOccupation(cellX, cellY);
console.log(building.name + ' placed at ' + cellX + ',' + cellY);
currentSelection = building;
selectionMarker.updatePosition(building.x, building.y);
currentUserActionState = UserActionState.NAVIGATING;
currentBuildingForPlacement = null;
updateActionBoard();
};
// Global function to handle action board updates
var updateActionBoard = function updateActionBoard() {
console.log('updateActionBoard...' + (currentSelection ? currentSelection.name : 'No selection'));
currentSelectionText.setText(currentSelection ? currentSelection.name : '');
displayBuildableItems(); // Call displayBuildableItems to show items that can be built
};
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)
};
};
// Global function to update mouse coordinates text
var updateMouseCoords = function updateMouseCoords(x, y) {
var cellCoord = convertCoordsToCell(x, y);
if (game.mouseCoordsText) {
game.mouseCoordsText.setText(Math.floor(x) + ',' + Math.floor(y) + '\r\n' + cellCoord.cellX + ',' + cellCoord.cellY);
}
};
function initGame() {
// Create players
player1 = new Player(1, 0xAAAAff);
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);
// Add the spice level text to the GUI overlay
LK.gui.topRight.addChild(player1SpiceText);
gameIsRunning = true;
}
// Game tick
LK.on('tick', function () {
// Render the map each tick
if (gameIsRunning) {
gameMap.render();
}
});
// Start the game with Player 1 construction yard preselected
LK.setTimeout(function () {
initGame();
currentSelection = gameMap.cells[rockIlot1Center.x][rockIlot1Center.y].building;
selectionMarker.updatePosition(currentSelection.x, currentSelection.y);
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