Rename some constraint arguments to constraints to reflect that one can pass an array...
[0ad.git] / binaries / data / mods / public / maps / random / rmgen / library.js
blob4603fea605feaaea40bfaa641500d8e5733ef1c8
1 /**
2  * A Centered Placer generates a shape (array of Vector2D points) around a variable center location satisfying a Constraint.
3  * The center can be modified externally using setCenterPosition, typically called by createAreas.
4  */
5 Engine.LoadLibrary("rmgen/placer/centered");
7 /**
8  * A Non-Centered Placer generates a shape (array of Vector2D points) at a fixed location meeting a Constraint and
9  * is typically called by createArea.
10  * Since this type of Placer has no x and z property, its location cannot be randomized using createAreas.
11  */
12 Engine.LoadLibrary("rmgen/placer/noncentered");
14 /**
15  * A Painter modifies an arbitrary feature in a given Area, for instance terrain textures, elevation or calling other painters on that Area.
16  * Typically the area is determined by a Placer called from createArea or createAreas.
17  */
18 Engine.LoadLibrary("rmgen/painter");
20 const TERRAIN_SEPARATOR = "|";
21 const SEA_LEVEL = 20.0;
22 const HEIGHT_UNITS_PER_METRE = 92;
24 /**
25  * Number of impassable, unexplorable tiles at the map border.
26  */
27 const MAP_BORDER_WIDTH = 3;
29 const g_DamageTypes = new DamageTypes();
31 /**
32  * Constants needed for heightmap_manipulation.js
33  */
34 const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
35 const MIN_HEIGHT = - SEA_LEVEL;
37 /**
38  * Length of one tile of the terrain grid in metres.
39  * Useful to transform footprint sizes of templates to the coordinate system used by getMapSize.
40  */
41 const TERRAIN_TILE_SIZE = Engine.GetTerrainTileSize();
43 const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
45 /**
46  * Default angle for buildings.
47  */
48 const BUILDING_ORIENTATION = -1/4 * Math.PI;
50 const g_CivData = deepfreeze(loadCivFiles(false));
52 const g_ActorPrefix = "actor|";
54 /**
55  * Sets whether setHeight operates on the center of a tile or on the vertices.
56  */
57 var TILE_CENTERED_HEIGHT_MAP = false;
59 function actorTemplate(templateName)
61         return g_ActorPrefix + templateName + ".xml";
64 function fractionToTiles(f)
66         return g_MapSettings.Size * f;
69 function tilesToFraction(t)
71         return t / g_MapSettings.Size;
74 function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
76         return min + (max - min) * (g_MapSettings.Size - minMapSize) / (maxMapSize - minMapSize);
79 function randomPositionOnTile(tilePosition)
81         return Vector2D.add(tilePosition, new Vector2D(randFloat(0, 1), randFloat(0, 1)));
84 /**
85  * Retries the given function with those arguments as often as specified.
86  */
87 function retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated = false)
89         let maxFail = amount * retryFactor;
91         let results = [];
92         let bad = 0;
94         while (results.length < amount && bad <= maxFail)
95         {
96                 let result = placeFunc();
98                 if (result !== undefined || behaveDeprecated)
99                         results.push(result);
100                 else
101                         ++bad;
102         }
104         return results;
107 // TODO this is a hack to simulate the old behaviour of those functions
108 // until all old maps are changed to use the correct version of these functions
109 function createObjectGroupsDeprecated(group, player, constraints, amount, retryFactor = 10)
111         return createObjectGroups(group, player, constraints, amount, retryFactor, true);
114 function createObjectGroupsByAreasDeprecated(group, player, constraints, amount, retryFactor, areas)
116         return createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, true);
120  * Attempts to place the given number of areas in random places of the map.
121  * Returns actually placed areas.
122  */
123 function createAreas(centeredPlacer, painter, constraints, amount, retryFactor = 10)
125         let placeFunc = function() {
126                 centeredPlacer.setCenterPosition(g_Map.randomCoordinate(false));
127                 return createArea(centeredPlacer, painter, constraints);
128         };
130         return retryPlacing(placeFunc, retryFactor, amount, false);
134  * Attempts to place the given number of areas in random places of the given areas.
135  * Returns actually placed areas.
136  */
137 function createAreasInAreas(centeredPlacer, painter, constraints, amount, retryFactor, areas)
139         let placeFunc = function() {
140                 centeredPlacer.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
141                 return createArea(centeredPlacer, painter, constraints);
142         };
144         return retryPlacing(placeFunc, retryFactor, amount, false);
148  * Attempts to place the given number of groups in random places of the map.
149  * Returns the number of actually placed groups.
150  */
151 function createObjectGroups(group, player, constraints, amount, retryFactor = 10, behaveDeprecated = false)
153         let placeFunc = function() {
154                 group.setCenterPosition(g_Map.randomCoordinate(true));
155                 return createObjectGroup(group, player, constraints);
156         };
158         return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
162  * Attempts to place the given number of groups in random places of the given areas.
163  * Returns the number of actually placed groups.
164  */
165 function createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, behaveDeprecated = false)
167         let placeFunc = function() {
168                 group.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
169                 return createObjectGroup(group, player, constraints);
170         };
172         return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
175 function createTerrain(terrain)
177         return typeof terrain == "string" ?
178                 new SimpleTerrain(...terrain.split(TERRAIN_SEPARATOR)) :
179                 new RandomTerrain(terrain.map(t => createTerrain(t)));
183  * Constructs a new Area shaped by the Placer meeting the Constraints and calls the Painters there.
184  * Supports both Centered and Non-Centered Placers.
185  */
186 function createArea(placer, painters, constraints)
188         let points = placer.place(new AndConstraint(constraints));
189         if (!points)
190                 return undefined;
192         let area = new Area(points);
194         new MultiPainter(painters).paint(area);
196         return area;
200  * @param mode is one of the HeightPlacer constants determining whether to exclude the min/max elevation.
201  */
202 function paintTerrainBasedOnHeight(minHeight, maxHeight, mode, terrain)
204         createArea(
205                 new HeightPlacer(mode, minHeight, maxHeight),
206                 new TerrainPainter(terrain));
209 function paintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
211         createArea(
212                 new HeightPlacer(mode, minHeight, maxHeight),
213                 new TileClassPainter(tileClass));
216 function unPaintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
218         createArea(
219                 new HeightPlacer(mode, minHeight, maxHeight),
220                 new TileClassUnPainter(tileClass));
224  * Places the Entities of the given Group if they meet the Constraints
225  * and sets the given player as the owner.
226  */
227 function createObjectGroup(group, player, constraints)
229         return group.place(player, new AndConstraint(constraints));
233  * Create an avoid constraint for the given classes by the given distances
234  */
235 function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
237         let ar = [];
238         for (let i = 0; i < arguments.length/2; ++i)
239                 ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]));
241         // Return single constraint
242         if (ar.length == 1)
243                 return ar[0];
245         return new AndConstraint(ar);
249  * Create a stay constraint for the given classes by the given distances
250  */
251 function stayClasses(/*class1, dist1, class2, dist2, etc*/)
253         let ar = [];
254         for (let i = 0; i < arguments.length/2; ++i)
255                 ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]));
257         // Return single constraint
258         if (ar.length == 1)
259                 return ar[0];
261         return new AndConstraint(ar);
265  * Create a border constraint for the given classes by the given distances
266  */
267 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
269         let ar = [];
270         for (let i = 0; i < arguments.length/3; ++i)
271                 ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]));
273         // Return single constraint
274         if (ar.length == 1)
275                 return ar[0];
277         return new AndConstraint(ar);
281  * Returns a subset of the given heightmap.
282  */
283 function extractHeightmap(heightmap, topLeft, size)
285         let result = [];
286         for (let x = 0; x < size; ++x)
287         {
288                 result[x] = new Float32Array(size);
289                 for (let y = 0; y < size; ++y)
290                         result[x][y] = heightmap[x + topLeft.x][y + topLeft.y];
291         }
292         return result;
295 function convertHeightmap1Dto2D(heightmap)
297         let result = [];
298         let hmSize = Math.sqrt(heightmap.length);
299         for (let x = 0; x < hmSize; ++x)
300         {
301                 result[x] = new Float32Array(hmSize);
302                 for (let y = 0; y < hmSize; ++y)
303                         result[x][y] = heightmap[y * hmSize + x];
304         }
305         return result;