1 const TERRAIN_SEPARATOR = "|";
2 const SEA_LEVEL = 20.0;
3 const HEIGHT_UNITS_PER_METRE = 92;
4 const MAP_BORDER_WIDTH = 3;
6 const g_DamageTypes = new DamageTypes();
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;
15 * Length of one tile of the terrain grid in metres.
16 * Useful to transform footprint sizes of templates to the coordinate system used by getMapSize.
18 const TERRAIN_TILE_SIZE = Engine.GetTerrainTileSize();
20 const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
23 * Default angle for buildings.
25 const BUILDING_ORIENTATION = -1/4 * Math.PI;
27 const g_CivData = deepfreeze(loadCivFiles(false));
29 function fractionToTiles(f)
31 return g_Map.size * f;
34 function tilesToFraction(t)
36 return t / g_Map.size;
39 function fractionToSize(f)
41 return getMapArea() * f;
44 function sizeToFraction(s)
46 return s / getMapArea();
49 function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
51 return min + (max - min) * (g_Map.size - minMapSize) / (maxMapSize - minMapSize);
55 * Retries the given function with those arguments as often as specified.
57 function retryPlacing(placeFunc, retryFactor, amount, getResult, behaveDeprecated = false)
59 let maxFail = amount * retryFactor;
65 while (good < amount && bad <= maxFail)
67 let result = placeFunc();
69 if (result !== undefined || behaveDeprecated)
78 return getResult ? results : good;
82 * Sets the x and z property of the given object (typically a Placer or Group) to a random point on the map.
83 * @param passableOnly - Should be true for entity placement and false for terrain or elevation operations.
85 function randomizeCoordinates(obj, passableOnly)
87 let border = passableOnly ? MAP_BORDER_WIDTH : 0;
88 if (g_MapSettings.CircularMap)
91 // Uniformly distributed on the disk
92 let halfMapSize = g_Map.size / 2 - border;
93 let r = halfMapSize * Math.sqrt(randFloat(0, 1));
94 let theta = randFloat(0, 2 * Math.PI);
95 obj.x = Math.floor(r * Math.cos(theta)) + halfMapSize;
96 obj.z = Math.floor(r * Math.sin(theta)) + halfMapSize;
100 // Rectangular coordinates
101 obj.x = randIntExclusive(border, g_Map.size - border);
102 obj.z = randIntExclusive(border, g_Map.size - border);
107 * Sets the x and z property of the given JS object (typically a Placer or Group) to a random point of the area.
109 function randomizeCoordinatesFromAreas(obj, areas)
111 let pt = pickRandom(pickRandom(areas).points);
116 // TODO this is a hack to simulate the old behaviour of those functions
117 // until all old maps are changed to use the correct version of these functions
118 function createObjectGroupsDeprecated(group, player, constraint, amount, retryFactor = 10)
120 return createObjectGroups(group, player, constraint, amount, retryFactor, true);
123 function createObjectGroupsByAreasDeprecated(group, player, constraint, amount, retryFactor, areas)
125 return createObjectGroupsByAreas(group, player, constraint, amount, retryFactor, areas, true);
129 * Attempts to place the given number of areas in random places of the map.
130 * Returns actually placed areas.
132 function createAreas(centeredPlacer, painter, constraint, amount, retryFactor = 10)
134 let placeFunc = function() {
135 randomizeCoordinates(centeredPlacer, false);
136 return createArea(centeredPlacer, painter, constraint);
139 return retryPlacing(placeFunc, retryFactor, amount, true, false);
143 * Attempts to place the given number of areas in random places of the given areas.
144 * Returns actually placed areas.
146 function createAreasInAreas(centeredPlacer, painter, constraint, amount, retryFactor, areas)
148 let placeFunc = function() {
149 randomizeCoordinatesFromAreas(centeredPlacer, areas);
150 return createArea(centeredPlacer, painter, constraint);
153 return retryPlacing(placeFunc, retryFactor, amount, true, false);
157 * Attempts to place the given number of groups in random places of the map.
158 * Returns the number of actually placed groups.
160 function createObjectGroups(group, player, constraint, amount, retryFactor = 10, behaveDeprecated = false)
162 let placeFunc = function() {
163 randomizeCoordinates(group, true);
164 return createObjectGroup(group, player, constraint);
167 return retryPlacing(placeFunc, retryFactor, amount, false, behaveDeprecated);
171 * Attempts to place the given number of groups in random places of the given areas.
172 * Returns the number of actually placed groups.
174 function createObjectGroupsByAreas(group, player, constraint, amount, retryFactor, areas, behaveDeprecated = false)
176 let placeFunc = function() {
177 randomizeCoordinatesFromAreas(group, areas);
178 return createObjectGroup(group, player, constraint);
181 return retryPlacing(placeFunc, retryFactor, amount, false, behaveDeprecated);
184 function createTerrain(terrain)
186 if (!(terrain instanceof Array))
187 return createSimpleTerrain(terrain);
189 return new RandomTerrain(terrain.map(t => createTerrain(t)));
192 function createSimpleTerrain(terrain)
194 if (typeof(terrain) != "string")
195 throw new Error("createSimpleTerrain expects string as input, received " + uneval(terrain));
197 // Split string by pipe | character, this allows specifying terrain + tree type in single string
198 let params = terrain.split(TERRAIN_SEPARATOR, 2);
200 if (params.length != 2)
201 return new SimpleTerrain(terrain);
203 return new SimpleTerrain(params[0], params[1]);
206 function placeObject(x, z, type, player, angle)
208 if (g_Map.validT(x, z))
209 g_Map.addObject(new Entity(type, player, x, z, angle));
212 function placeTerrain(x, z, terrainNames)
214 createTerrain(terrainNames).place(x, z);
217 function initTerrain(terrainNames)
219 let terrain = createTerrain(terrainNames);
221 for (let x = 0; x < getMapSize(); ++x)
222 for (let z = 0; z < getMapSize(); ++z)
226 function isCircularMap()
228 return !!g_MapSettings.CircularMap;
231 function getMapBaseHeight()
233 return g_MapSettings.BaseHeight;
236 function createTileClass()
238 return g_Map.createTileClass();
241 function getTileClass(id)
243 if (!g_Map.validClass(id))
246 return g_Map.tileClasses[id];
250 * Constructs a new Area shaped by the Placer meeting the Constraints and calls the Painters there.
251 * Supports both Centered and Non-Centered Placers.
253 function createArea(placer, painter, constraints)
255 let points = placer.place(new AndConstraint(constraints));
259 let area = g_Map.createArea(points);
261 if (painter instanceof Array)
262 painter = new MultiPainter(painter);
270 * @param mode is one of the HeightPlacer constants determining whether to exclude the min/max elevation.
272 function paintTerrainBasedOnHeight(minHeight, maxHeight, mode, terrain)
275 new HeightPlacer(mode, minHeight, maxHeight),
276 new TerrainPainter(terrain));
279 function paintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
282 new HeightPlacer(mode, minHeight, maxHeight),
283 new TileClassPainter(getTileClass(tileClass)));
286 function unPaintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
289 new HeightPlacer(mode, minHeight, maxHeight),
290 new TileClassUnPainter(getTileClass(tileClass)));
294 * Places the Entities of the given Group if they meet the Constraints
295 * and sets the given player as the owner.
297 function createObjectGroup(group, player, constraints)
299 return group.place(player, new AndConstraint(constraints));
302 function getMapSize()
307 function getMapArea()
309 return Math.square(g_Map.size);
312 function getMapCenter()
314 return deepfreeze(new Vector2D(g_Map.size / 2, g_Map.size / 2));
319 return !!g_MapSettings.Nomad;
322 function getNumPlayers()
324 return g_MapSettings.PlayerData.length - 1;
327 function getCivCode(playerID)
329 return g_MapSettings.PlayerData[playerID].Civ;
332 function areAllies(playerID1, playerID2)
335 g_MapSettings.PlayerData[playerID1].Team !== undefined &&
336 g_MapSettings.PlayerData[playerID2].Team !== undefined &&
337 g_MapSettings.PlayerData[playerID1].Team != -1 &&
338 g_MapSettings.PlayerData[playerID2].Team != -1 &&
339 g_MapSettings.PlayerData[playerID1].Team === g_MapSettings.PlayerData[playerID2].Team);
342 function getPlayerTeam(playerID)
344 if (g_MapSettings.PlayerData[playerID].Team === undefined)
347 return g_MapSettings.PlayerData[playerID].Team;
350 function getHeight(x, z)
352 return g_Map.getHeight(x, z);
355 function setHeight(x, z, height)
357 g_Map.setHeight(x, z, height);
360 function initHeight(height)
362 g_Map.initHeight(height);
366 * Utility functions for classes
370 * Add point to given class by id
372 function addToClass(x, z, id)
374 let tileClass = getTileClass(id);
376 if (tileClass !== null)
381 * Remove point from the given class by id
383 function removeFromClass(x, z, id)
385 let tileClass = getTileClass(id);
387 if (tileClass !== null)
388 tileClass.remove(x, z);
392 * Create a painter for the given class
394 function paintClass(id)
396 return new TileClassPainter(getTileClass(id));
400 * Create a painter for the given class
402 function unPaintClass(id)
404 return new TileClassUnPainter(getTileClass(id));
408 * Create an avoid constraint for the given classes by the given distances
410 function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
413 for (let i = 0; i < arguments.length/2; ++i)
414 ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]));
416 // Return single constraint
420 return new AndConstraint(ar);
424 * Create a stay constraint for the given classes by the given distances
426 function stayClasses(/*class1, dist1, class2, dist2, etc*/)
429 for (let i = 0; i < arguments.length/2; ++i)
430 ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]));
432 // Return single constraint
436 return new AndConstraint(ar);
440 * Create a border constraint for the given classes by the given distances
442 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
445 for (let i = 0; i < arguments.length/3; ++i)
446 ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]));
448 // Return single constraint
452 return new AndConstraint(ar);
456 * Checks if the given tile is in class "id"
458 function checkIfInClass(x, z, id)
460 let tileClass = getTileClass(id);
461 if (tileClass === null)
464 let members = tileClass.countMembersInRadius(x, z, 1);
465 if (members === null)
471 function getTerrainTexture(x, y)
473 return g_Map.getTexture(x, y);