Code edit (4 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'viewPort.wCellX = Math.max(0, Math.min(mapXSize - viewPort.cellW, rockIlot1Center.x - Math.floor(viewPort.cellW / 2)));' Line Number: 2035
User prompt
ok, but also set the viewport centered on the rockIlot1Center at start
User prompt
currently rockIlot1Center and rockIlot2Center are fixed on top left and bottom down. make them randomly 'rotate' so that players start at one of the corners randomly
Code edit (11 edits merged)
Please save this source code
User prompt
in checkGameEnd(), show popup asset before game over
User prompt
show popup asset before game over
Code edit (1 edits merged)
Please save this source code
Code edit (4 edits merged)
Please save this source code
User prompt
under welcomeText, add a help text "how to play..."
Code edit (2 edits merged)
Please save this source code
User prompt
on the popup write the text "Welcome on Dune."
Code edit (1 edits merged)
Please save this source code
User prompt
before game starts, show popup Asset
User prompt
before the game starts display a popup
Code edit (6 edits merged)
Please save this source code
User prompt
how would you handle energy so that low energy level slow down constructions
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'type')' in or related to this line: 'player1.spice += buildableRepository.getBuildableInfo(currentBuildingForPlacement.type).cost;' Line Number: 462
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: unit is not defined' in or related to this line: 'switch (unit.type) {' Line Number: 2176
Code edit (1 edits merged)
Please save this source code
Code edit (14 edits merged)
Please save this source code
/**** 
* 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.progressDisplay = null;
	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) {
			console.log("BuildableItemIcon : Not interactive. ignore");
			return;
		} else {
			console.log("BuildableItemIcon : Is interactive. continue");
		}
		if (currentBuildingForPlacement) {
			currentUserActionState = UserActionState.PLACING_BUILDINGS;
			console.log("BuildableItemIcon : User is placing ", currentBuildingForPlacement);
			game.addChild(currentBuildingForPlacement);
			return;
		} else {
			console.log("BuildableItemIcon : No current building for placement. ignore");
		}
		if (!self.progressDisplay) {
			console.log("BuildableItemIcon : No progress display => create one");
			self.progressDisplay = new BuildingProgressDisplay(self);
		} else {
			console.log("BuildableItemIcon : Progress display already exists");
		}
		self.progressDisplay.show();
		game.addChild(self.progressDisplay);
		enqueueBuildable(self.type, self.sourceFactory, player1, 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
	};
	// Restore progress display from global queue
	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': 100,
			'constructionTime': 10,
			'className': 'ConstructionYard',
			'buildable': ['windTrap', 'spiceRefinery', 'lightFactory', 'heavyFactory']
		},
		'windTrap': {
			'name': 'Wind Trap',
			'cost': 300,
			'energy': 100,
			'health': 50,
			'constructionTime': 5,
			'className': 'WindTrap',
			'buildable': []
		},
		'spiceRefinery': {
			'name': 'Spice Refinery',
			'cost': 600,
			'energy': -50,
			'health': 75,
			'constructionTime': 15,
			'className': 'SpiceRefinery',
			'buildable': []
		},
		'lightFactory': {
			'name': 'Light Factory',
			'cost': 400,
			'energy': -30,
			'health': 60,
			'constructionTime': 15,
			'className': 'LightFactory',
			'buildable': ['unitQuad'],
			'requires': 'spiceRefinery'
		},
		'heavyFactory': {
			'name': 'Heavy Factory',
			'cost': 600,
			'energy': -50,
			'health': 80,
			'constructionTime': 20,
			'className': 'HeavyFactory',
			'buildable': ['unitLightTank', 'unitHeavyTank', 'unitHarvester'],
			'requires': 'lightFactory'
		},
		'unitQuad': {
			'name': 'Quad',
			'cost': 200,
			'energy': 0,
			'constructionTime': 5,
			'className': 'UnitQuad',
			'buildable': []
		},
		'unitLightTank': {
			'name': 'Light Tank',
			'cost': 400,
			'energy': 0,
			'constructionTime': 8,
			'className': 'UnitLightTank',
			'buildable': []
		},
		'unitHeavyTank': {
			'name': 'Heavy Tank',
			'cost': 600,
			'energy': 0,
			'constructionTime': 15,
			'className': 'UnitHeavyTank',
			'buildable': []
		},
		'unitHarvester': {
			'name': 'Harvester',
			'cost': 500,
			'energy': 0,
			'constructionTime': 12,
			'className': 'UnitHarvester',
			'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.id = getNextId();
	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.lastAttackTime = 0;
	self.lastHitTime = 0;
	self.lastAttaker = null;
	self.defenders = [];
	self.damage = function (attacker) {
		console.log("Building " + self.name + " hit! " + attacker.attackDamage);
		self.lastAttaker = attacker;
		self.health -= attacker.attackDamage;
		self.lastHitTime = Date.now();
		self.asset.tint = 0xff0000;
		LK.setTimeout(function () {
			self.asset.tint = 0xFFFFFF;
		}, 500);
		if (self.health <= 0) {
			console.log("Building " + self.name + " destroyed");
			self.clean();
			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;
							}
						}
					}
					console.log("update selection destroyed ", currentSelection, self, currentSelection == self);
					if (currentSelection && currentSelection == self) {
						currentSelection = null;
						selectionMarker.setOnElement();
					}
					// Remove building from player's list of buildings
					if (self.playerId === player1.playerId) {
						player1.buildings = player1.buildings.filter(function (building) {
							return building !== self;
						});
					} else if (self.playerId === player2.playerId) {
						player2.buildings = player2.buildings.filter(function (building) {
							return building !== self;
						});
					}
					game.removeChild(self);
					self.destroy();
					updateActionBoard();
					checkGameEnd();
				}
			};
			growExplosion(1);
		}
		LK.setTimeout(function () {
			if (self.health > 0 && Date.now() - self.lastHitTime > aiUnderAttackDelayMs) {
				// Not under attack anymore
				self.lastAttaker = null;
				self.defenders = [];
			}
		}, aiUnderAttackDelayMs * 1.1);
	};
	self.clean = function () {
		self.buildingQueue = [];
		self.lastAttackTime = 0;
		self.lastHitTime = 0;
		self.lastAttaker = null;
		self.defenders = [];
	};
	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;
});
// HeavyFactory class to represent the Heavy Factory building
var HeavyFactory = Building.expand(function (x, y, playerId) {
	var self = Building.call(this, x, y, 'heavyFactory', 'Heavy Factory', playerId, 3, 2, buildableRepository.getBuildableInfo('heavyFactory').buildable);
	// Additional properties and methods for HeavyFactory 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.progressAsset.width = 0;
		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 () {
		// Cancel current building
		if (!currentBuildingForPlacement) {
			// Browse the currentSelection.buildingQueue
			if (currentSelection && currentSelection.buildingQueue.length > 0) {
				// Check for non null buildable and stop it's constructionTimer
				for (var i = 0; i < currentSelection.buildingQueue.length; i++) {
					if (currentSelection.buildingQueue[i] && currentSelection.buildingQueue[i].constructionTimer) {
						console.log("Stop constructionTimer on ", currentSelection.buildingQueue[i]);
						var initialCost = buildableRepository.getBuildableInfo(currentSelection.buildingQueue[i].type).cost;
						var initialTime = buildableRepository.getBuildableInfo(currentSelection.buildingQueue[i].type).constructionTime;
						console.log("constructionProgress = ", currentSelection.buildingQueue[i].constructionProgress);
						player1.spice += initialCost * currentSelection.buildingQueue[i].constructionProgress * initialTime / 100;
						updateBaseInfo();
						LK.clearInterval(currentSelection.buildingQueue[i].constructionTimer);
						currentSelection.buildingQueue[i].constructionTimer = null;
						currentSelection.buildingQueue[i].progressDisplay.hide();
						game.children.forEach(function (child) {
							if (child instanceof BuildableItemIcon) {
								child.asset.interactive = true;
								child.alpha = 1.0;
							}
						});
					}
				}
			}
			self.visible = false; // Hide the CancelActionButton
		}
		// Cancel current placement
		if (currentBuildingForPlacement) {
			player1.spice += buildableRepository.getBuildableInfo(currentBuildingForPlacement.type).cost;
			updateBaseInfo();
			currentBuildingForPlacement.destroy();
			currentBuildingForPlacement = null;
			currentUserActionState = UserActionState.NAVIGATING;
			self.visible = false; // Hide the CancelActionButton
		}
		// Cancel Order
		if (currentUserActionState === UserActionState.SET_ORDER_TARGET) {
			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('', {
		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) {
						if (building.playerId === player2.playerId) {
							building.asset.visible = !cell.fog;
						}
						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);
						}
					}
					if (cell.fog) {
						var fogAsset = cell.fogAsset;
						fogAsset.x = screenX - 1;
						fogAsset.y = screenY - 1;
						if (!fogAsset.parent) {
							self.addChild(fogAsset);
						}
					}
				}
			}
		}
	};
	self.getNavigationMesh = function () {
		// Create and return the navigation mesh for pathfinding
	};
	self.findPath = function (start, end, range, ignoreSpecificBuilding) {
		range = range || 0;
		if (range == 13) {
			console.log("findPath ", start, end, range);
		}
		// 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];
			var dx = currentNode.x - endNode.x;
			var dy = currentNode.y - endNode.y;
			if (Math.sqrt(dx * dx + dy * dy) <= range) {
				if (range == 13) {
					console.log("findPath Ok Within range :", currentNode.x, currentNode.y + " / dist=" + Math.sqrt(dx * dx + dy * dy));
				}
				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();
				if (range == 13) {
					console.log("Return path 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;
				}
			}
		}
		// If the openSet is empty but we haven't reached the destination, return the path to the closest node
		if (openSet.length === 0) {
			var closestNode = closedSet.reduce(function (prev, curr) {
				var prevDistance = heuristic(prev, endNode);
				var currDistance = heuristic(curr, endNode);
				return prevDistance < currDistance ? prev : curr;
			});
			var dx = closestNode.x - endNode.x;
			var dy = closestNode.y - endNode.y;
			if (Math.sqrt(dx * dx + dy * dy) <= range) {
				var curr = closestNode;
				while (curr.parent) {
					var prev = curr.parent;
					if (prev) {
						var moveX = curr.x - prev.x;
						var moveY = curr.y - prev.y;
						path.push({
							cellX: moveX,
							cellY: moveY
						});
					}
					curr = curr.parent;
				}
				path.reverse();
			}
		}
		//console.log("Return best effort path", 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 = tint;
	self.spice = 9999; //2000; // 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.isCurrentlyBuilding = false;
	self.isCurrentlyBuildingUnit = false;
	// ...
	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 || element.health <= 0) {
			self.visible = false;
			updateActionBoard();
			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;
	self.id = getNextId();
	var unitInfo = unitRepository.getUnitInfo(type);
	self.name = unitInfo.name;
	self.health = unitInfo.health; // Range for attacking
	self.isDestroyed = false;
	self.isDestroying = 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.lastHitTime = 0;
	self.lastAttaker = null;
	self.isArmy = false;
	self.defenders = [];
	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;
	var screenCoord = worldCellToScreenCoord(cellX, cellY);
	self.x = screenCoord.x;
	self.y = screenCoord.y;
	self.cellW = 1;
	self.cellH = 1;
	console.log("clear onArrivedCallback 0");
	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 || currentUserActionState === UserActionState.GIVING_ORDERS) {
			console.log("Unit selected");
			currentSelection = self;
			// Update map to fix unselectable units
			gameMap.cells[self.cellX][self.cellY].unit = self;
			selectionMarker.setOnElement(currentSelection);
			currentUserActionState = UserActionState.GIVING_ORDERS;
			updateActionBoard();
		} else {
			console.log("Not Navigating don't select unit...", currentUserActionState);
		}
	});
	self.moveAlongPath = function (path, callback) {
		if (self.isArmy) {
			console.log("Army " + self.type + " #" + self.id + " moveAlongPath", path, callback);
		}
		self.path = path;
		self.pathIndex = 0;
		self.isMoving = true;
		self.onArrivedCallback = callback;
	};
	self.updateMovement = function (delta) {
		if (!self.isMoving || self.isDestroying) {
			//console.log("Not isMoving", self.isMoving, self.isDestroying);
			if (self.y > mapHeight) {
				self.visible = false;
			} else {
				self.visible = true;
			}
			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();
				//console.log("clear onArrivedCallback 1");
				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) {
			//console.log("updateMovement reached 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;
			if (self.playerId == player1.playerId) {
				updateFogOfWar();
			}
			self.pathIndex++;
			if (self.pathIndex >= self.path.length) {
				self.isMoving = false;
				if (self.onArrivedCallback) {
					//console.log("updateMovement call onArrivedCallback 2", self.onArrivedCallback);
					self.onArrivedCallback();
					self.onArrivedCallback = null;
				} else {
					//console.log("No onArrivedCallback", self, self.onArrivedCallback);
				}
			} else {
				//console.log("Not self.pathIndex >= self.path.length", self.pathIndex >= self.path.length, self.pathIndex, self.path.length);
			}
		} 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 allTargets = this.playerId === player1.playerId ? player2.buildings.concat(player2.units) : player1.buildings.concat(player1.units);
		allTargets.forEach(function (target) {
			if (target.health > 0) {
				var distance = calculateDistance(self, target);
				if (distance <= self.sightRange) {
					enemies.push(target);
				}
			}
		});
		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.health > 0 && !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) {
				console.log(self.playerId + " - " + self.type + " : Target at range => fire");
				// Rotate towards the target
				var angleToTarget = calculateAngle(self, self.attackTarget);
				self.asset.rotation = angleToTarget;
				// Fire at the target
				self.fire();
			} else {
				console.log(self.playerId + " - " + self.type + " : Target too far => move to target");
				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.attackRange);
				console.log("Path  to target:", path);
				self.moveAlongPath(path, function () {
					console.log("Reached to target !");
					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 && self.health > 0) {
			// Reduce target's health
			self.attackTarget.damage(self);
			if (!self.attackTarget || self.attackTarget.health <= 0) {
				self.attackMode = 0;
				self.attackTarget = null;
			}
		}
	};
	self.damage = function (attacker) {
		console.log("Unit " + self.name + " hit! ", attacker);
		self.asset.tint = 0xff0000;
		self.lastAttaker = attacker;
		self.lastHitTime = Date.now();
		LK.setTimeout(function () {
			self.asset.tint = getPlayerTint(self.playerId);
		}, 300);
		self.health -= attacker.attackDamage;
		if (self.health <= 0 && !self.isDestroying) {
			self.isDestroying = true;
			console.log("Unit " + self.name + " destroyed");
			self.clean();
			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...");
					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();
					self.isDestroyed = true;
					if (currentUserActionState === UserActionState.SET_ORDER_TARGET && currentSelection == self) {
						currentSelection = null;
						currentUserActionState = UserActionState.NAVIGATING;
						if (cancelActionBtn) {
							cancelActionBtn.visible = false;
						}
					}
					updateActionBoard();
				}
			};
			growExplosion(1);
		}
		LK.setTimeout(function () {
			if (self.health > 0 && Date.now() - self.lastHitTime > aiUnderAttackDelayMs) {
				// Not under attack anymore
				self.lastAttaker = null;
				self.defenders = [];
			}
		}, aiUnderAttackDelayMs * 1.1);
	};
	self.stopAttack = function () {
		self.attackMode = false;
		self.attackTarget = null;
	};
	self.stopOtherActions = function () {
		console.log("Unit stopOtherActions :");
		self.attackMode = false; // Stop attacking
	};
	self.clean = function () {
		self.lastAttackTime = 0;
		self.lastHitTime = 0;
		self.lastAttaker = null;
		self.defenders = [];
	};
	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 UnitLightTank = Unit.expand(function (x, y, playerId) {
	var self = Unit.call(this, x, y, playerId, 'unitLightTank');
	// Additional properties and methods for UnitLightTank can be added here
	return self;
});
var UnitHeavyTank = Unit.expand(function (x, y, playerId) {
	var self = Unit.call(this, x, y, playerId, 'unitHeavyTank');
	// Additional properties and methods for UnitHeavyTank 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 = 0.3; // Adjust this value to control the speed of harvesting
	self.unloadSpeed = 0.4; // Adjust this value to control the speed of harvesting
	self.setHarvesting = function () {
		//console.log("Ok at site setHarvesting...");
		self.harvestingMode = 1;
	};
	self.startHarvesting = function (alternate) {
		//console.log("startHarvesting...");
		self.harvestingMode = 0;
		var deltaX = 0;
		var deltaY = 0;
		if (alternate) {
			//console.log("search alternate spot...");
			deltaX = -1 + Math.floor(Math.random() * 3);
			deltaY = -1 + Math.floor(Math.random() * 3);
		}
		var closestSpiceSpot = findClosestSpiceSpot(self.cellX + deltaX, self.cellY + deltaY);
		if (closestSpiceSpot) {
			//console.log("closestSpiceSpot :", closestSpiceSpot);
			var path = gameMap.findPath({
				x: self.cellX,
				y: self.cellY
			}, {
				x: closestSpiceSpot.x,
				y: closestSpiceSpot.y
			});
			if (!path || path.length == 0) {
				console.warn("No path to spot! ", closestSpiceSpot, ". Retry later");
				LK.setTimeout(function () {
					self.startHarvesting(true);
				}, 1000);
				return;
			}
			self.moveAlongPath(path); //self.setHarvesting
			//console.log("Ok at site setHarvesting...");
			self.harvestingMode = 1;
		} else {
			console.warn("No spice found! retry later");
			// Try again later
			LK.setTimeout(function () {
				self.startHarvesting(true);
			}, 1000);
		}
	};
	self.startUnloading = function () {
		self.harvestingMode = 3;
		//console.log("Unloading spice...");
	};
	self.update = function () {
		if (self.harvestingMode == 1 && self.harvestedSpiceOnCellCounter < 200) {
			if (!gameMap.cells[self.cellX][self.cellY].spice) {
				if (!self.isMoving) {
					//console.log("No spice here ");
					//self.startHarvesting(true);
				}
				return;
			}
			self.harvestedSpiceOnTripCounter += self.harvestSpeed;
			self.harvestedSpiceOnCellCounter += self.harvestSpeed;
			if (self.harvestedSpiceOnCellCounter >= 200) {
				self.asset.tint = getPlayerTint(self.playerId);
				gameMap.clearCellSpice(self.cellX, self.cellY);
				self.harvestedSpiceOnCellCounter = 0;
				if (self.harvestedSpiceOnTripCounter >= self.harvestedSpiceCapacity) {
					self.harvestingMode = 2; // Returning
				} else {
					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 : getPlayerTint(self.playerId);
				self.asset.rotation += 45 / 400 * (Math.PI / 180);
			}
		}
		if (self.harvestingMode == 2) {
			// Returning
			var closestRefinery = findClosestRefinery(self);
			if (closestRefinery) {
				var path = gameMap.findPath({
					x: self.cellX,
					y: self.cellY
				}, {
					x: closestRefinery.cellX,
					y: closestRefinery.cellY
				}, 0, closestRefinery);
				if (path.length > 0) {
					//console.log("Returning to Refiniery...");
					self.moveAlongPath(path, self.startUnloading);
				} else {
					console.warn("No path to Refinery !");
					// Wait for the refinery to be ready
					self.harvestingMode = 0; // Idle
					LK.setTimeout(function () {
						self.harvestingMode = 2;
					}, 2000);
				}
			}
		}
		if (self.harvestingMode == 3) {
			// Unloading
			self.harvestedSpiceOnTripCounter -= self.unloadSpeed;
			// Add the unloaded spice to the player's spice counter
			if (self.playerId === player1.playerId) {
				player1.spice += self.unloadSpeed;
			} else if (self.playerId === player2.playerId) {
				player2.spice += self.unloadSpeed;
			}
			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 : getPlayerTint(self.playerId);
			if (self.harvestedSpiceOnTripCounter <= 0) {
				//console.log("Unloading finished...");
				self.asset.tint = getPlayerTint(self.playerId);
				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 = getPlayerTint(self.playerId);
	};
	// 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': 10,
			'sightRange': 4,
			'attackRange': 2,
			'attackSpeed': 2,
			'attackDamage': 1
		},
		'unitLightTank': {
			'name': 'Light Tank',
			'cost': 400,
			'energy': 0,
			'constructionTime': 2,
			// 10
			'className': 'UnitLightTank',
			'actions': ['move', 'attack', 'return'],
			'health': 20,
			'sightRange': 6,
			'attackRange': 4,
			'attackSpeed': 3,
			'attackDamage': 3
		},
		'unitHeavyTank': {
			'name': 'Heavy Tank',
			'cost': 800,
			'energy': 0,
			'constructionTime': 15,
			'className': 'UnitHeavyTank',
			'actions': ['move', 'attack', 'return'],
			'health': 60,
			'sightRange': 8,
			'attackRange': 6,
			'attackSpeed': 5,
			'attackDamage': 6
		}
		// 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 to find an emplacement for a building based on its size and a starting cell
function findEmplacementForBuilding(building, cellX, cellY) {
	// Check if the building size is defined
	if (!building.cellW || !building.cellH) {
		console.error("Building size is not defined.");
		return null;
	}
	// Define the search area around the given cellX, cellY
	var searchRadius = 10; // Search within 5 cells around the given cell
	for (var dx = -searchRadius; dx <= searchRadius; dx++) {
		for (var dy = -searchRadius; dy <= searchRadius; dy++) {
			var candidateX = cellX + dx;
			var candidateY = cellY + dy;
			// Check if the candidate area is within the map bounds
			if (candidateX >= 0 && candidateX + building.cellW <= mapXSize && candidateY >= 0 && candidateY + building.cellH <= mapYSize) {
				var fits = true;
				// Check if the entire building area is on rocks and not occupied
				for (var w = 0; w < building.cellW; w++) {
					for (var h = 0; h < building.cellH; h++) {
						var targetCell = gameMap.cells[candidateX + w][candidateY + h];
						if (targetCell.terrain !== 'rock' || targetCell.building) {
							fits = false;
							break;
						}
					}
					if (!fits) {
						break;
					}
				}
				// If the area fits, return the top-left corner cell of the area
				if (fits) {
					return {
						cellX: candidateX,
						cellY: candidateY
					};
				}
			}
		}
	}
	// If no suitable area is found, return null
	return null;
}
function findFactoryForUnit(unitType, player) {
	// Determine the required factory type based on the unit type
	var requiredFactoryType;
	switch (unitType) {
		case 'unitQuad':
		case 'unitLightTank':
			requiredFactoryType = 'lightFactory';
			break;
		case 'unitHeavyTank':
		case 'unitHarvester':
			requiredFactoryType = 'heavyFactory';
			break;
		default:
			console.error('Unknown unit type for factory requirement:', unitType);
			return null;
	}
	// Find the first factory of the required type that belongs to the player
	var suitableFactory = player.buildings.find(function (building) {
		return building.type === requiredFactoryType;
	});
	return suitableFactory || null; // Return the factory or null if not found
}
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;
		case 'UnitLightTank':
			unit = new UnitLightTank(x, y, playerId);
			var path = findAvailablePositionAroundBuilding(x, y);
			if (path.length > 0) {
				unit.moveAlongPath(path, function () {});
			}
			break;
		case 'UnitHeavyTank':
			unit = new UnitHeavyTank(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) {
		if (aiCurrentlyBuildingForArmy) {
			unit.isArmy = true;
			aiCurrentArmyUnits.push(unit);
		}
		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, isAttack) {
	console.log("MoveActionLogic...", destinationX, ',', destinationY, ", isAttack=", isAttack);
	if (targetUnit && targetUnit.isUnit) {
		targetUnit.stopOtherActions();
		var path = gameMap.findPath({
			x: targetUnit.cellX,
			y: targetUnit.cellY
		}, {
			x: destinationX,
			y: destinationY
		}, isAttack ? targetUnit.attackRange : 0);
		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.attackRange);
		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 && 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
		}, 0, 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(buildableType, buildableSourceFactory, currentPlayer, progressDisplay) {
	console.log(currentPlayer.playerId + " - enqueueBuildable...", buildableType, buildableSourceFactory);
	if (currentPlayer.playerId === player1.playerId && (!currentSelection || !currentSelection.buildable.includes(buildableType))) {
		console.log(currentPlayer.playerId + " - Cannot build this item here.");
		return;
	}
	var buildableInfo = buildableRepository.getBuildableInfo(buildableType);
	if (buildableInfo && currentPlayer.spice < buildableInfo.cost) {
		console.log(currentPlayer.playerId + " - Not enough resources to build.");
		if (currentPlayer.playerId === player1.playerId) {
			progressDisplay.blinkRed();
		}
		return;
	}
	if (currentPlayer.playerId === player1.playerId) {
		updateBaseInfo();
	}
	var constructionTime = buildableInfo.constructionTime;
	var buildable;
	switch (buildableInfo.className) {
		case 'ConstructionYard':
			buildable = new ConstructionYard(0, 0, currentPlayer.playerId);
			break;
		case 'WindTrap':
			buildable = new WindTrap(0, 0, currentPlayer.playerId);
			break;
		case 'SpiceRefinery':
			buildable = new SpiceRefinery(0, 0, currentPlayer.playerId);
			break;
		case 'LightFactory':
			buildable = new LightFactory(0, 0, currentPlayer.playerId);
			break;
		case 'HeavyFactory':
			buildable = new HeavyFactory(0, 0, currentPlayer.playerId);
			break;
		case 'UnitQuad':
			buildable = new UnitQuad(0, 0, currentPlayer.playerId);
			break;
		case 'UnitLightTank':
			buildable = new UnitLightTank(0, 0, currentPlayer.playerId);
			break;
		case 'UnitHeavyTank':
			buildable = new UnitHeavyTank(0, 0, currentPlayer.playerId);
			break;
		case 'UnitHarvester':
			buildable = new UnitHarvester(0, 0, currentPlayer.playerId);
			break;
		// Add cases for other buildings as needed
	}
	if (!buildable) {
		console.error(currentPlayer.playerId + " - Building object is not defined.");
		return;
	}
	buildable.constructionProgress = 0;
	buildable.constructionTimer = LK.setInterval(function () {
		buildable.constructionProgress += 0.1;
		var progressFraction = buildable.constructionProgress / constructionTime;
		if (currentPlayer.playerId === player1.playerId) {
			buildable.progressDisplay.setProgress(progressFraction);
		}
		// Deduct spice progressively based on construction progress
		var costPerTick = buildableInfo.cost * 0.1 / constructionTime;
		currentPlayer.spice -= costPerTick;
		if (currentPlayer.playerId === player1.playerId) {
			updateBaseInfo();
		}
		// When construction is complete...
		if (buildable.constructionProgress >= constructionTime) {
			LK.clearInterval(buildable.constructionTimer);
			console.log(currentPlayer.playerId + " - " + buildableType + " construction completed.");
			buildable.constructionTimer = null;
			if (currentPlayer.playerId === player1.playerId) {
				buildable.progressDisplay.hide();
				if (buildable.isBuilding) {
					currentPlayer.isCurrentlyBuilding = false;
					console.log(currentPlayer.playerId + " - Building Ready for placement ", buildable);
					if (progressDisplay.parentIcon && typeof progressDisplay.parentIcon.setLabelToPlace === 'function') {
						progressDisplay.parentIcon.setLabelToPlace(); // Change label text to 'Place'
					}
					currentBuildingForPlacement = buildable;
				}
				//console.log("REMOVING PROGRESS DISPLAY ON ", buildable.progressDisplay.parent);
				//buildable.progressDisplay.parent.progressDisplay = null;
			} else {
				if (buildable.isBuilding) {
					currentPlayer.isCurrentlyBuilding = false;
					aiPlaceBuilding(buildable);
				}
			}
			if (buildable.isUnit) {
				console.log(currentPlayer.playerId + " - Unit Ready to spawn ", buildable);
				currentPlayer.isCurrentlyBuildingUnit = false;
				var spawnCell = {
					cellX: 0,
					cellY: 0
				};
				if (buildableSourceFactory) {
					console.log(currentPlayer.playerId + " - Found source factory ", buildableSourceFactory);
					spawnCell.cellX = buildableSourceFactory.cellX;
					spawnCell.cellY = buildableSourceFactory.cellY;
				} else {
					console.log(currentPlayer.playerId + " - No source factory found ", buildableSourceFactory);
				}
				spawnUnit(buildable.type, spawnCell.cellX, spawnCell.cellY, currentPlayer.playerId);
			}
			if (currentPlayer.playerId === player1.playerId) {
				game.children.forEach(function (child) {
					if (child instanceof BuildableItemIcon) {
						child.asset.interactive = true;
						child.alpha = 1.0;
					}
				});
			}
		}
	}, 100);
	if (currentPlayer.playerId === player1.playerId) {
		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 for buildings
		if (buildable.isBuilding) {
			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) {
	//console.log("getPlayerTint", id);
	return player1.playerId === id ? player1.tint : player2.tint;
};
var getNextId = function getNextId() {
	return nextId++;
};
var orderEntitiesPerDistance = function orderEntitiesPerDistance(position, entityList) {
	var orderedEntityList = [];
	// Calculer la distance de chaque unité par rapport à la position donnée
	entityList.forEach(function (entity) {
		var distance = Math.sqrt(Math.pow(position.cellX - entity.cellX, 2) + Math.pow(position.cellY - entity.cellY, 2));
		// Ajouter l'unité à la liste
		orderedEntityList.push({
			entity: entity,
			distance: distance
		});
		console.log("entity", entity.id, " ", entity.type, " Pos:", entity.cellX, entity.cellY, " distance", distance);
	});
	// Trier les unités en fonction de leurs distances
	orderedEntityList.sort(function (a, b) {
		return a.distance - b.distance;
	});
	console.log("orderedEntityList", orderedEntityList);
	return orderedEntityList;
};
/* ******************************************** GLOBAL GAME VARIABLES ******************************************** */ 
/* ******************************************** GLOBAL GAME VARIABLES ******************************************** */ 
/* ******************************************** GLOBAL GAME VARIABLES ******************************************** */ 
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
};
var nextId = 1;
// 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 = [];
var fogOfWar = [];
// 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
};
var rockIlot2Center = {
	x: 0,
	y: 0
};
/* ***************************************************************************************************** */ 
/* ********************************************* AI FUNCTIONS ****************************************** */
/* ***************************************************************************************************** */ 
var AI_LEVEL = {
	EASY: {
		actingDelay: 50000,
		//500, /// TEMP DEBUG !!!
		thinkingDelay: 50000 //500, /// TEMP DEBUG !!!
	},
	NORMAL: {
		actingDelay: 300,
		thinkingDelay: 600
	},
	HARD: {
		actingDelay: 100,
		thinkingDelay: 200
	}
};
var aiCurrentLevel = AI_LEVEL.EASY;
var aiBaseUnitList = {
	'unitHarvester': 1,
	'unitQuad': 3,
	'unitLightTank': 2,
	'unitHeavyTank': 4
};
var aiBaseBuildingList = {
	'windTrap': 3,
	'spiceRefinery': 1,
	'lightFactory': 1,
	'heavyFactory': 1
};
var aiCurrentUnitConstruction = null;
var aiCurrentBuildingConstruction = null;
var aiNeededBuildingList = null;
var aiNeededDefenseUnitsList = null;
var aiBuildingsUnderAttackList = null;
var aiUnitsUnderAttackList = null;
var aiCurrentArmyUnits = [];
var aiCurrentlyBuildingForArmy = false;
var aiArmyIsReady = false;
var aiNeededAttackUnitsList = null;
var aiLastAttackWaveTime = 0;
var aiNextAttackWaveSize = 0;
var aiCurrentAttackWave = 1;
var aiNextAttackWaveDelayMs = 1000 * 5; //1000 * 60; // TODO : handle in AiLevel
var aiUnderAttackDelayMs = aiCurrentLevel.thinkingDelay * 10;
// Think of what AI must do
function aiThinking() {
	if (LK.ticks % aiCurrentLevel.thinkingDelay !== 0) {
		return;
	}
	console.log("aiThinking...");
	// Check buildings requirements
	aiNeededBuildingList = checkBuildingRequirements();
	// Check units requirements
	aiNeededDefenseUnitsList = checkDefenseUnitsRequirements();
	// Check buildings defense needs
	aiBuildingsUnderAttackList = checkBuildingUnderAttack();
	// Check units defense needs
	aiUnitsUnderAttackList = checkUnitsUnderAttack();
	// Check attack opportunities
	aiNeededAttackUnitsList = checkAttckWaveUnitNeeds();
}
function checkBuildingRequirements() {
	console.log("checkBuildingRequirements...");
	console.log("player2 buildings:", player2.buildings);
	// 1) Count currently owned buildings
	var aiCurrentBuildingList = {
		'windTrap': 0,
		'spiceRefinery': 0,
		'lightFactory': 0,
		'heavyFactory': 0
	};
	player2.buildings.forEach(function (building) {
		aiCurrentBuildingList[building.type] = (aiCurrentBuildingList[building.type] || 0) + 1;
	});
	console.log("aiCurrentBuildingList:", aiCurrentBuildingList);
	// 2) Check if all requirements are met
	var tempNeededBuildingList = {};
	// Compare aiBaseBuildingList and aiCurrentBuildingList
	for (var building in aiBaseBuildingList) {
		if (aiBaseBuildingList[building] > aiCurrentBuildingList[building]) {
			tempNeededBuildingList[building] = aiBaseBuildingList[building] - aiCurrentBuildingList[building];
		}
	}
	return tempNeededBuildingList;
}
function checkBuildingUnderAttack() {
	console.log("checkBuildingUnderAttack...");
	// Check delay since lastHitTime of all AI buildings
	var tempUnderAttackBuildingArray = [];
	player2.buildings.forEach(function (building) {
		if (Date.now() - building.lastHitTime < aiUnderAttackDelayMs) {
			// Building is under attack
			tempUnderAttackBuildingArray.push(building);
		}
	});
	return tempUnderAttackBuildingArray;
}
function checkUnitsUnderAttack() {
	console.log("checkUnitsUnderAttack...");
	// Check delay since lastHitTime of all AI units except army
	var tempUnderAttackUnitsArray = [];
	player2.units.forEach(function (unit) {
		if (!unit.isArmy && Date.now() - unit.lastHitTime < aiUnderAttackDelayMs) {
			// Unit is under attack
			tempUnderAttackUnitsArray.push(unit);
		}
	});
	return tempUnderAttackUnitsArray;
}
function checkAttckWaveUnitNeeds() {
	console.log("checkAttckWaveUnitNeeds...Wave ", aiCurrentAttackWave);
	var tempNeededUnitList = {};
	var aiBaseUnitListPerWave = getUnitNeedsPerWave(aiCurrentAttackWave);
	console.log("Wave requires :", aiBaseUnitListPerWave);
	// aiThinking : evaluate army needs => list needed units
	// Browse aiCurrentArmyUnits and compare with aiBaseUnitListPerWave 
	var tempNbQuads = 0;
	var tempNbLightTanks = 0;
	var tempNbHeavyTanks = 0;
	console.log("check current ", aiCurrentArmyUnits.length, " army units:", aiCurrentArmyUnits);
	for (var i = 0; i < aiCurrentArmyUnits.length; i++) {
		var _aiCurrentArmyUnits$i, _aiCurrentArmyUnits$i2;
		console.log("check army unit:", (_aiCurrentArmyUnits$i = aiCurrentArmyUnits[i]) === null || _aiCurrentArmyUnits$i === void 0 ? void 0 : _aiCurrentArmyUnits$i.type, " h=", (_aiCurrentArmyUnits$i2 = aiCurrentArmyUnits[i]) === null || _aiCurrentArmyUnits$i2 === void 0 ? void 0 : _aiCurrentArmyUnits$i2.health);
		if (!aiCurrentArmyUnits[i] || aiCurrentArmyUnits[i].health <= 0) {
			continue;
		}
		console.log("Take ", aiCurrentArmyUnits[i].type, "#", aiCurrentArmyUnits[i].id, " H=", aiCurrentArmyUnits[i].health);
		switch (aiCurrentArmyUnits[i].type) {
			case "unitQuad":
				tempNbQuads++;
				break;
			case "unitLightTank":
				tempNbLightTanks++;
				break;
			case "unitHeavyTank":
				tempNbHeavyTanks++;
				break;
			default:
				console.error("unknown army unit type:", aiCurrentArmyUnits[i].type);
				break;
		}
	}
	console.log("=> We have:", tempNbQuads, tempNbLightTanks, tempNbHeavyTanks, " units");
	for (var j = 0; j < Object.keys(aiBaseUnitListPerWave).length; j++) {
		var tempUnitType = Object.keys(aiBaseUnitListPerWave)[j];
		if (!aiBaseUnitListPerWave[tempUnitType]) {
			continue;
		}
		switch (tempUnitType) {
			case "unitQuad":
				if (tempNbQuads < aiBaseUnitListPerWave[tempUnitType]) {
					tempNeededUnitList[tempUnitType] = aiBaseUnitListPerWave[tempUnitType] - tempNbQuads;
				}
				break;
			case "unitLightTank":
				if (tempNbLightTanks < aiBaseUnitListPerWave[tempUnitType]) {
					tempNeededUnitList[tempUnitType] = aiBaseUnitListPerWave[tempUnitType] - tempNbLightTanks;
				}
				break;
			case "unitHeavyTank":
				if (tempNbHeavyTanks < aiBaseUnitListPerWave[tempUnitType]) {
					tempNeededUnitList[tempUnitType] = aiBaseUnitListPerWave[tempUnitType] - tempNbHeavyTanks;
				}
				break;
			default:
				console.error("unknown base unit type:", tempUnitType);
		}
	}
	console.log("=> We need:", tempNeededUnitList);
	return tempNeededUnitList;
}
function getUnitNeedsPerWave(waveIndex) {
	console.log("getUnitNeedsPerWave...", waveIndex);
	/*
		Wave 1 : 1 Quad
		Wave 2 : 2 Quad
		Wave 3 : 2 Quad + 1 LightTank
		Wave 4 : 2 Quad + 2 LightTank
		Wave 5 : 2 Quad + 2 LightTank + 1 HeavyTank
		Wave 6+ : 2 Quad + 2 LightTank + 2 HeavyTank
	*/
	var tempNbQuads = Math.min(2, waveIndex);
	var tempNbLightTanks = Math.min(2, Math.max(0, waveIndex - 2));
	var tempNbHeavyTanks = Math.min(2, Math.max(0, waveIndex - 4));
	return {
		'unitQuad': tempNbQuads,
		'unitLightTank': tempNbLightTanks,
		'unitHeavyTank': tempNbHeavyTanks
	};
}
function checkDefenseUnitsRequirements() {
	console.log("checkDefenseUnitsRequirements...");
	console.log("player2 units:", player2.units);
	// 1) Count currently owned units
	var aiCurrentUnitList = {
		'unitHarvester': 0,
		'unitQuad': 0,
		'unitLightTank': 0,
		'unitHeavyTank': 0
	};
	player2.units.forEach(function (unit) {
		aiCurrentUnitList[unit.type] = (aiCurrentUnitList[unit.type] || 0) + 1;
	});
	console.log("aiCurrentUnitList:", aiCurrentUnitList);
	// 2) Check if all requirements are met
	var tempNeededUnitList = {};
	// Compare aiBaseUnitList and aiCurrentUnitList
	for (var unit in aiBaseUnitList) {
		if (aiBaseUnitList[unit] > aiCurrentUnitList[unit]) {
			tempNeededUnitList[unit] = aiBaseUnitList[unit] - aiCurrentUnitList[unit];
		}
	}
	return tempNeededUnitList;
}
// Do what AI thought about
function aiActing() {
	if (LK.ticks % aiCurrentLevel.actingDelay !== 0) {
		return;
	}
	console.log("aiActing...");
	// 1) Meet buildings requirements
	console.log("aiNeededBuildingList:", aiNeededBuildingList);
	if (Object.keys(aiNeededBuildingList).length > 0 && !player2.isCurrentlyBuilding) {
		aiHandleBuildingConstruction(Object.keys(aiNeededBuildingList)[0]);
	}
	// 2) Meet units requirements
	console.log("aiNeededUnitsList:", aiNeededDefenseUnitsList);
	if (Object.keys(aiNeededDefenseUnitsList).length > 0 && !player2.isCurrentlyBuildingUnit) {
		var tempUnitToBuild = Object.keys(aiNeededDefenseUnitsList)[0];
		var tempSourceFactory = findFactoryForUnit(tempUnitToBuild, player2);
		if (tempSourceFactory) {
			aiHandleUnitConstruction(tempUnitToBuild, tempSourceFactory);
		} else {
			console.warn("2 - No factory found for unit " + tempUnitToBuild);
		}
	}
	// 3) Meet building defense needs
	console.log("aiBuildingsUnderAttackList:", aiBuildingsUnderAttackList);
	if (aiBuildingsUnderAttackList.length > 0) {
		aiBuildingsUnderAttackList.forEach(function (building) {
			aiHandleBuildingUnderAttack(building);
		});
	}
	// 4) Meet unit defense needs
	console.log("aiUnitsUnderAttackList:", aiUnitsUnderAttackList);
	if (aiUnitsUnderAttackList.length > 0) {
		aiUnitsUnderAttackList.forEach(function (unit) {
			aiHandleUnitUnderAttack(unit);
		});
	}
	// 5) Meet attack wave needs
	if (Object.keys(aiNeededAttackUnitsList).length > 0) {
		aiArmyIsReady = false;
		console.log("Army not ready:", aiNeededAttackUnitsList);
		// Take 1st needed unit
		var tempUnitToBuild = Object.keys(aiNeededAttackUnitsList)[0];
		var tempSourceFactory = findFactoryForUnit(tempUnitToBuild, player2);
		if (tempSourceFactory) {
			aiHandleUnitConstruction(tempUnitToBuild, tempSourceFactory, true);
		} else {
			console.warn("5 - No factory found for unit " + tempUnitToBuild);
		}
	} else {
		console.log("Clean Army...", aiCurrentArmyUnits);
		var tempCleanArmy = aiCurrentArmyUnits.filter(function (unit) {
			// keep only units alive
			return !unit.isDestroyed;
		});
		aiCurrentArmyUnits = tempCleanArmy;
		if (aiCurrentArmyUnits.length >= aiCurrentAttackWave) {
			// ATTENTION !!! Arbitray rue : Nb units == Wave index
			console.log("Army is ready:", aiCurrentArmyUnits);
			aiArmyIsReady = true;
		}
	}
	// 6) Launch attack wave
	if (aiArmyIsReady && Date.now() - aiLastAttackWaveTime > aiNextAttackWaveDelayMs) {
		aiLastAttackWaveTime = Date.now();
		console.warn("6 - ATTACK !!! Wave " + aiCurrentAttackWave + " at ", new Date().toLocaleTimeString());
		aiCurrentAttackWave++;
		aiNextAttackWaveDelayMs = Math.min(5 * 60 * 1000, aiCurrentAttackWave * 60 * 1000);
		// Find a building to attack
		var firstAttaker = null;
		for (var i = 0; i < aiCurrentArmyUnits.length; i++) {
			if (!aiCurrentArmyUnits[i] || aiCurrentArmyUnits[i].health <= 0) {
				continue;
			}
			firstAttaker = aiCurrentArmyUnits[i];
			break;
		}
		var nextBuildingTarget = aiFindClosestBuildingToAttack(firstAttaker);
		if (nextBuildingTarget) {
			aiStartAttack(nextBuildingTarget);
		} else {
			console.warn("6 - No building found to attack");
		}
	}
}
function aiHandleBuildingConstruction(buildingToBuild) {
	console.log("aiHandleBuildingConstruction...", buildingToBuild);
	if (player2.isCurrentlyBuilding) {
		console.log("Already building building...");
		return;
	}
	player2.isCurrentlyBuilding = true;
	enqueueBuildable(buildingToBuild, null, player2);
}
function aiPlaceBuilding(buildable) {
	console.log("aiPlaceBuilding...", buildable);
	if (!buildable || !buildable.isBuilding) {
		return;
	}
	var baseBuilding = findClosestConstructionYard(buildable);
	if (!baseBuilding) {
		console.warn(buildable.playerId + " - No Construction Yard to place building " + buildable.type + " around");
		return;
	}
	var freePosition = findEmplacementForBuilding(buildable, baseBuilding.cellX, baseBuilding.cellY);
	if (!freePosition) {
		console.warn(buildable.playerId + " - No free space to place building " + buildable.type);
		return;
	}
	var placeX = freePosition.cellX;
	var placeY = freePosition.cellY;
	handleBuildingPlacement(buildable, placeX, placeY);
}
function aiHandleBuildingUnderAttack(buildingToDefend) {
	console.log("aiHandleBuildingUnderAttack...", buildingToDefend);
	// Check if already have defenders
	if (buildingToDefend.defenders.length > 1) {
		console.log("already have defenders...", buildingToDefend.defenders);
		return;
	}
	// List all units per distance to building [] = { unit: unit, distance: distance }
	var potentialDefenders = orderEntitiesPerDistance(buildingToDefend, player2.units);
	// Browse potentialDefenders and add as defenders the ones that can attack & are idle
	potentialDefenders.forEach(function (defenderEntry) {
		var defender = defenderEntry.entity;
		console.log("potential defender ", defender.id, " attackDamage:", defender.attackDamage, " / moving:", defender.isMoving, " / attackMode:", defender.attackMode);
		if (buildingToDefend.defenders.length < 2 && defender.attackDamage && !defender.isMoving && defender.attackMode == 0) {
			buildingToDefend.defenders.push(defender);
		}
	});
	if (buildingToDefend.defenders.length > 0) {
		console.log(buildingToDefend.defenders.length + " defenders found");
		// Start attacking the first enemy spotted
		buildingToDefend.defenders.forEach(function (defender) {
			console.log("defender ", defender.id, " ", defender.type, " Will attack:", buildingToDefend.lastAttaker);
			defender.startAttack(buildingToDefend.lastAttaker);
		});
	} else {
		console.log("No defenders found");
	}
	console.warn("potentialDefenders:", potentialDefenders);
}
function aiHandleUnitUnderAttack(unitToDefend) {
	console.log("aiHandleUnitUnderAttack...", unitToDefend);
	// Check if already have defenders
	if (unitToDefend.defenders.length > 1) {
		console.log("unit already have defenders...", unitToDefend.defenders);
		return;
	}
	// List all units per distance to building [] = { unit: unit, distance: distance }
	var potentialDefenders = orderEntitiesPerDistance(unitToDefend, player2.units);
	// Browse potentialDefenders and add as defenders the ones that can attack & are idle
	potentialDefenders.forEach(function (defenderEntry) {
		var defender = defenderEntry.entity;
		console.log("potential defender ", defender.id, " attackDamage:", defender.attackDamage, " / moving:", defender.isMoving, " / attackMode:", defender.attackMode);
		if (unitToDefend.defenders.length < 2 && defender.attackDamage && !defender.isMoving && defender.attackMode == 0) {
			unitToDefend.defenders.push(defender);
		}
	});
	if (unitToDefend.defenders.length > 0) {
		console.log(unitToDefend.defenders.length + " defenders found");
		// Start attacking the first enemy spotted
		unitToDefend.defenders.forEach(function (defender) {
			console.log("defender ", defender.id, " ", defender.type, " Will attack:", unitToDefend.lastAttaker);
			defender.startAttack(unitToDefend.lastAttaker);
		});
	} else {
		console.log("No defenders found");
	}
}
function aiHandleUnitConstruction(unitToBuild, sourceFactory, forArmy) {
	console.log("aiHandleUnitConstruction...", unitToBuild, sourceFactory, forArmy);
	if (player2.isCurrentlyBuildingUnit) {
		console.log("Already building unit...");
		return;
	}
	player2.isCurrentlyBuildingUnit = true;
	aiCurrentlyBuildingForArmy = !!forArmy; // Flag to add the unit to the army
	enqueueBuildable(unitToBuild, sourceFactory, player2);
}
function aiFindClosestBuildingToAttack(firstAttaker) {
	// Browse player1.buildings
	var potientialTargetBuildings = orderEntitiesPerDistance(firstAttaker, player1.buildings);
	if (potientialTargetBuildings && potientialTargetBuildings.length > 0) {
		return potientialTargetBuildings[0].entity;
	}
	return null;
}
function aiStartAttack(target) {
	var nbUnits = aiCurrentArmyUnits.length;
	console.log("aiStartAttack... nbUnits=", nbUnits, " target=", target);
	// Send all army around the target
	for (var i = 0; i < nbUnits; i++) {
		// Find a place for each unit by adding a delta depending on index
		var deltaX = -1 + i % 3;
		var deltaY = Math.floor(i / 3);
		moveActionLogic(aiCurrentArmyUnits[i], target.cellX + deltaX, target.cellY + deltaY, true);
	}
}
/* ***************************************************************************************************** */ 
/* ********************************************* INIT FUNCTIONS ****************************************** */
/* ***************************************************************************************************** */ 
// 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: 8,
		y: 10
	};
	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;
			}
		}
	}
	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);
	// 2 quads for player 1
	var p1Quad1 = new UnitQuad(rockIlot1Center.x - 2, rockIlot1Center.y + 1, 1);
	gameMap.cells[rockIlot1Center.x][rockIlot1Center.y - 6].unit = p1Quad1;
	player1.addUnit(p1Quad1);
	game.addChild(p1Quad1);
	var p1Quad2 = new UnitQuad(rockIlot1Center.x + 3, rockIlot1Center.y + 1, 1);
	gameMap.cells[rockIlot1Center.x][rockIlot1Center.y + 6].unit = p1Quad2;
	player1.addUnit(p1Quad2);
	game.addChild(p1Quad2);
	// Player 2 base
	// Define a fixed rock ilot near the center right of the map
	rockIlot2Center = {
		x: 30,
		y: 30
	};
	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);
	var windTrap1 = new WindTrap(rockIlot2Center.x + 3, rockIlot2Center.y, 2);
	gameMap.cells[rockIlot2Center.x + 3][rockIlot2Center.y].building = windTrap1;
	applyCellOccupation(rockIlot2Center.x + 3, rockIlot2Center.y);
	player2.addBuilding(windTrap1);
	var windTrap2 = new WindTrap(rockIlot2Center.x - 3, rockIlot2Center.y, 2);
	gameMap.cells[rockIlot2Center.x - 3][rockIlot2Center.y].building = windTrap2;
	applyCellOccupation(rockIlot2Center.x - 3, rockIlot2Center.y);
	player2.addBuilding(windTrap2);
	var refinery1 = new SpiceRefinery(rockIlot2Center.x - 3, rockIlot2Center.y + 3, 2);
	gameMap.cells[rockIlot2Center.x - 3][rockIlot2Center.y + 3].building = refinery1;
	applyCellOccupation(rockIlot2Center.x - 3, rockIlot2Center.y + 3);
	player2.addBuilding(refinery1);
	var heavyFactory1 = new HeavyFactory(rockIlot2Center.x + 2, rockIlot2Center.y + 3, 2);
	gameMap.cells[rockIlot2Center.x + 2][rockIlot2Center.y + 3].building = heavyFactory1;
	applyCellOccupation(rockIlot2Center.x + 2, rockIlot2Center.y + 3);
	player2.addBuilding(heavyFactory1);
	var lightFactory1 = new LightFactory(rockIlot2Center.x, rockIlot2Center.y - 3, 2);
	gameMap.cells[rockIlot2Center.x][rockIlot2Center.y - 3].building = lightFactory1;
	applyCellOccupation(rockIlot2Center.x, rockIlot2Center.y - 3);
	player2.addBuilding(lightFactory1);
	var quad1 = new UnitQuad(rockIlot2Center.x, rockIlot2Center.y - 6, 2);
	gameMap.cells[rockIlot2Center.x][rockIlot2Center.y - 6].unit = quad1;
	player2.addUnit(quad1);
	game.addChild(quad1);
	var quad2 = new UnitQuad(rockIlot2Center.x, rockIlot2Center.y + 6, 2);
	gameMap.cells[rockIlot2Center.x][rockIlot2Center.y + 6].unit = quad2;
	player2.addUnit(quad2);
	game.addChild(quad2);
	var heavyTank1 = new UnitHeavyTank(rockIlot2Center.x - 5, rockIlot2Center.y - 5, 2);
	gameMap.cells[rockIlot2Center.x - 5][rockIlot2Center.y - 5].unit = heavyTank1;
	player2.addUnit(heavyTank1);
	game.addChild(heavyTank1);
	var heavyTank2 = new UnitHeavyTank(rockIlot2Center.x + 5, rockIlot2Center.y - 5, 2);
	gameMap.cells[rockIlot2Center.x + 5][rockIlot2Center.y - 5].unit = heavyTank2;
	player2.addUnit(heavyTank2);
	game.addChild(heavyTank2);
	var heavyTank3 = new UnitHeavyTank(rockIlot2Center.x + 5, rockIlot2Center.y + 5, 2);
	gameMap.cells[rockIlot2Center.x + 5][rockIlot2Center.y + 5].unit = heavyTank3;
	player2.addUnit(heavyTank3);
	game.addChild(heavyTank3);
	var heavyTank4 = new UnitHeavyTank(rockIlot2Center.x - 5, rockIlot2Center.y + 5, 2);
	gameMap.cells[rockIlot2Center.x - 5][rockIlot2Center.y + 5].unit = heavyTank4;
	player2.addUnit(heavyTank4);
	game.addChild(heavyTank4);
	var lightTank5 = new UnitLightTank(rockIlot2Center.x - 1, rockIlot2Center.y - 1, 2);
	gameMap.cells[rockIlot2Center.x - 1][rockIlot2Center.y - 1].unit = lightTank5;
	player2.addUnit(lightTank5);
	game.addChild(lightTank5);
	var lightTank6 = new UnitLightTank(rockIlot2Center.x + 2, rockIlot2Center.y + 2, 2);
	gameMap.cells[rockIlot2Center.x + 2][rockIlot2Center.y + 2].unit = lightTank6;
	player2.addUnit(lightTank6);
	game.addChild(lightTank6);
	var harvester1 = new UnitHarvester(rockIlot2Center.x - 3, rockIlot2Center.y + 5, 2);
	gameMap.cells[rockIlot2Center.x - 3][rockIlot2Center.y + 5].unit = harvester1;
	player2.addUnit(harvester1);
	game.addChild(harvester1);
	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);
	harvester1.startHarvesting();
	initFogOfWar();
	updateFogOfWar();
}
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 = 4 + Math.floor(Math.random() * 3);
		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
					});
				}
			}
		}
	}
}
function initFogOfWar() {
	for (var x = 0; x < mapXSize; x++) {
		for (var y = 0; y < mapYSize; y++) {
			fogOfWar.push({
				x: x,
				y: y
			});
			gameMap.cells[x][y].fog = false; // true; 	// TEMP DEBUG !!!// TEMP DEBUG !!!// TEMP DEBUG !!!
			gameMap.cells[x][y].fogAsset = LK.getAsset('fogOfWar', {
				x: x * 100,
				y: y * 100,
				tint: 0x222222
			});
		}
	}
}
function updateFogOfWar() {
	console.log("updateFogOfWar...");
	player1.units.concat(player1.buildings).forEach(function (element) {
		for (var dx = -5; dx <= 5; dx++) {
			for (var dy = -5; dy <= 5; dy++) {
				var x = element.cellX + dx;
				var y = element.cellY + dy;
				if (x >= 0 && x < mapXSize && y >= 0 && y < mapYSize) {
					gameMap.cells[x][y].fog = false;
				}
			}
		}
	});
}
//#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;
		console.log("Trying cell ", cellX + ',' + cellY);
		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 - viewPort.wCellX) * tileSize;
		currentBuildingForPlacement.y = (cellY + currentBuildingForPlacement.cellH / 2 - viewPort.wCellY) * 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);
		if (currentSelection && currentSelection.health > 0) {
			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();
		if (cancelActionBtn) {
			cancelActionBtn.visible = false;
		}
		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) {
					if (currentSelection && currentSelection.isUnit && currentSelection.cellX == cellX && currentSelection.cellY == cellY) {
						console.log("Already selected unit at " + cellX + ',' + cellY);
					} else {
						currentSelection = null;
						selectionMarker.setOnElement();
						selectionMarker.hide();
						//console.log("Nothing selected at " + cellX + ',' + cellY, " deltaToSelection=" + deltaToSelection, " dragDelta=", dragDelta);
						updateActionBoard();
					}
				}
			} else if (cell.building && !cell.fog) {
				//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 (building instanceof SpiceRefinery) {
		spawnUnit('unitHarvester', cellX, cellY, building.playerId);
	}
	if (player1.playerId === building.playerId) {
		player1.updateEnergy();
		updateBaseInfo();
		currentUserActionState = UserActionState.NAVIGATING;
		currentBuildingForPlacement = null;
		if (cancelActionBtn) {
			cancelActionBtn.visible = false;
		}
		updateActionBoard(true);
		updateFogOfWar();
	}
};
// 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(resetProgress) {
	console.log('updateActionBoard...' + (currentSelection ? currentSelection.name : 'No selection'));
	if (!currentSelection || currentSelection.health <= 0) {
		currentSelection = null;
	}
	currentSelectionText.setText(currentSelection ? currentSelection.name : '');
	clearIconBorders();
	displayBuildableItems(resetProgress); // 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(resetProgress) {
	console.log('displayBuildableItems...', currentSelection);
	var buildableItemIcons = game.children.filter(function (child) {
		return child instanceof BuildableItemIcon;
	});
	buildableItemIcons.forEach(function (icon) {
		//icon.destroy();  // TEMP DEBUG !!!
		if (icon.progressDisplay) {
			icon.progressDisplay.visible = !resetProgress && !!currentSelection;
		}
		icon.visible = false;
	});
	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) {
			console.log("Prepare icon for ", 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 = null;
			// Search if icon already exists
			var existingIcons = game.children.filter(function (child) {
				return child instanceof BuildableItemIcon && child.type === itemType;
			});
			console.log("Found existing icons", existingIcons);
			if (existingIcons.length > 0) {
				icon = existingIcons[0];
				if (resetProgress) {
					icon.restoreLabel();
				}
				icon.visible = true;
			} else {
				icon = new BuildableItemIcon(itemType, iconX, iconY, currentSelection);
			}
			game.addChild(icon);
			if (icon.progressDisplay) {
				icon.progressDisplay.visible = true;
				game.addChild(icon.progressDisplay);
			}
			//icon = new BuildableItemIcon(itemType, iconX, iconY, currentSelection); // TEMP DEBUG !!!
			//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.health > 0 && 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('| ' + Math.floor(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 checkGameEnd() {
	console.log("checkGameEnd...");
	// Check if either player1 or player2 has no more buildings
	if (player1.buildings.length === 0 || player2.buildings.length === 0) {
		LK.showGameOver();
	}
}
function initGame() {
	// Create players
	player1 = new Player(1, 0xFFFFFF);
	player2 = new Player(2, 0xFF8888);
	// 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('', {
			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('| ' + Math.floor(player1.spice).toString(), {
		size: 50,
		fill: '#FFFFFF',
		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: '#FFFFFF',
		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();
		player2.units.forEach(function (unit) {
			unit.asset.visible = !gameMap.cells[unit.cellX][unit.cellY].fog;
		});
		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);
			}
		});
		aiThinking();
		aiActing();
	}
	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); ===================================================================
