Nomad mode on all random map scripts (except Survival of the Fittest), fixes #3591.
[0ad.git] / binaries / data / mods / public / maps / random / wild_lake.js
blob9a2083128ea31413c5f0fd4aeb44986b3698eb84
1 Engine.LoadLibrary("rmgen");
2 Engine.LoadLibrary("rmbiome");
3 Engine.LoadLibrary("heightmap");
5 InitMap();
7 /**
8  * getArray - To ensure a terrain texture is contained within an array
9  */
10 function getArray(stringOrArrayOfStrings)
12         if (typeof stringOrArrayOfStrings == "string")
13                 return [stringOrArrayOfStrings];
14         return stringOrArrayOfStrings;
17 setSelectedBiome();
19 // Terrain, entities and actors
20 let wildLakeBiome = [
21         // 0 Deep water
22         {
23                 "texture": getArray(g_Terrains.water),
24                 "actor": [[g_Gaia.fish], 0.01],
25                 "textureHS": getArray(g_Terrains.water),
26                 "actorHS": [[g_Gaia.fish], 0.03]
27         },
28         // 1 Shallow water
29         {
30                 "texture": getArray(g_Terrains.water),
31                 "actor": [[g_Decoratives.lillies, g_Decoratives.reeds], 0.3],
32                 "textureHS": getArray(g_Terrains.water),
33                 "actorHS": [[g_Decoratives.lillies], 0.1]
34         },
35         // 2 Shore
36         {
37                 "texture": getArray(g_Terrains.shore),
38                 "actor": [
39                         [
40                                 g_Gaia.tree1, g_Gaia.tree1,
41                                 g_Gaia.tree2, g_Gaia.tree2,
42                                 g_Gaia.mainHuntableAnimal,
43                                 g_Decoratives.grass, g_Decoratives.grass,
44                                 g_Decoratives.rockMedium, g_Decoratives.rockMedium,
45                                 g_Decoratives.bushMedium, g_Decoratives.bushMedium
46                         ],
47                         0.3
48                 ],
49                 "textureHS": getArray(g_Terrains.cliff),
50                 "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1]
51         },
52         // 3 Low ground
53         {
54                 "texture": getArray(g_Terrains.tier1Terrain),
55                 "actor": [
56                         [
57                                 g_Decoratives.grass,
58                                 g_Decoratives.grassShort,
59                                 g_Decoratives.rockLarge,
60                                 g_Decoratives.rockMedium,
61                                 g_Decoratives.bushMedium,
62                                 g_Decoratives.bushSmall
63                         ],
64                         0.2
65                 ],
66                 "textureHS": getArray(g_Terrains.cliff),
67                 "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1]
68         },
69         // 4 Mid ground. Player and path height
70         {
71                 "texture": getArray(g_Terrains.mainTerrain),
72                 "actor": [
73                         [
74                                 g_Decoratives.grass,
75                                 g_Decoratives.grassShort,
76                                 g_Decoratives.rockLarge,
77                                 g_Decoratives.rockMedium,
78                                 g_Decoratives.bushMedium,
79                                 g_Decoratives.bushSmall
80                         ],
81                         0.2
82                 ],
83                 "textureHS": getArray(g_Terrains.cliff),
84                 "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1]
85         },
86         // 5 High ground
87         {
88                 "texture": getArray(g_Terrains.tier2Terrain),
89                 "actor": [
90                         [
91                                 g_Decoratives.grass,
92                                 g_Decoratives.grassShort,
93                                 g_Decoratives.rockLarge,
94                                 g_Decoratives.rockMedium,
95                                 g_Decoratives.bushMedium,
96                                 g_Decoratives.bushSmall
97                         ],
98                         0.2
99                 ],
100                 "textureHS": getArray(g_Terrains.cliff),
101                 "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1]
102         },
103         // 6 Lower hilltop forest border
104         {
105                 "texture": getArray(g_Terrains.dirt),
106                 "actor": [
107                         [
108                                 g_Gaia.tree1,
109                                 g_Gaia.tree3,
110                                 g_Gaia.fruitBush,
111                                 g_Gaia.secondaryHuntableAnimal,
112                                 g_Decoratives.grass,
113                                 g_Decoratives.rockMedium,
114                                 g_Decoratives.bushMedium
115                         ],
116                         0.3
117                 ],
118                 "textureHS": getArray(g_Terrains.cliff),
119                 "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1]
120         },
121         // 7 Hilltop forest
122         {
123                 "texture": getArray(g_Terrains.forestFloor1),
124                 "actor": [
125                         [
126                                 g_Gaia.tree1,
127                                 g_Gaia.tree2,
128                                 g_Gaia.tree3,
129                                 g_Gaia.tree4,
130                                 g_Gaia.tree5,
131                                 g_Decoratives.tree,
132                                 g_Decoratives.grass,
133                                 g_Decoratives.rockMedium,
134                                 g_Decoratives.bushMedium
135                         ],
136                         0.5
137                 ],
138                 "textureHS": getArray(g_Terrains.cliff),
139                 "actorHS": [[g_Decoratives.grassShort, g_Decoratives.rockMedium, g_Decoratives.bushSmall], 0.1]
140         }
143 var mercenaryCampGuards = {
144         "temperate": [
145                 { "Template" : "structures/merc_camp_egyptian" },
146                 { "Template" : "units/mace_infantry_javelinist_b", "Count" : 4 },
147                 { "Template" : "units/mace_cavalry_spearman_e", "Count" : 3 },
148                 { "Template" : "units/mace_infantry_archer_a", "Count" : 4 },
149                 { "Template" : "units/mace_champion_infantry_a", "Count" : 3 }
150         ],
151         "snowy": [
152                 { "Template" : "structures/ptol_mercenary_camp" },
153                 { "Template" : "units/brit_infantry_javelinist_b", "Count" : 4 },
154                 { "Template" : "units/brit_cavalry_swordsman_e", "Count" : 3 },
155                 { "Template" : "units/brit_infantry_slinger_a", "Count" : 4 },
156                 { "Template" : "units/brit_champion_infantry", "Count" : 3 }
157         ],
158         "desert": [
159                 { "Template" : "structures/ptol_mercenary_camp" },
160                 { "Template" : "units/pers_infantry_javelinist_b", "Count" : 4 },
161                 { "Template" : "units/pers_cavalry_swordsman_e", "Count" : 3 },
162                 { "Template" : "units/pers_infantry_archer_a", "Count" : 4 },
163                 { "Template" : "units/pers_champion_infantry", "Count" : 3 }
164         ],
165         "alpine": [
166                 { "Template" : "structures/ptol_mercenary_camp" },
167                 { "Template" : "units/rome_infantry_swordsman_b", "Count" : 4 },
168                 { "Template" : "units/rome_cavalry_spearman_e", "Count" : 3 },
169                 { "Template" : "units/rome_infantry_javelinist_a", "Count" : 4 },
170                 { "Template" : "units/rome_champion_infantry", "Count" : 3 }
171         ],
172         "mediterranean": [
173                 { "Template" : "structures/merc_camp_egyptian" },
174                 { "Template" : "units/iber_infantry_javelinist_b", "Count" : 4 },
175                 { "Template" : "units/iber_cavalry_spearman_e", "Count" : 3 },
176                 { "Template" : "units/iber_infantry_slinger_a", "Count" : 4 },
177                 { "Template" : "units/iber_champion_infantry", "Count" : 3 }
178         ],
179         "savanna": [
180                 { "Template" : "structures/merc_camp_egyptian" },
181                 { "Template" : "units/sele_infantry_javelinist_b", "Count" : 4 },
182                 { "Template" : "units/sele_cavalry_spearman_merc_e", "Count" : 3 },
183                 { "Template" : "units/sele_infantry_spearman_a", "Count" : 4 },
184                 { "Template" : "units/sele_champion_infantry_swordsman", "Count" : 3 }
185         ],
186         "tropic": [
187                 { "Template" : "structures/merc_camp_egyptian" },
188                 { "Template" : "units/ptol_infantry_javelinist_b", "Count" : 4 },
189                 { "Template" : "units/ptol_cavalry_archer_e", "Count" : 3 },
190                 { "Template" : "units/ptol_infantry_slinger_a", "Count" : 4 },
191                 { "Template" : "units/ptol_champion_infantry_pikeman", "Count" : 3 }
192         ],
193         "autumn": [
194                 { "Template" : "structures/ptol_mercenary_camp" },
195                 { "Template" : "units/gaul_infantry_javelinist_b", "Count" : 4 },
196                 { "Template" : "units/gaul_cavalry_swordsman_e", "Count" : 3 },
197                 { "Template" : "units/gaul_infantry_slinger_a", "Count" : 4 },
198                 { "Template" : "units/gaul_champion_infantry", "Count" : 3 }
199         ]
203  * Resource spots and other points of interest
204  */
206 // Mines
207 function placeMine(point, centerEntity,
208         decorativeActors = [
209                 g_Decoratives.grass, g_Decoratives.grassShort,
210                 g_Decoratives.rockLarge, g_Decoratives.rockMedium,
211                 g_Decoratives.bushMedium, g_Decoratives.bushSmall
212         ]
215         placeObject(point.x, point.y, centerEntity, 0, randFloat(0, 2 * Math.PI));
216         let quantity = randIntInclusive(11, 23);
217         let dAngle = 2 * Math.PI / quantity;
218         for (let i = 0; i < quantity; ++i)
219         {
220                 let angle = dAngle * randFloat(i, i + 1);
221                 let dist = randFloat(2, 5);
222                 placeObject(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(decorativeActors), 0, randFloat(0, 2 * Math.PI));
223         }
226 // Groves, only Wood
227 let groveActors = [g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.bushMedium];
228 let clGrove = createTileClass();
230 function placeGrove(point,
231         groveEntities = [
232                 g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1,
233                 g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2,
234                 g_Gaia.tree3, g_Gaia.tree3, g_Gaia.tree3,
235                 g_Gaia.tree4, g_Gaia.tree4, g_Gaia.tree5
236         ],
237         groveActors = [g_Decoratives.grass, g_Decoratives.rockMedium, g_Decoratives.bushMedium], groveTileClass = undefined,
238         groveTerrainTexture = getArray(g_Terrains.forestFloor1)
241         placeObject(point.x, point.y, pickRandom(["structures/gaul_outpost", "gaia/flora_tree_oak_new"]), 0, randFloat(0, 2 * Math.PI));
242         let quantity = randIntInclusive(20, 30);
243         let dAngle = 2 * Math.PI / quantity;
244         for (let i = 0; i < quantity; ++i)
245         {
246                 let angle = dAngle * randFloat(i, i + 1);
247                 let dist = randFloat(2, 5);
248                 let objectList = groveEntities;
249                 if (i % 3 == 0)
250                         objectList = groveActors;
251                 let x = point.x + dist * Math.cos(angle);
252                 let y = point.y + dist * Math.sin(angle);
253                 placeObject(x, y, pickRandom(objectList), 0, randFloat(0, 2 * Math.PI));
254                 if (groveTileClass)
255                         createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter(groveTerrainTexture), paintClass(groveTileClass)]);
256                 else
257                         createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter(groveTerrainTexture)]);
258         }
261 var farmEntities = {
262         "temperate": { "building": "structures/mace_farmstead", "animal": "gaia/fauna_pig" },
263         "snowy": { "building": "structures/brit_farmstead", "animal": "gaia/fauna_sheep" },
264         "desert": { "building": "structures/pers_farmstead", "animal": "gaia/fauna_camel" },
265         "alpine": { "building": "structures/rome_farmstead", "animal": "gaia/fauna_sheep" },
266         "mediterranean": { "building": "structures/iber_farmstead", "animal": "gaia/fauna_pig" },
267         "savanna": { "building": "structures/sele_farmstead", "animal": "gaia/fauna_horse" },
268         "tropic": { "building": "structures/ptol_farmstead", "animal": "gaia/fauna_camel" },
269         "autumn": { "building": "structures/gaul_farmstead", "animal": "gaia/fauna_horse" }
272 g_WallStyles.other = {
273         "overlap": 0,
274         "fence": readyWallElement("other/fence_long", "gaia"),
275         "fence_short": readyWallElement("other/fence_short", "gaia"),
276         "bench":     { "angle": Math.PI / 2, "length": 1.5,  "indent": 0,    "bend": 0, "templateName": "other/bench" },
277         "foodBin":   { "angle": Math.PI / 2, "length": 1.5,  "indent": 0,    "bend": 0, "templateName": "gaia/special_treasure_food_bin" },
278         "animal":    { "angle": 0,           "length": 0,    "indent": 0.75, "bend": 0, "templateName": farmEntities[currentBiome()].animal },
279         "farmstead": { "angle": Math.PI,     "length": 0,    "indent": -3,   "bend": 0, "templateName": farmEntities[currentBiome()].building }
282 let fences = [
283         new Fortress("fence", [
284                 "foodBin", "farmstead", "bench",
285                 "turn_0.25", "animal", "turn_0.25", "fence",
286                 "turn_0.25", "animal", "turn_0.25", "fence",
287                 "turn_0.25", "animal", "turn_0.25", "fence"
288         ]),
289         new Fortress("fence", [
290                 "foodBin", "farmstead", "fence",
291                 "turn_0.25", "animal", "turn_0.25", "fence",
292                 "turn_0.25", "animal", "turn_0.25", "bench", "animal", "fence",
293                 "turn_0.25", "animal", "turn_0.25", "fence"
294         ]),
295         new Fortress("fence", [
296                 "foodBin", "farmstead", "turn_0.5", "bench", "turn_-0.5", "fence_short",
297                 "turn_0.25", "animal", "turn_0.25", "fence",
298                 "turn_0.25", "animal", "turn_0.25", "fence",
299                 "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence"
300         ]),
301         new Fortress("fence", [
302                 "foodBin", "farmstead", "turn_0.5", "fence_short", "turn_-0.5", "bench",
303                 "turn_0.25", "animal", "turn_0.25", "fence",
304                 "turn_0.25", "animal", "turn_0.25", "fence",
305                 "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence"
306         ]),
307         new Fortress("fence", [
308                 "foodBin", "farmstead", "fence",
309                 "turn_0.25", "animal", "turn_0.25", "bench", "animal", "fence",
310                 "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence",
311                 "turn_0.25", "animal", "turn_0.25", "fence_short", "animal", "fence"
312         ])
314 let num = fences.length;
315 for (let i = 0; i < num; ++i)
316         fences.push(new Fortress("fence", clone(fences[i].wall).reverse()));
318 // Camps with fire and gold treasure
319 function placeCamp(point,
320         centerEntity = "actor|props/special/eyecandy/campfire.xml",
321         otherEntities = ["gaia/special_treasure_metal", "gaia/special_treasure_standing_stone",
322                 "units/brit_infantry_slinger_b", "units/brit_infantry_javelinist_b", "units/gaul_infantry_slinger_b", "units/gaul_infantry_javelinist_b", "units/gaul_champion_fanatic",
323                 "actor|props/special/common/waypoint_flag.xml", "actor|props/special/eyecandy/barrel_a.xml", "actor|props/special/eyecandy/basket_celt_a.xml", "actor|props/special/eyecandy/crate_a.xml", "actor|props/special/eyecandy/dummy_a.xml", "actor|props/special/eyecandy/handcart_1.xml", "actor|props/special/eyecandy/handcart_1_broken.xml", "actor|props/special/eyecandy/sack_1.xml", "actor|props/special/eyecandy/sack_1_rough.xml"
324         ]
327         placeObject(point.x, point.y, centerEntity, 0, randFloat(0, 2 * Math.PI));
328         let quantity = randIntInclusive(5, 11);
329         let dAngle = 2 * Math.PI / quantity;
330         for (let i = 0; i < quantity; ++i)
331         {
332                 let angle = dAngle * randFloat(i, i + 1);
333                 let dist = randFloat(1, 3);
334                 placeObject(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(otherEntities), 0, randFloat(0, 2 * Math.PI));
335         }
338 function placeStartLocationResources(
339         point,
340         foodEntities = [g_Gaia.fruitBush, g_Gaia.chicken],
341         groveEntities = [
342                 g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1, g_Gaia.tree1,
343                 g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2, g_Gaia.tree2,
344                 g_Gaia.tree3, g_Gaia.tree3, g_Gaia.tree3,
345                 g_Gaia.tree4, g_Gaia.tree4, g_Gaia.tree5
346         ],
347         groveTerrainTexture = getArray(g_Terrains.forestFloor1),
348         averageDistToCC = 10,
349         dAverageDistToCC = 2
352         function getRandDist()
353         {
354                 return averageDistToCC + randFloat(-dAverageDistToCC, dAverageDistToCC);
355         }
357         let currentAngle = randFloat(0, 2 * Math.PI);
358         // Stone
359         let dAngle = 4/9 * Math.PI;
360         let angle = currentAngle + randFloat(dAngle / 4, 3 * dAngle / 4);
361         placeMine({ "x": point.x + averageDistToCC * Math.cos(angle), "y": point.y + averageDistToCC * Math.sin(angle) }, g_Gaia.stoneLarge);
363         currentAngle += dAngle;
365         // Wood
366         let quantity = 80;
367         dAngle = 2/3 * Math.PI / quantity;
368         for (let i = 0; i < quantity; ++i)
369         {
370                 angle = currentAngle + randFloat(0, dAngle);
371                 let dist = getRandDist();
372                 let objectList = groveEntities;
373                 if (i % 2 == 0)
374                         objectList = groveActors;
375                 let x = point.x + dist * Math.cos(angle);
376                 let y = point.y + dist * Math.sin(angle);
377                 placeObject(x, y, pickRandom(objectList), 0, randFloat(0, 2 * Math.PI));
378                 createArea(new ClumpPlacer(5, 1, 1, 1, Math.floor(x), Math.floor(y)), [new TerrainPainter(groveTerrainTexture), paintClass(clGrove)]);
379                 currentAngle += dAngle;
380         }
382         // Metal
383         dAngle = 4/9 * Math.PI;
384         angle = currentAngle + randFloat(dAngle / 4, 3 * dAngle / 4);
385         placeMine({ "x": point.x + averageDistToCC * Math.cos(angle), "y": point.y + averageDistToCC * Math.sin(angle) }, g_Gaia.metalLarge);
386         currentAngle += dAngle;
388         // Berries and domestic animals
389         quantity = 15;
390         dAngle = 4/9 * Math.PI / quantity;
391         for (let i = 0; i < quantity; ++i)
392         {
393                 angle = currentAngle + randFloat(0, dAngle);
394                 let dist = getRandDist();
395                 placeObject(point.x + dist * Math.cos(angle), point.y + dist * Math.sin(angle), pickRandom(foodEntities), 0, randFloat(0, 2 * Math.PI));
396                 currentAngle += dAngle;
397         }
401  * Base terrain shape generation and settings
402  */
403  // Height range by map size
404 let heightScale = (g_Map.size + 256) / 768 / 4;
405 let heightRange = { "min": MIN_HEIGHT * heightScale, "max": MAX_HEIGHT * heightScale };
407 // Water coverage
408 let averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value
409 let waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine
410 let waterHeightAdjusted = waterHeight + MIN_HEIGHT; // Water height as terrain height
411 setWaterHeight(waterHeight);
413 // Generate base terrain shape
414 let lowH = heightRange.min;
415 let medH = (heightRange.min + heightRange.max) / 2;
417 // Lake
418 let initialHeightmap = [
419         [medH, medH, medH, medH, medH, medH],
420         [medH, medH, medH, medH, medH, medH],
421         [medH, medH, lowH, lowH, medH, medH],
422         [medH, medH, lowH, lowH, medH, medH],
423         [medH, medH, medH, medH, medH, medH],
424         [medH, medH, medH, medH, medH, medH],
426 if (g_Map.size < 256)
428         initialHeightmap = [
429                 [medH, medH, medH, medH, medH],
430                 [medH, medH, medH, medH, medH],
431                 [medH, medH, lowH, medH, medH],
432                 [medH, medH, medH, medH, medH],
433                 [medH, medH, medH, medH, medH]
434         ];
436 if (g_Map.size >= 384)
438         initialHeightmap = [
439                 [medH, medH, medH, medH, medH, medH, medH, medH],
440                 [medH, medH, medH, medH, medH, medH, medH, medH],
441                 [medH, medH, medH, medH, medH, medH, medH, medH],
442                 [medH, medH, medH, lowH, lowH, medH, medH, medH],
443                 [medH, medH, medH, lowH, lowH, medH, medH, medH],
444                 [medH, medH, medH, medH, medH, medH, medH, medH],
445                 [medH, medH, medH, medH, medH, medH, medH, medH],
446                 [medH, medH, medH, medH, medH, medH, medH, medH],
447         ];
450 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialHeightmap, 0.8);
452 // Apply simple erosion
453 for (let i = 0; i < 5; ++i)
454         splashErodeMap(0.1);
455 globalSmoothHeightmap();
457 // Final rescale
458 rescaleHeightmap(heightRange.min, heightRange.max);
460 Engine.SetProgress(25);
463  * Prepare terrain texture placement
464  */
465 let heighLimits = [
466         heightRange.min + 3/4 * (waterHeightAdjusted - heightRange.min), // 0 Deep water
467         waterHeightAdjusted, // 1 Shallow water
468         waterHeightAdjusted + 2/8 * (heightRange.max - waterHeightAdjusted), // 2 Shore
469         waterHeightAdjusted + 3/8 * (heightRange.max - waterHeightAdjusted), // 3 Low ground
470         waterHeightAdjusted + 4/8 * (heightRange.max - waterHeightAdjusted), // 4 Player and path height
471         waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 5 High ground
472         waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 6 Lower forest border
473         heightRange.max // 7 Forest
475 let playerHeightRange = { "min" : heighLimits[3], "max" : heighLimits[4] };
476 let resourceSpotHeightRange = { "min" : (heighLimits[2] + heighLimits[3]) / 2, "max" : (heighLimits[4] + heighLimits[5]) / 2 };
477 let playerHeight = (playerHeightRange.min + playerHeightRange.max) / 2; // Average player height
479 log("Chosing starting locations...");
480 let [playerIDs, startLocations] = sortPlayersByLocation(getStartLocationsByHeightmap(playerHeightRange, 1000, 30));
482 Engine.SetProgress(30);
485  * Smooth Start Locations before height region calculation
486  */
487 let playerBaseRadius = 35;
488 if (g_Map.size < 256)
489         playerBaseRadius = 25;
490 for (let p = 0; p < playerIDs.length; ++p)
491         rectangularSmoothToHeight(startLocations[p], playerBaseRadius, playerBaseRadius, playerHeight, 0.7);
494  * Calculate tile centered height map after start position smoothing but before placing paths
495  * This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false!
496  */
497 let tchm = getTileCenteredHeightmap();
500  * Add paths (If any)
501  */
502 let clPath = createTileClass();
505  * Divide tiles in areas by height and avoid paths
506  */
507 let areas = [];
508 for (let h = 0; h < heighLimits.length; ++h)
509         areas.push([]);
511 for (let x = 0; x < tchm.length; ++x)
512         for (let y = 0; y < tchm[0].length; ++y)
513         {
514                 if (g_Map.tileClasses[clPath].inclusionCount[x][y] > 0) // Avoid paths
515                         continue;
517                 let minHeight = heightRange.min;
518                 for (let h = 0; h < heighLimits.length; ++h)
519                 {
520                         if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h])
521                         {
522                                 areas[h].push({ "x": x, "y": y });
523                                 break;
524                         }
525                         else
526                                 minHeight = heighLimits[h];
527                 }
528         }
531  * Get max slope of each area
532  */
533 let slopeMap = getSlopeMap();
534 let minSlope = [];
535 let maxSlope = [];
536 for (let h = 0; h < heighLimits.length; ++h)
538         minSlope[h] = Infinity;
539         maxSlope[h] = 0;
540         for (let t = 0; t < areas[h].length; ++t)
541         {
542                 let x = areas[h][t].x;
543                 let y = areas[h][t].y;
544                 let slope = slopeMap[x][y];
546                 if (slope > maxSlope[h])
547                         maxSlope[h] = slope;
549                 if (slope < minSlope[h])
550                         minSlope[h] = slope;
551         }
555  * Paint areas by height and slope
556  */
557 for (let h = 0; h < heighLimits.length; ++h)
558         for (let t = 0; t < areas[h].length; ++t)
559         {
560                 let x = areas[h][t].x;
561                 let y = areas[h][t].y;
562                 let actor;
563                 let texture = pickRandom(wildLakeBiome[h].texture);
565                 if (slopeMap[x][y] < 0.5 * (minSlope[h] + maxSlope[h]))
566                 {
567                         if (randBool(wildLakeBiome[h].actor[1]))
568                                 actor = pickRandom(wildLakeBiome[h].actor[0]);
569                 }
570                 else
571                 {
572                         texture = pickRandom(wildLakeBiome[h].textureHS);
573                         if (randBool(wildLakeBiome[h].actorHS[1]))
574                                 actor = pickRandom(wildLakeBiome[h].actorHS[0]);
575                 }
577                 g_Map.texture[x][y] = g_Map.getTextureID(texture);
579                 if (actor)
580                         placeObject(randFloat(x, x + 1), randFloat(y, y + 1), actor, 0, randFloat(0, 2 * Math.PI));
581         }
582 Engine.SetProgress(80);
584 log("Placing resources...");
585 let avoidPoints = clone(startLocations);
586 for (let i = 0; i < avoidPoints.length; ++i)
587         avoidPoints[i].dist = 30;
588 let resourceSpots = getPointsByHeight(resourceSpotHeightRange, avoidPoints, clPath);
589 Engine.SetProgress(55);
591 log("Placing players...");
592 if (isNomad())
593         placePlayersNomad(createTileClass(), new HeightConstraint(playerHeightRange.min, playerHeightRange.max));
594 else
595         for (let p = 0; p < playerIDs.length; ++p)
596         {
597                 let point = startLocations[p];
598                 placeCivDefaultStartingEntities(point.x, point.y, playerIDs[p], g_Map.size > 192);
599                 placeStartLocationResources(point);
600         }
602 let mercenaryCamps = isNomad() ? 0 : Math.ceil(g_Map.size / 256);
603 log("Maximum number of mercenary camps: " + mercenaryCamps);
604 for (let i = 0; i < resourceSpots.length; ++i)
606         let choice = i % 5;
607         if (choice == 0)
608                 placeMine(resourceSpots[i], g_Gaia.stoneLarge);
609         if (choice == 1)
610                 placeMine(resourceSpots[i], g_Gaia.metalLarge);
611         if (choice == 2)
612                 placeGrove(resourceSpots[i]);
613         if (choice == 3)
614         {
615                 placeCamp(resourceSpots[i]);
616                 rectangularSmoothToHeight(resourceSpots[i], 5, 5, g_Map.height[resourceSpots[i].x][resourceSpots[i].y] - 10, 0.5);
617         }
618         if (choice == 4)
619         {
620                 if (mercenaryCamps)
621                 {
622                         placeStartingEntities(resourceSpots[i].x, resourceSpots[i].y, 0, mercenaryCampGuards[currentBiome()]);
623                         rectangularSmoothToHeight(resourceSpots[i], 15, 15, g_Map.height[resourceSpots[i].x][resourceSpots[i].y], 0.5);
624                         --mercenaryCamps;
625                 }
626                 else
627                 {
628                         placeCustomFortress(resourceSpots[i].x, resourceSpots[i].y, pickRandom(fences), "other", 0, randFloat(0, 2 * Math.PI));
629                         rectangularSmoothToHeight(resourceSpots[i], 10, 10, g_Map.height[resourceSpots[i].x][resourceSpots[i].y], 0.5);
630                 }
631         }
634 ExportMap();