Line data Source code
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 6 : Engine.LoadLibrary("rmgen/placer/centered");
6 :
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 6 : Engine.LoadLibrary("rmgen/placer/noncentered");
13 :
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 6 : Engine.LoadLibrary("rmgen/painter");
19 :
20 6 : const TERRAIN_SEPARATOR = "|";
21 6 : const SEA_LEVEL = 20.0;
22 6 : const HEIGHT_UNITS_PER_METRE = 92;
23 :
24 : /**
25 : * Constants needed for heightmap_manipulation.js
26 : */
27 6 : const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE; // Engine limit, Roughly 700 meters
28 6 : const MIN_HEIGHT = - SEA_LEVEL;
29 :
30 6 : const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
31 :
32 : /**
33 : * Default angle for buildings.
34 : */
35 6 : const BUILDING_ORIENTATION = -1/4 * Math.PI;
36 :
37 6 : const g_CivData = deepfreeze(loadCivFiles(false));
38 :
39 6 : const g_ActorPrefix = "actor|";
40 :
41 : /**
42 : * Sets whether setHeight operates on the center of a tile or on the vertices.
43 : */
44 6 : var TILE_CENTERED_HEIGHT_MAP = false;
45 :
46 : function actorTemplate(templateName)
47 : {
48 0 : return g_ActorPrefix + templateName + ".xml";
49 : }
50 :
51 : function getObstructionSize(templateName, margin = 0)
52 : {
53 0 : let obstruction = Engine.GetTemplate(templateName).Obstruction;
54 :
55 : let obstructionSize =
56 0 : obstruction.Static ?
57 : new Vector2D(obstruction.Static["@depth"], obstruction.Static["@width"]) :
58 : // Used for gates, should consider the position too
59 : obstruction.Obstructions ?
60 : new Vector2D(
61 0 : Object.keys(obstruction.Obstructions).reduce((depth, key) => Math.max(depth, +obstruction.Obstructions[key]["@depth"]), 0),
62 0 : Object.keys(obstruction.Obstructions).reduce((width, key) => width + +obstruction.Obstructions[key]["@width"], 0)) :
63 : new Vector2D(0, 0);
64 :
65 0 : return obstructionSize.div(TERRAIN_TILE_SIZE).add(new Vector2D(2, 2).mult(margin));
66 : }
67 :
68 : function fractionToTiles(f)
69 : {
70 0 : return g_MapSettings.Size * f;
71 : }
72 :
73 : function tilesToFraction(t)
74 : {
75 0 : return t / g_MapSettings.Size;
76 : }
77 :
78 : function scaleByMapSize(min, max, minMapSize = 128, maxMapSize = 512)
79 : {
80 0 : return min + (max - min) * (g_MapSettings.Size - minMapSize) / (maxMapSize - minMapSize);
81 : }
82 :
83 : /**
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).
86 : */
87 : function scaleByMapArea(min, max, minMapArea = g_Map.getArea(128), maxMapArea = g_Map.getArea(256))
88 : {
89 0 : return min + (max - min) * (g_Map.getArea() - minMapArea) / (maxMapArea - minMapArea);
90 : }
91 :
92 : /**
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).
97 : */
98 : function scaleByMapAreaAbsolute(base, disallowedArea = 0, baseArea = g_Map.getArea(128))
99 : {
100 0 : return scaleByMapArea(0, base, disallowedArea, baseArea + disallowedArea);
101 : }
102 :
103 : function randomPositionOnTile(tilePosition)
104 : {
105 0 : return Vector2D.add(tilePosition, new Vector2D(randFloat(0, 1), randFloat(0, 1)));
106 : }
107 :
108 : /**
109 : * Retries the given function with those arguments as often as specified.
110 : */
111 : function retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated = false)
112 : {
113 0 : let maxFail = amount * retryFactor;
114 :
115 0 : let results = [];
116 0 : let bad = 0;
117 :
118 0 : while (results.length < amount && bad <= maxFail)
119 : {
120 0 : let result = placeFunc();
121 :
122 0 : if (result !== undefined || behaveDeprecated)
123 0 : results.push(result);
124 : else
125 0 : ++bad;
126 : }
127 :
128 0 : return results;
129 : }
130 :
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)
134 : {
135 0 : return createObjectGroups(group, player, constraints, amount, retryFactor, true);
136 : }
137 :
138 : function createObjectGroupsByAreasDeprecated(group, player, constraints, amount, retryFactor, areas)
139 : {
140 0 : return createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, true);
141 : }
142 :
143 : /**
144 : * Attempts to place the given number of areas in random places of the map.
145 : * Returns actually placed areas.
146 : */
147 : function createAreas(centeredPlacer, painter, constraints, amount, retryFactor = 10)
148 : {
149 0 : let placeFunc = function() {
150 0 : centeredPlacer.setCenterPosition(g_Map.randomCoordinate(false));
151 0 : return createArea(centeredPlacer, painter, constraints);
152 : };
153 :
154 0 : return retryPlacing(placeFunc, retryFactor, amount, false);
155 : }
156 :
157 : /**
158 : * Attempts to place the given number of areas in random places of the given areas.
159 : * Returns actually placed areas.
160 : */
161 : function createAreasInAreas(centeredPlacer, painter, constraints, amount, retryFactor, areas)
162 : {
163 0 : areas = areas.filter(area => area.getPoints().length);
164 0 : if (!areas.length) {
165 0 : log("createAreasInAreas: 'areas' was either empty or only contained empty areas thus returning an empty array.\n" + new Error().stack)
166 0 : return [];
167 : }
168 :
169 0 : let placeFunc = function() {
170 0 : centeredPlacer.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
171 0 : return createArea(centeredPlacer, painter, constraints);
172 : };
173 :
174 0 : return retryPlacing(placeFunc, retryFactor, amount, false);
175 : }
176 :
177 : /**
178 : * Attempts to place the given number of groups in random places of the map.
179 : * Returns the number of actually placed groups.
180 : */
181 : function createObjectGroups(group, player, constraints, amount, retryFactor = 10, behaveDeprecated = false)
182 : {
183 0 : let placeFunc = function() {
184 0 : group.setCenterPosition(g_Map.randomCoordinate(true));
185 0 : return createObjectGroup(group, player, constraints);
186 : };
187 :
188 0 : return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
189 : }
190 :
191 : /**
192 : * Attempts to place the given number of groups in random places of the given areas.
193 : * Returns the number of actually placed groups.
194 : */
195 : function createObjectGroupsByAreas(group, player, constraints, amount, retryFactor, areas, behaveDeprecated = false)
196 : {
197 0 : areas = areas.filter(area => area.getPoints().length);
198 0 : if (!areas.length) {
199 0 : log("createObjectGroupsByAreas: 'areas' was either empty or only contained empty areas.\n" + new Error().stack)
200 0 : return [];
201 : }
202 :
203 0 : let placeFunc = function() {
204 0 : group.setCenterPosition(pickRandom(pickRandom(areas).getPoints()));
205 0 : return createObjectGroup(group, player, constraints);
206 : };
207 :
208 0 : return retryPlacing(placeFunc, retryFactor, amount, behaveDeprecated);
209 : }
210 :
211 : function createTerrain(terrain)
212 : {
213 2 : return typeof terrain == "string" ?
214 : new SimpleTerrain(...terrain.split(TERRAIN_SEPARATOR)) :
215 0 : new RandomTerrain(terrain.map(t => createTerrain(t)));
216 : }
217 :
218 : /**
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.
221 : */
222 : function createArea(placer, painters, constraints)
223 : {
224 10 : let points = placer.place(new AndConstraint(constraints));
225 10 : if (!points)
226 0 : return undefined;
227 :
228 10 : let area = new Area(points);
229 :
230 10 : new MultiPainter(painters).paint(area);
231 :
232 10 : return area;
233 : }
234 :
235 : /**
236 : * @param mode is one of the HeightPlacer constants determining whether to exclude the min/max elevation.
237 : */
238 : function paintTerrainBasedOnHeight(minHeight, maxHeight, mode, terrain)
239 : {
240 0 : return createArea(
241 : new HeightPlacer(mode, minHeight, maxHeight),
242 : new TerrainPainter(terrain));
243 : }
244 :
245 : function paintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
246 : {
247 0 : return createArea(
248 : new HeightPlacer(mode, minHeight, maxHeight),
249 : new TileClassPainter(tileClass));
250 : }
251 :
252 : function unPaintTileClassBasedOnHeight(minHeight, maxHeight, mode, tileClass)
253 : {
254 0 : return createArea(
255 : new HeightPlacer(mode, minHeight, maxHeight),
256 : new TileClassUnPainter(tileClass));
257 : }
258 :
259 : /**
260 : * Places the Entities of the given Group if they meet the Constraints
261 : * and sets the given player as the owner.
262 : */
263 : function createObjectGroup(group, player, constraints)
264 : {
265 0 : return group.place(player, new AndConstraint(constraints));
266 : }
267 :
268 : /**
269 : * Create an avoid constraint for the given classes by the given distances
270 : */
271 : function avoidClasses(/*class1, dist1, class2, dist2, etc*/)
272 : {
273 0 : let ar = [];
274 0 : for (let i = 0; i < arguments.length/2; ++i)
275 0 : ar.push(new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]));
276 :
277 : // Return single constraint
278 0 : if (ar.length == 1)
279 0 : return ar[0];
280 :
281 0 : return new AndConstraint(ar);
282 : }
283 :
284 : /**
285 : * Create a stay constraint for the given classes by the given distances
286 : */
287 : function stayClasses(/*class1, dist1, class2, dist2, etc*/)
288 : {
289 0 : let ar = [];
290 0 : for (let i = 0; i < arguments.length/2; ++i)
291 0 : ar.push(new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]));
292 :
293 : // Return single constraint
294 0 : if (ar.length == 1)
295 0 : return ar[0];
296 :
297 0 : return new AndConstraint(ar);
298 : }
299 :
300 : /**
301 : * Create a border constraint for the given classes by the given distances
302 : */
303 : function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/)
304 : {
305 0 : let ar = [];
306 0 : for (let i = 0; i < arguments.length/3; ++i)
307 0 : ar.push(new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]));
308 :
309 : // Return single constraint
310 0 : if (ar.length == 1)
311 0 : return ar[0];
312 :
313 0 : return new AndConstraint(ar);
314 : }
315 :
316 : /**
317 : * Returns a subset of the given heightmap.
318 : */
319 : function extractHeightmap(heightmap, topLeft, size)
320 : {
321 0 : let result = [];
322 0 : for (let x = 0; x < size; ++x)
323 : {
324 0 : result[x] = new Float32Array(size);
325 0 : for (let y = 0; y < size; ++y)
326 0 : result[x][y] = heightmap[x + topLeft.x][y + topLeft.y];
327 : }
328 0 : return result;
329 : }
330 :
331 : function convertHeightmap1Dto2D(heightmap)
332 : {
333 0 : let result = [];
334 0 : let hmSize = Math.sqrt(heightmap.length);
335 0 : for (let x = 0; x < hmSize; ++x)
336 : {
337 0 : result[x] = new Float32Array(hmSize);
338 0 : for (let y = 0; y < hmSize; ++y)
339 0 : result[x][y] = heightmap[y * hmSize + x];
340 : }
341 0 : return result;
342 : }
343 :
344 : function getDifficulties()
345 : {
346 0 : return Engine.ReadJSONFile("simulation/data/settings/trigger_difficulties.json").Data;
347 : }
348 :
349 : /**
350 : * Returns the numeric difficulty level the player chose.
351 : */
352 : function getDifficulty()
353 : {
354 0 : let level = g_MapSettings.TriggerDifficulty || 3;
355 0 : return getDifficulties().find(difficulty => difficulty.Difficulty == level).Difficulty;
356 : }
|