--- original.js
+++ change.js
@@ -159,29 +159,27 @@
 			'name': 'Wind Trap',
 			'cost': 300,
 			'energy': 100,
 			'health': 50,
-			'constructionTime': 10,
+			'constructionTime': 5,
 			'className': 'WindTrap',
 			'buildable': []
 		},
 		'spiceRefinery': {
 			'name': 'Spice Refinery',
 			'cost': 600,
 			'energy': -50,
 			'health': 75,
-			'constructionTime': 2,
-			//15, TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!!
+			'constructionTime': 15,
 			'className': 'SpiceRefinery',
 			'buildable': []
 		},
 		'lightFactory': {
 			'name': 'Light Factory',
 			'cost': 400,
 			'energy': -30,
 			'health': 60,
-			'constructionTime': 2,
-			//15, TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!!
+			'constructionTime': 15,
 			'className': 'LightFactory',
 			'buildable': ['unitQuad'],
 			'requires': 'spiceRefinery'
 		},
@@ -189,10 +187,9 @@
 			'name': 'Heavy Factory',
 			'cost': 600,
 			'energy': -50,
 			'health': 80,
-			'constructionTime': 2,
-			// 30, TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!!
+			'constructionTime': 20,
 			'className': 'HeavyFactory',
 			'buildable': ['unitLightTank', 'unitHeavyTank', 'unitHarvester'],
 			'requires': 'lightFactory'
 		},
