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.
5 Engine.LoadLibrary("rmgen/placer/centered");
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.
12 Engine.LoadLibrary("rmgen/placer/noncentered");
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.
18 Engine.LoadLibrary("rmgen/painter");
20 const TERRAIN_SEPARATOR = "|";
21 const SEA_LEVEL = 20.0;
22 const HEIGHT_UNITS_PER_METRE = 92;
25 * Constants needed for heightmap_manipulation.js
27 const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
28 const MIN_HEIGHT = - SEA_LEVEL;
30 const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
33 * Default angle for buildings.
35 const BUILDING_ORIENTATION = -1/4 * Math.PI;
37 const g_CivData = deepfreeze(loadCivFiles(false));
39 const g_ActorPrefix = "actor|";
42 * Sets whether setHeight operates on the center of a tile or on the vertices.
44 var TILE_CENTERED_HEIGHT_MAP = false;
46 function actorTemplate(templateName)
48 return g_ActorPrefix + templateName + ".xml";
51 function getObstructionSize(templateName, margin = 0)
53 const obstruction = Engine.GetTemplate(templateName).Obstruction;
55 const obstructionSize =
57 new Vector2D(obstruction.Static["@depth"], obstruction.Static["@width"]) :
58 // Used for gates, should consider the position too
59 obstruction.Obstructions ?
61 Object.keys(obstruction.Obstructions).reduce((depth, key) => Math.max(depth, +obstruction.Obstructions[key]["@depth"]), 0),
62 Object.keys(obstruction.Obstructions).reduce((width, key) => width + +obstruction.Obstructions[key]["@width"], 0)) :
65 return obstructionSize.div(TERRAIN_TILE_SIZE).add(new Vector2D(2, 2).mult(margin));
68 function fractionToTiles(f)
70 return g_MapSettings.Size * f;
73 function tilesToFraction(t)
75 return t / g_MapSettings.Size;
78 function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
80 return min + (max - min) * (g_MapSettings.Size - minMapSize) / (maxMapSize - minMapSize);
84 * Interpolate quadratic between (min, minMapArea) and (max, maxMapArea) with respect to the mapSize.
85 * Default values set on the area of tiny and giant map sizes according to the map shape (square or circular).
87 function scaleByMapArea(min, max, minMapArea = g_Map.getArea(128), maxMapArea = g_Map.getArea(512))
89 return min + (max - min) * (g_Map.getArea() - minMapArea) / (maxMapArea - minMapArea);
93 * Interpolate quadraticly between (0,0) and (base, baseArea) with respect to the map size.
94 * @param base - Value we should attain at baseArea.
95 * @param disallowedArea - Area deducted from the map area.
96 * @param baseArea - Area at which the base value should be attained. Defaults to the map area of a tiny map of current shape (square or circular).
98 function scaleByMapAreaAbsolute(base, disallowedArea = 0, baseArea = g_Map.getArea(128))
100 return scaleByMapArea(0, base, disallowedArea, baseArea + disallowedArea);
103 function randomPositionOnTile(tilePosition)
105 return Vector2D.add(tilePosition, new Vector2D(randFloat(0, 1), randFloat(0, 1)));
109 * Retries the given function with those arguments as often as specified.
111 function retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated = false)
113 const maxFail = amount * retryFactor;
118 while (results.length < amount && bad <= maxFail)
120 const result = placeFunc();
122 if (result !== undefined || behaveDeprecated)
123 results.push(result);
131 // TODO this is a hack to simulate the old behaviour of those functions
132 // until all old maps are changed to use the correct version of these functions
133 function createObjectGroupsDeprecated(group, player, constraints, amount, retryFactor = 10)
135 return createObjectGroups(group, player, constraints, amount, retryFactor, true);
138 function createObjectGroupsByAreasDeprecated(group, player, constraints, amount, retryFactor, areas)
140 return createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, true);
144 * Attempts to place the given number of areas in random places of the map.
145 * Returns actually placed areas.
147 function createAreas(centeredPlacer, painter, constraints, amount, retryFactor = 10)
149 const placeFunc = function() {
150 centeredPlacer.setCenterPosition(g_Map.randomCoordinate(false));
151 return createArea(centeredPlacer, painter, constraints);
154 return retryPlacing(placeFunc, retryFactor, amount, false);
158 * Attempts to place the given number of areas in random places of the given areas.
159 * Returns actually placed areas.
161 function createAreasInAreas(centeredPlacer, painter, constraints, amount, retryFactor, areas)
163 areas = areas.filter(area => area.getPoints().length);
165 log("createAreasInAreas: 'areas' was either empty or only contained empty areas thus returning an empty array.\n" + new Error().stack);
169 const placeFunc = function() {
170 centeredPlacer.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
171 return createArea(centeredPlacer, painter, constraints);
174 return retryPlacing(placeFunc, retryFactor, amount, false);
178 * Attempts to place the given number of groups in random places of the map.
179 * Returns the number of actually placed groups.
181 function createObjectGroups(group, player, constraints, amount, retryFactor = 10, behaveDeprecated = false)
183 const placeFunc = function() {
184 group.setCenterPosition(g_Map.randomCoordinate(true));
185 return createObjectGroup(group, player, constraints);
188 return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
192 * Attempts to place the given number of groups in random places of the given areas.
193 * Returns the number of actually placed groups.
195 function createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, behaveDeprecated = false)
197 areas = areas.filter(area => area.getPoints().length);
199 log("createObjectGroupsByAreas: 'areas' was either empty or only contained empty areas.\n" + new Error().stack);
203 const placeFunc = function() {
204 group.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
205 return createObjectGroup(group, player, constraints);
208 return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
211 function createTerrain(terrain)
213 return typeof terrain == "string" ?
214 new SimpleTerrain(...terrain.split(TERRAIN_SEPARATOR)) :
215 new RandomTerrain(terrain.map(t => createTerrain(t)));
219 * Constructs a new Area shaped by the Placer meeting the Constraints and calls the Painters there.
220 * Supports both Centered and Non-Centered Placers.
222 function createArea(placer, painters, constraints)
224 const points = placer.place(new AndConstraint(constraints));
228 const area = new Area(points);
230 new MultiPainter(painters).paint(area);
236 * @param mode is one of the HeightPlacer constants determining whether to exclude the min/max elevation.
238 function paintTerrainBasedOnHeight(minHeight, maxHeight, mode, terrain)
241 new HeightPlacer(mode, minHeight, maxHeight),
242 new TerrainPainter(terrain));
245 function paintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
248 new HeightPlacer(mode, minHeight, maxHeight),
249 new TileClassPainter(tileClass));
252 function unPaintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
255 new HeightPlacer(mode, minHeight, maxHeight),
256 new TileClassUnPainter(tileClass));
260 * Places the Entities of the given Group if they meet the Constraints
261 * and sets the given player as the owner.
263 function createObjectGroup(group, player, constraints)
265 return group.place(player, new AndConstraint(constraints));
269 * Create an avoid constraint for the given classes by the given distances
271 function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
274 for (let i = 0; i < arguments.length/2; ++i)
275 ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]));
277 // Return single constraint
281 return new AndConstraint(ar);
285 * Create a stay constraint for the given classes by the given distances
287 function stayClasses(/*class1, dist1, class2, dist2, etc*/)
290 for (let i = 0; i < arguments.length/2; ++i)
291 ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]));
293 // Return single constraint
297 return new AndConstraint(ar);
301 * Create a border constraint for the given classes by the given distances
303 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
306 for (let i = 0; i < arguments.length/3; ++i)
307 ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]));
309 // Return single constraint
313 return new AndConstraint(ar);
317 * Returns a subset of the given heightmap.
319 function extractHeightmap(heightmap, topLeft, size)
322 for (let x = 0; x < size; ++x)
324 result[x] = new Float32Array(size);
325 for (let y = 0; y < size; ++y)
326 result[x][y] = heightmap[x + topLeft.x][y + topLeft.y];
331 function convertHeightmap1Dto2D(heightmap)
334 const hmSize = Math.sqrt(heightmap.length);
335 for (let x = 0; x < hmSize; ++x)
337 result[x] = new Float32Array(hmSize);
338 for (let y = 0; y < hmSize; ++y)
339 result[x][y] = heightmap[y * hmSize + x];
344 function getDifficulties()
346 return Engine.ReadJSONFile("simulation/data/settings/trigger_difficulties.json").Data;
350 * Returns the numeric difficulty level the player chose.
352 function getDifficulty()
354 const level = g_MapSettings.TriggerDifficulty || 3;
355 return getDifficulties().find(difficulty => difficulty.Difficulty == level).Difficulty;