Nomad mode on all random map scripts (except Survival of the Fittest), fixes #3591.
[0ad.git] / binaries / data / mods / public / maps / random / rmgen / library.js
blobb8755de4548e965bcd1249d9fb193588406c00ad
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();
8 /**
9  * Constants needed for heightmap_manipulation.js
10  */
11 const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
12 const MIN_HEIGHT = - SEA_LEVEL;
14 /**
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.
17  */
18 const TERRAIN_TILE_SIZE = Engine.GetTerrainTileSize();
20 const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
22 /**
23  * Default angle for buildings.
24  */
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);
54 /**
55  * Retries the given function with those arguments as often as specified.
56  */
57 function retryPlacing(placeFunc, retryFactor, amount, getResult, behaveDeprecated = false)
59         let maxFail = amount * retryFactor;
61         let results = [];
62         let good = 0;
63         let bad = 0;
65         while (good < amount && bad <= maxFail)
66         {
67                 let result = placeFunc();
69                 if (result !== undefined || behaveDeprecated)
70                 {
71                         ++good;
72                         if (getResult)
73                                 results.push(result);
74                 }
75                 else
76                         ++bad;
77         }
78         return getResult ? results : good;
81 /**
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.
84  */
85 function randomizeCoordinates(obj, passableOnly)
87         let border = passableOnly ? MAP_BORDER_WIDTH : 0;
88         if (g_MapSettings.CircularMap)
89         {
90                 // Polar coordinates
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;
97         }
98         else
99         {
100                 // Rectangular coordinates
101                 obj.x = randIntExclusive(border, g_Map.size - border);
102                 obj.z = randIntExclusive(border, g_Map.size - border);
103         }
107  * Sets the x and z property of the given JS object (typically a Placer or Group) to a random point of the area.
108  */
109 function randomizeCoordinatesFromAreas(obj, areas)
111         let pt = pickRandom(pickRandom(areas).points);
112         obj.x = pt.x;
113         obj.z = pt.z;
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.
131  */
132 function createAreas(centeredPlacer, painter, constraint, amount, retryFactor = 10)
134         let placeFunc = function() {
135                 randomizeCoordinates(centeredPlacer, false);
136                 return createArea(centeredPlacer, painter, constraint);
137         };
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.
145  */
146 function createAreasInAreas(centeredPlacer, painter, constraint, amount, retryFactor, areas)
148         let placeFunc = function() {
149                 randomizeCoordinatesFromAreas(centeredPlacer, areas);
150                 return createArea(centeredPlacer, painter, constraint);
151         };
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.
159  */
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);
165         };
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.
173  */
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);
179         };
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)
223                         terrain.place(x, 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))
244                 return undefined;
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.
252  */
253 function createArea(placer, painter, constraints)
255         let points = placer.place(new AndConstraint(constraints));
256         if (!points)
257                 return undefined;
259         let area = g_Map.createArea(points);
261         if (painter instanceof Array)
262                 painter = new MultiPainter(painter);
264         painter.paint(area);
266         return area;
270  * @param mode is one of the HeightPlacer constants determining whether to exclude the min/max elevation.
271  */
272 function paintTerrainBasedOnHeight(minHeight, maxHeight, mode, terrain)
274         createArea(
275                 new HeightPlacer(mode, minHeight, maxHeight),
276                 new TerrainPainter(terrain));
279 function paintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
281         createArea(
282                 new HeightPlacer(mode, minHeight, maxHeight),
283                 new TileClassPainter(getTileClass(tileClass)));
286 function unPaintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
288         createArea(
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.
296  */
297 function createObjectGroup(group, player, constraints)
299         return group.place(player, new AndConstraint(constraints));
302 function getMapSize()
304         return g_Map.size;
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));
317 function isNomad()
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)
334         return (
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)
345                 return -1;
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
367  */
370  * Add point to given class by id
371  */
372 function addToClass(x, z, id)
374         let tileClass = getTileClass(id);
376         if (tileClass !== null)
377                 tileClass.add(x, z);
381  * Remove point from the given class by id
382  */
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
393  */
394 function paintClass(id)
396         return new TileClassPainter(getTileClass(id));
400  * Create a painter for the given class
401  */
402 function unPaintClass(id)
404         return new TileClassUnPainter(getTileClass(id));
408  * Create an avoid constraint for the given classes by the given distances
409  */
410 function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
412         let ar = [];
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
417         if (ar.length == 1)
418                 return ar[0];
420         return new AndConstraint(ar);
424  * Create a stay constraint for the given classes by the given distances
425  */
426 function stayClasses(/*class1, dist1, class2, dist2, etc*/)
428         let ar = [];
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
433         if (ar.length == 1)
434                 return ar[0];
436         return new AndConstraint(ar);
440  * Create a border constraint for the given classes by the given distances
441  */
442 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
444         let ar = [];
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
449         if (ar.length == 1)
450                 return ar[0];
452         return new AndConstraint(ar);
456  * Checks if the given tile is in class "id"
457  */
458 function checkIfInClass(x, z, id)
460         let tileClass = getTileClass(id);
461         if (tileClass === null)
462                 return 0;
464         let members = tileClass.countMembersInRadius(x, z, 1);
465         if (members === null)
466                 return 0;
468         return members;
471 function getTerrainTexture(x, y)
473         return g_Map.getTexture(x, y);