2 const TWO_PI = 2 * Math.PI;
3 const TERRAIN_SEPARATOR = "|";
4 const SEA_LEVEL = 20.0;
5 const HEIGHT_UNITS_PER_METRE = 92;
6 const MAP_BORDER_WIDTH = 3;
7 const FALLBACK_CIV = "athen";
9 * Constants needed for heightmap_manipulation.js
11 const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
12 const MIN_HEIGHT = - SEA_LEVEL;
13 const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
14 // Default angle for buildings
15 const BUILDING_ORIENTATION = - PI / 4;
17 function fractionToTiles(f)
19 return g_Map.size * f;
22 function tilesToFraction(t)
24 return t / g_Map.size;
27 function fractionToSize(f)
29 return getMapArea() * f;
32 function sizeToFraction(s)
34 return s / getMapArea();
37 function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
39 return min + (max - min) * (g_Map.size - minMapSize) / (maxMapSize - minMapSize);
61 function lerp(a, b, t)
92 * Retries the given function with those arguments as often as specified.
94 function retryPlacing(placeFunc, placeArgs, retryFactor, amount, getResult, behaveDeprecated = false)
96 if (behaveDeprecated && !(placeArgs.placer instanceof SimpleGroup || placeArgs.placer instanceof RandomGroup))
97 warn("Deprecated version of createFoo should only be used for SimpleGroup and RandomGroup placers!");
99 let maxFail = amount * retryFactor;
105 while (good < amount && bad <= maxFail)
107 let result = placeFunc(placeArgs);
109 if (result !== undefined || behaveDeprecated)
113 results.push(result);
118 return getResult ? results : good;
122 * Helper function for randomly placing areas and groups on the map.
124 function randomizePlacerCoordinates(placer, halfMapSize)
126 if (!!g_MapSettings.CircularMap)
129 // Uniformly distributed on the disk
130 let r = halfMapSize * Math.sqrt(randFloat(0, 1));
131 let theta = randFloat(0, 2 * PI);
132 placer.x = Math.floor(r * Math.cos(theta)) + halfMapSize;
133 placer.z = Math.floor(r * Math.sin(theta)) + halfMapSize;
137 // Rectangular coordinates
138 placer.x = randIntExclusive(0, g_Map.size);
139 placer.z = randIntExclusive(0, g_Map.size);
144 * Helper function for randomly placing areas and groups in the given areas.
146 function randomizePlacerCoordinatesFromAreas(placer, areas)
148 let pt = pickRandom(pickRandom(areas).points);
153 // TODO this is a hack to simulate the old behaviour of those functions
154 // until all old maps are changed to use the correct version of these functions
155 function createObjectGroupsDeprecated(placer, player, constraint, amount, retryFactor = 10)
157 return createObjectGroups(placer, player, constraint, amount, retryFactor, true);
160 function createObjectGroupsByAreasDeprecated(placer, player, constraint, amount, retryFactor, areas)
162 return createObjectGroupsByAreas(placer, player, constraint, amount, retryFactor, areas, true);
166 * Attempts to place the given number of areas in random places of the map.
167 * Returns actually placed areas.
169 function createAreas(centeredPlacer, painter, constraint, amount, retryFactor = 10, behaveDeprecated = false)
171 let placeFunc = function (args) {
172 randomizePlacerCoordinates(args.placer, args.halfMapSize);
173 return createArea(args.placer, args.painter, args.constraint);
177 "placer": centeredPlacer,
179 "constraint": constraint,
180 "halfMapSize": g_Map.size / 2
183 return retryPlacing(placeFunc, args, retryFactor, amount, true, behaveDeprecated);
187 * Attempts to place the given number of areas in random places of the given areas.
188 * Returns actually placed areas.
190 function createAreasInAreas(centeredPlacer, painter, constraint, amount, retryFactor, areas, behaveDeprecated = false)
195 let placeFunc = function (args) {
196 randomizePlacerCoordinatesFromAreas(args.placer, args.areas);
197 return createArea(args.placer, args.painter, args.constraint);
201 "placer": centeredPlacer,
203 "constraint": constraint,
205 "halfMapSize": g_Map.size / 2
208 return retryPlacing(placeFunc, args, retryFactor, amount, true, behaveDeprecated);
212 * Attempts to place the given number of groups in random places of the map.
213 * Returns the number of actually placed groups.
215 function createObjectGroups(placer, player, constraint, amount, retryFactor = 10, behaveDeprecated = false)
217 let placeFunc = function (args) {
218 randomizePlacerCoordinates(args.placer, args.halfMapSize);
219 return createObjectGroup(args.placer, args.player, args.constraint);
225 "constraint": constraint,
226 "halfMapSize": getMapSize() / 2 - MAP_BORDER_WIDTH
229 return retryPlacing(placeFunc, args, retryFactor, amount, false, behaveDeprecated);
233 * Attempts to place the given number of groups in random places of the given areas.
234 * Returns the number of actually placed groups.
236 function createObjectGroupsByAreas(placer, player, constraint, amount, retryFactor, areas, behaveDeprecated = false)
241 let placeFunc = function (args) {
242 randomizePlacerCoordinatesFromAreas(args.placer, args.areas);
243 return createObjectGroup(args.placer, args.player, args.constraint);
249 "constraint": constraint,
253 return retryPlacing(placeFunc, args, retryFactor, amount, false, behaveDeprecated);
256 function createTerrain(terrain)
258 if (!(terrain instanceof Array))
259 return createSimpleTerrain(terrain);
261 return new RandomTerrain(terrain.map(t => createTerrain(t)));
264 function createSimpleTerrain(terrain)
266 if (typeof(terrain) != "string")
267 throw new Error("createSimpleTerrain expects string as input, received " + uneval(terrain));
269 // Split string by pipe | character, this allows specifying terrain + tree type in single string
270 let params = terrain.split(TERRAIN_SEPARATOR, 2);
272 if (params.length != 2)
273 return new SimpleTerrain(terrain);
275 return new SimpleTerrain(params[0], params[1]);
278 function placeObject(x, z, type, player, angle)
280 if (g_Map.validT(x, z))
281 g_Map.addObject(new Entity(type, player, x, z, angle));
284 function placeTerrain(x, z, terrainNames)
286 createTerrain(terrainNames).place(x, z);
289 function initTerrain(terrainNames)
291 let terrain = createTerrain(terrainNames);
293 for (let x = 0; x < getMapSize(); ++x)
294 for (let z = 0; z < getMapSize(); ++z)
298 function isCircularMap()
300 return !!g_MapSettings.CircularMap;
303 function getMapBaseHeight()
305 return g_MapSettings.BaseHeight;
308 function createTileClass()
310 return g_Map.createTileClass();
313 function getTileClass(id)
315 if (!g_Map.validClass(id))
318 return g_Map.tileClasses[id];
322 * Constructs a new Area shaped by the Placer meeting the Constraint and calls the Painters there.
323 * Supports both Centered and Non-Centered Placers.
325 function createArea(placer, painter, constraint)
328 constraint = new NullConstraint();
329 else if (constraint instanceof Array)
330 constraint = new AndConstraint(constraint);
332 let points = placer.place(constraint);
336 let area = g_Map.createArea(points);
338 if (painter instanceof Array)
339 painter = new MultiPainter(painter);
347 * Places the Entities of the given Group if they meet the Constraint
348 * and sets the given player as the owner.
350 function createObjectGroup(group, player, constraint)
353 constraint = new NullConstraint();
354 else if (constraint instanceof Array)
355 constraint = new AndConstraint(constraint);
357 return group.place(player, constraint);
360 function getMapSize()
365 function getMapArea()
367 return g_Map.size * g_Map.size;
370 function getNumPlayers()
372 return g_MapSettings.PlayerData.length - 1;
375 function getCivCode(player)
377 if (g_MapSettings.PlayerData[player+1].Civ)
378 return g_MapSettings.PlayerData[player+1].Civ;
380 warn("undefined civ specified for player " + (player + 1) + ", falling back to '" + FALLBACK_CIV + "'");
384 function areAllies(player1, player2)
386 if (g_MapSettings.PlayerData[player1+1].Team === undefined ||
387 g_MapSettings.PlayerData[player2+1].Team === undefined ||
388 g_MapSettings.PlayerData[player2+1].Team == -1 ||
389 g_MapSettings.PlayerData[player1+1].Team == -1)
392 return g_MapSettings.PlayerData[player1+1].Team === g_MapSettings.PlayerData[player2+1].Team;
395 function getPlayerTeam(player)
397 if (g_MapSettings.PlayerData[player+1].Team === undefined)
400 return g_MapSettings.PlayerData[player+1].Team;
403 function getHeight(x, z)
405 return g_Map.getHeight(x, z);
408 function setHeight(x, z, height)
410 g_Map.setHeight(x, z, height);
414 * Utility functions for classes
418 * Add point to given class by id
420 function addToClass(x, z, id)
422 let tileClass = getTileClass(id);
424 if (tileClass !== null)
429 * Remove point from the given class by id
431 function removeFromClass(x, z, id)
433 let tileClass = getTileClass(id);
435 if (tileClass !== null)
436 tileClass.remove(x, z);
440 * Create a painter for the given class
442 function paintClass(id)
444 return new TileClassPainter(getTileClass(id));
448 * Create a painter for the given class
450 function unPaintClass(id)
452 return new TileClassUnPainter(getTileClass(id));
456 * Create an avoid constraint for the given classes by the given distances
458 function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
461 for (let i = 0; i < arguments.length/2; ++i)
462 ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]));
464 // Return single constraint
468 return new AndConstraint(ar);
472 * Create a stay constraint for the given classes by the given distances
474 function stayClasses(/*class1, dist1, class2, dist2, etc*/)
477 for (let i = 0; i < arguments.length/2; ++i)
478 ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]));
480 // Return single constraint
484 return new AndConstraint(ar);
488 * Create a border constraint for the given classes by the given distances
490 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
493 for (let i = 0; i < arguments.length/3; ++i)
494 ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]));
496 // Return single constraint
500 return new AndConstraint(ar);
504 * Checks if the given tile is in class "id"
506 function checkIfInClass(x, z, id)
508 let tileClass = getTileClass(id);
509 if (tileClass === null)
512 let members = tileClass.countMembersInRadius(x, z, 1);
513 if (members === null)
519 function getTerrainTexture(x, y)
521 return g_Map.getTexture(x, y);