Unify Caledonian Meadows and Wild Lake player location duplication from rP19704 ...
[0ad.git] / binaries / data / mods / public / maps / random / rmgen / library.js
bloba44f0254fab19c6292ee6ef4e06c1202bd0a8388
1 const PI = Math.PI;
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";
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;
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);
42 function cos(x)
44         return Math.cos(x);
47 function sin(x)
49         return Math.sin(x);
52 function abs(x) {
53         return Math.abs(x);
56 function round(x)
58         return Math.round(x);
61 function lerp(a, b, t)
63         return a + (b-a) * t;
66 function sqrt(x)
68         return Math.sqrt(x);
71 function ceil(x)
73         return Math.ceil(x);
76 function floor(x)
78         return Math.floor(x);
81 function max(a, b)
83         return a > b ? a : b;
86 function min(a, b)
88         return a < b ? a : b;
91 /**
92  * Retries the given function with those arguments as often as specified.
93  */
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;
101         let results = [];
102         let good = 0;
103         let bad = 0;
105         while (good < amount && bad <= maxFail)
106         {
107                 let result = placeFunc(placeArgs);
109                 if (result !== undefined || behaveDeprecated)
110                 {
111                         ++good;
112                         if (getResult)
113                                 results.push(result);
114                 }
115                 else
116                         ++bad;
117         }
118         return getResult ? results : good;
122  * Helper function for randomly placing areas and groups on the map.
123  */
124 function randomizePlacerCoordinates(placer, halfMapSize)
126         if (!!g_MapSettings.CircularMap)
127         {
128                 // Polar coordinates
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;
134         }
135         else
136         {
137                 // Rectangular coordinates
138                 placer.x = randIntExclusive(0, g_Map.size);
139                 placer.z = randIntExclusive(0, g_Map.size);
140         }
144  * Helper function for randomly placing areas and groups in the given areas.
145  */
146 function randomizePlacerCoordinatesFromAreas(placer, areas)
148         let pt = pickRandom(pickRandom(areas).points);
149         placer.x = pt.x;
150         placer.z = pt.z;
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.
168  */
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);
174         };
176         let args = {
177                 "placer": centeredPlacer,
178                 "painter": painter,
179                 "constraint": constraint,
180                 "halfMapSize": g_Map.size / 2
181         };
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.
189  */
190 function createAreasInAreas(centeredPlacer, painter, constraint, amount, retryFactor, areas, behaveDeprecated = false)
192         if (!areas.length)
193                 return [];
195         let placeFunc = function (args) {
196                 randomizePlacerCoordinatesFromAreas(args.placer, args.areas);
197                 return createArea(args.placer, args.painter, args.constraint);
198         };
200         let args = {
201                 "placer": centeredPlacer,
202                 "painter": painter,
203                 "constraint": constraint,
204                 "areas": areas,
205                 "halfMapSize": g_Map.size / 2
206         };
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.
214  */
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);
220         };
222         let args = {
223                 "placer": placer,
224                 "player": player,
225                 "constraint": constraint,
226                 "halfMapSize": getMapSize() / 2 - MAP_BORDER_WIDTH
227         };
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.
235  */
236 function createObjectGroupsByAreas(placer, player, constraint, amount, retryFactor, areas, behaveDeprecated = false)
238         if (!areas.length)
239                 return 0;
241         let placeFunc = function (args) {
242                 randomizePlacerCoordinatesFromAreas(args.placer, args.areas);
243                 return createObjectGroup(args.placer, args.player, args.constraint);
244         };
246         let args = {
247                 "placer": placer,
248                 "player": player,
249                 "constraint": constraint,
250                 "areas": areas
251         };
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)
295                         terrain.place(x, 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))
316                 return undefined;
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.
324  */
325 function createArea(placer, painter, constraint)
327         if (!constraint)
328                 constraint = new NullConstraint();
329         else if (constraint instanceof Array)
330                 constraint = new AndConstraint(constraint);
332         let points = placer.place(constraint);
333         if (!points)
334                 return undefined;
336         let area = g_Map.createArea(points);
338         if (painter instanceof Array)
339                 painter = new MultiPainter(painter);
341         painter.paint(area);
343         return area;
347  * Places the Entities of the given Group if they meet the Constraint
348  * and sets the given player as the owner.
349  */
350 function createObjectGroup(group, player, constraint)
352         if (!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()
362         return g_Map.size;
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 + "'");
381         return 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)
390                 return false;
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)
398                 return -1;
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
415  */
418  * Add point to given class by id
419  */
420 function addToClass(x, z, id)
422         let tileClass = getTileClass(id);
424         if (tileClass !== null)
425                 tileClass.add(x, z);
429  * Remove point from the given class by id
430  */
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
441  */
442 function paintClass(id)
444         return new TileClassPainter(getTileClass(id));
448  * Create a painter for the given class
449  */
450 function unPaintClass(id)
452         return new TileClassUnPainter(getTileClass(id));
456  * Create an avoid constraint for the given classes by the given distances
457  */
458 function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
460         let ar = [];
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
465         if (ar.length == 1)
466                 return ar[0];
468         return new AndConstraint(ar);
472  * Create a stay constraint for the given classes by the given distances
473  */
474 function stayClasses(/*class1, dist1, class2, dist2, etc*/)
476         let ar = [];
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
481         if (ar.length == 1)
482                 return ar[0];
484         return new AndConstraint(ar);
488  * Create a border constraint for the given classes by the given distances
489  */
490 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
492         let ar = [];
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
497         if (ar.length == 1)
498                 return ar[0];
500         return new AndConstraint(ar);
504  * Checks if the given tile is in class "id"
505  */
506 function checkIfInClass(x, z, id)
508         let tileClass = getTileClass(id);
509         if (tileClass === null)
510                 return 0;
512         let members = tileClass.countMembersInRadius(x, z, 1);
513         if (members === null)
514                 return 0;
516         return members;
519 function getTerrainTexture(x, y)
521         return g_Map.getTexture(x, y);