@@ -215,19 +212,17 @@
 		'unitHeavyTank': {
 			'name': 'Heavy Tank',
 			'cost': 600,
 			'energy': 0,
-			'constructionTime': 2,
-			//15,TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!!
+			'constructionTime': 15,
 			'className': 'UnitHeavyTank',
 			'buildable': []
 		},
 		'unitHarvester': {
 			'name': 'Harvester',
 			'cost': 500,
 			'energy': 0,
-			'constructionTime': 2,
-			//12,TEMP DEBUG !!!TEMP DEBUG !!!TEMP DEBUG !!!
+			'constructionTime': 12,
 			'className': 'UnitHarvester',
 			'buildable': []
 		}
 		// Add more buildables as needed
@@ -1830,9 +1825,9 @@
 	}
 	if (currentPlayer.playerId === player1.playerId) {
 		updateBaseInfo();
 	}
-	var constructionTime = buildableInfo.constructionTime * (1 + Math.max(0, 100 - currentPlayer.energy) / 50);
+	var constructionTime = buildableInfo.constructionTime;
 	var buildable;
 	switch (buildableInfo.className) {
 		case 'ConstructionYard':
 			buildable = new ConstructionYard(0, 0, currentPlayer.playerId);
:quality(85)/https://cdn.frvr.ai/65bf9b2ac10ed5ddaec99bb7.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/65bf9e2dc10ed5ddaec99bc7.png%3F3) 
 a tileable sand terrain tile.
:quality(85)/https://cdn.frvr.ai/65bfb45ec10ed5ddaec99c02.png%3F3) 
 A square tileable rock terrain tile WITHOUT BBORDER. Single Game Texture. In-Game asset. 2d. No shadows. No Border
:quality(85)/https://cdn.frvr.ai/65bff843c10ed5ddaec99dd1.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/65c14ae1fc3df84f65ed5adc.png%3F3) 
 Zenith view of Dune's Wind Trap power facility square fence. Ressembles a bit to Sydney's Opera. Zenith view Directly from above.
:quality(85)/https://cdn.frvr.ai/65c3fcc3fc3df84f65ed60d6.png%3F3) 
 :quality(85)/https://cdn.frvr.ai/65c547c707ae736c088fad03.png%3F3) 
 grey cancel icon. UI
:quality(85)/https://cdn.frvr.ai/65c566ba07ae736c088fad99.png%3F3) 
 thin white circle empty.
:quality(85)/https://cdn.frvr.ai/65c56ba507ae736c088fadb6.png%3F3) 
 0x5e86ff
:quality(85)/https://cdn.frvr.ai/65c6651b07ae736c088fb0a8.png%3F3) 
 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.
:quality(85)/https://cdn.frvr.ai/65c66abd07ae736c088fb0f2.png%3F3) 
 Minimal Ui icon of an right sign aside with an icon of a target. sand background
:quality(85)/https://cdn.frvr.ai/65c66f0507ae736c088fb19d.png%3F3) 
 Minimal icon of a home with direction icon pointing to the home. sand background
:quality(85)/https://cdn.frvr.ai/65c744a9fc3df84f65ed688a.png%3F3) 
 3 white flat isometric concentric circles like a target.
:quality(85)/https://cdn.frvr.ai/65c9466b63eecf378e78cbd9.png%3F3) 
 Remove background
:quality(85)/https://cdn.frvr.ai/65db729ccfd2bc39a2010629.png%3F3) 
 Gray background
:quality(85)/https://cdn.frvr.ai/65df9c99a5cbb70b7a1e04be.png%3F3) 
 Minimal Ui icon of target sign on a fire icon.. sand background
:quality(85)/https://cdn.frvr.ai/65dfd060c0074ce0cf5a63c6.png%3F3) 
 top view of an explosion effect.
:quality(85)/https://cdn.frvr.ai/65e067a9c0074ce0cf5a653b.png%3F3) 
 Simple heavy army tank factory buiding a tank with a crane. Square fence... Zenith view Directly overhead. Plumb view.
:quality(85)/https://cdn.frvr.ai/6612c73902cc0ae7da393a80.png%3F3) 
 an empty black popup UI. UI