Unify the other axis of the 8 remaining playerPlacementRiver calls following rP20151...
[0ad.git] / binaries / data / mods / public / maps / random / the_unknown / unknown_common.js
blob53d64918047ed24e50e194b1b408e3ecf5fd3692
1 /**
2  * @file This library is used to generate different map variations on the map Unknown, Unknown Land and Unknown Nomad.
3  */
5 /**
6  * True if city centers should be placed or false for nomad.
7  */
8 var g_PlayerBases;
10 /**
11  * True if all players should be connected via land and false if river or islands can split some if not all the players.
12  */
13 var g_AllowNaval;
15 TILE_CENTERED_HEIGHT_MAP = true;
17 setSelectedBiome();
19 const tMainTerrain = g_Terrains.mainTerrain;
20 const tForestFloor1 = g_Terrains.forestFloor1;
21 const tForestFloor2 = g_Terrains.forestFloor2;
22 const tCliff = g_Terrains.cliff;
23 const tTier1Terrain = g_Terrains.tier1Terrain;
24 const tTier2Terrain = g_Terrains.tier2Terrain;
25 const tTier3Terrain = g_Terrains.tier3Terrain;
26 const tHill = g_Terrains.hill;
27 const tRoad = g_Terrains.road;
28 const tRoadWild = g_Terrains.roadWild;
29 const tTier4Terrain = g_Terrains.tier4Terrain;
30 const tShore = g_Terrains.shore;
31 const tWater = g_Terrains.water;
33 const oTree1 = g_Gaia.tree1;
34 const oTree2 = g_Gaia.tree2;
35 const oTree4 = g_Gaia.tree4;
36 const oTree5 = g_Gaia.tree5;
37 const oFruitBush = g_Gaia.fruitBush;
38 const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal;
39 const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal;
40 const oFish = g_Gaia.fish;
41 const oStoneLarge = g_Gaia.stoneLarge;
42 const oStoneSmall = g_Gaia.stoneSmall;
43 const oMetalLarge = g_Gaia.metalLarge;
44 const oWoodTreasure = "gaia/special_treasure_wood";
46 const aGrass = g_Decoratives.grass;
47 const aGrassShort = g_Decoratives.grassShort;
48 const aReeds = g_Decoratives.reeds;
49 const aLillies = g_Decoratives.lillies;
50 const aRockLarge = g_Decoratives.rockLarge;
51 const aRockMedium = g_Decoratives.rockMedium;
52 const aBushMedium = g_Decoratives.bushMedium;
53 const aBushSmall = g_Decoratives.bushSmall;
55 const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2];
56 const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1];
58 InitMap();
60 const numPlayers = getNumPlayers();
61 const mapSize = getMapSize();
62 const mapArea = getMapArea();
63 const lSize = Math.pow(scaleByMapSize(1, 6), 1/8);
65 var clPlayer = createTileClass();
66 var clPlayerTerritory = createTileClass();
67 var clHill = createTileClass();
68 var clForest = createTileClass();
69 var clWater = createTileClass();
70 var clDirt = createTileClass();
71 var clRock = createTileClass();
72 var clMetal = createTileClass();
73 var clFood = createTileClass();
74 var clPeninsulaSteam = createTileClass();
75 var clBaseResource = createTileClass();
76 var clLand = createTileClass();
77 var clShallow = createTileClass();
79 var landHeight = 3;
80 var cliffHeight = 3.12;
81 var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, landHeight, 4);
83 var unknownMapFunctions = {
84         "land": [
85                 "Continent",
86                 "CentralSea",
87                 "CentralRiver",
88                 "EdgeSeas",
89                 "Gulf",
90                 "Lakes",
91                 "Passes",
92                 "Lowlands",
93                 "Mainland"
94         ],
95         "naval": [
96                 "Archipelago",
97                 "RiversAndLake"
98         ]
102  * The player IDs and locations shall only be determined by the landscape functions if it's not a nomad game,
103  * because nomad maps randomize the locations after the terrain generation.
104  * The locations should only determined by the landscape functions to avoid placing bodies of water and resources into civic centers and the starting resources.
105  */
106 var playerIDs = sortAllPlayers();
107 var playerX = [];
108 var playerZ = [];
110 var g_StartingTreasures = false;
111 var g_IberianWalls = "walls";
113 function createUnknownMap()
115         let funcs = unknownMapFunctions.land;
117         if (g_AllowNaval)
118                 funcs = funcs.concat(unknownMapFunctions.naval);
120         global["unknown" + pickRandom(funcs)]();
122         paintUnknownMapBasedOnHeight();
124         if (g_PlayerBases)
125                 createUnknownPlayerBases();
129  * Chain of islands or many disconnected islands.
130  */
131 function unknownArchipelago()
133         g_IberianWalls = "towers";
134         g_StartingTreasures = true;
136         let [pIDs, islandX, islandZ] = radialPlayerPlacement();
137         if (g_PlayerBases)
138         {
139                 [playerIDs, playerX, playerZ] = [pIDs, islandX, islandZ];
140                 markPlayerArea("large");
141         }
143         log("Creating islands...");
144         let islandSize = diskArea(scaleByMapSize(17, 29));
145         for (let i = 0; i < numPlayers; ++i)
146                 createArea(
147                         new ClumpPlacer(islandSize, 0.8, 0.1, 10, fractionToTiles(islandX[i]), fractionToTiles(islandZ[i])),
148                         landElevationPainter,
149                         null);
151         let type = randIntInclusive(1, 3);
152         if (type == 1)
153         {
154                 log("Creating archipelago...");
155                 createAreas(
156                         new ClumpPlacer(Math.floor(islandSize * randFloat(0.8, 1.2)), 0.8, 0.1, 10),
157                         [
158                                 landElevationPainter,
159                                 paintClass(clLand)
160                         ],
161                         null,
162                         scaleByMapSize(2, 5) * randIntInclusive(8, 14));
164                 log("Creating shore jaggedness with small puddles...");
165                 createAreas(
166                         new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, 1),
167                         [
168                                 new SmoothElevationPainter(ELEVATION_SET, landHeight, 4),
169                                 paintClass(clLand)
170                         ],
171                         borderClasses(clLand, 6, 3),
172                         scaleByMapSize(12, 130) * 2,
173                         150);
174         }
175         else if (type == 2)
176         {
177                 log("Creating islands...");
178                 createAreas(
179                         new ClumpPlacer(Math.floor(islandSize * randFloat(0.6, 1.4)), 0.8, 0.1, randFloat(0.0, 0.2)),
180                         [
181                                 landElevationPainter,
182                                 paintClass(clLand)
183                         ],
184                         avoidClasses(clLand, 3, clPlayerTerritory, 3),
185                         scaleByMapSize(6, 10) * randIntInclusive(8, 14));
187                 log("Creating small islands...");
188                 createAreas(
189                         new ClumpPlacer(Math.floor(islandSize * randFloat(0.3, 0.7)), 0.8, 0.1, 0.07),
190                         [
191                                 new SmoothElevationPainter(ELEVATION_SET, landHeight, 6),
192                                 paintClass(clLand)
193                         ],
194                         avoidClasses(clLand, 3, clPlayerTerritory, 3),
195                         scaleByMapSize(2, 6) * randIntInclusive(6, 15),
196                         25);
197         }
198         else if (type == 3)
199         {
200                 log("Creating tight islands...");
201                 createAreas(
202                         new ClumpPlacer(Math.floor(islandSize * randFloat(0.8, 1.2)), 0.8, 0.1, 10),
203                         [
204                                 landElevationPainter,
205                                 paintClass(clLand)
206                         ],
207                         avoidClasses(clLand, randIntInclusive(8, 16), clPlayerTerritory, 3),
208                         scaleByMapSize(2, 5) * randIntInclusive(8, 14));
209         }
213  * Disk shaped mainland with water on the edge.
214  */
215 function unknownContinent()
217         let waterHeight = -5;
219         if (g_PlayerBases)
220         {
221                 log("Ensuring player area...");
222                 [playerIDs, playerX, playerZ] = radialPlayerPlacement(0.25);
223                 markPlayerArea("small");
225                 for (let i = 0; i < numPlayers; ++i)
226                         createArea(
227                                 new ChainPlacer(
228                                         2,
229                                         Math.floor(scaleByMapSize(5, 9)),
230                                         Math.floor(scaleByMapSize(5, 20)),
231                                         1,
232                                         Math.round(fractionToTiles(playerX[i])),
233                                         Math.round(fractionToTiles(playerZ[i])),
234                                         0,
235                                         [Math.floor(scaleByMapSize(23, 50))]),
236                                 [
237                                         landElevationPainter,
238                                         paintClass(clLand)
239                                 ],
240                                 null);
241         }
243         log("Creating continent...");
244         createArea(
245                 new ClumpPlacer(mapArea * 0.45, 0.9, 0.09, 10, Math.round(fractionToTiles(0.5)), Math.round(fractionToTiles(0.5))),
246                 [
247                         landElevationPainter,
248                         paintClass(clLand)
249                 ],
250                 null);
252         if (randBool(1/3))
253         {
254                 log("Creating peninsula (i.e. half the map not being surrounded by water)...");
255                 let angle = randFloat(0, 2 * Math.PI);
256                 createArea(
257                         new ClumpPlacer(
258                                 mapArea * 0.45,
259                                 0.9,
260                                 0.09,
261                                 10,
262                                 Math.round(fractionToTiles(0.5 + 0.25 * Math.cos(angle))),
263                                 Math.round(fractionToTiles(0.5 + 0.25 * Math.sin(angle)))),
264                         [
265                                 landElevationPainter,
266                                 paintClass(clLand)
267                         ],
268                         null);
270                 log("Remembering to not paint shorelines into the peninsula...");
271                 createArea(
272                         new ClumpPlacer(
273                                 mapArea * 0.3,
274                                 0.9,
275                                 0.01,
276                                 10,
277                                 Math.round(fractionToTiles(0.5 + 0.35 * Math.cos(angle))),
278                                 Math.round(fractionToTiles(0.5 + 0.35 * Math.sin(angle)))),
279                         paintClass(clPeninsulaSteam),
280                         null);
281         }
283         createShoreJaggedness(waterHeight, clLand, 7);
287  * Creates a huge central river, possibly connecting the riversides with a narrow piece of land.
288  */
289 function unknownCentralSea()
291         let waterHeight = -3;
292         let horizontal = randBool();
294         let [start, end] = centralRiverCoordinates(horizontal);
295         paintRiver({
296                 "parallel": false,
297                 "startX": tilesToFraction(start[0]),
298                 "startZ": tilesToFraction(start[1]),
299                 "endX": tilesToFraction(end[0]),
300                 "endZ": tilesToFraction(end[1]),
301                 "width": randFloat(0.22, 0.3) + scaleByMapSize(0.05, 0.2),
302                 "fadeDist": 0.025,
303                 "deviation": 0,
304                 "waterHeight": waterHeight,
305                 "landHeight": landHeight,
306                 "meanderShort": 20,
307                 "meanderLong": 0,
308                 "waterFunc": (ix, iz, height, riverFraction) => {
309                         if (height < 0)
310                                 addToClass(ix, iz, clWater);
311                 },
312                 "landFunc": (ix, iz, shoreDist1, shoreDist2) => {
313                         setHeight(ix, iz, 3.1);
314                         addToClass(ix, iz, clLand);
315                 }
316         });
318         if (g_PlayerBases)
319         {
320                 [playerIDs, playerX, playerZ] = playerPlacementRiver(horizontal ? Math.PI / 2 : 0, 0.6);
321                 markPlayerArea("small");
322         }
324         if (!g_AllowNaval || randBool())
325         {
326                 log("Creating isthmus (i.e. connecting the two riversides with a big land passage)...");
327                 let [coord1, coord2] = centralRiverCoordinates(!horizontal);
328                 createArea(
329                         new PathPlacer(
330                                 ...coord1,
331                                 ...coord2,
332                                 scaleByMapSize(randIntInclusive(16, 24),
333                                 randIntInclusive(100, 140)),
334                                 0.5,
335                                 3 * scaleByMapSize(1, 4),
336                                 0.1,
337                                 0.01),
338                         [
339                                 landElevationPainter,
340                                 paintClass(clLand),
341                                 unPaintClass(clWater)
342                         ],
343                         null);
344         }
346         createExtensionsOrIslands();
347         // Don't createShoreJaggedness since it doesn't fit artistically here
351  * Creates a very small central river.
352  */
353 function unknownCentralRiver()
355         let waterHeight = -4;
357         initHeight(landHeight);
359         let horizontal = randBool();
361         if (g_PlayerBases)
362         {
363                 [playerIDs, playerX, playerZ] = playerPlacementRiver(horizontal ? Math.PI / 2 : 0, 0.5);
364                 markPlayerArea("large");
365         }
367         log("Creating the main river...");
368         let [coord1, coord2] = centralRiverCoordinates(horizontal);
369         createArea(
370                 new PathPlacer(...coord1, ...coord2, scaleByMapSize(14, 24), 0.5, scaleByMapSize(3, 12), 0.1, 0.01),
371                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
372                 avoidClasses(clPlayerTerritory, 4));
374         log("Creating small water spots at the map border to ensure separation of players...");
375         for (let coord of [coord1, coord2])
376                 createArea(
377                         new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(5, 10))), 0.95, 0.6, 10, ...coord),
378                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 2),
379                         avoidClasses(clPlayerTerritory, 8));
381         if (!g_AllowNaval || randBool())
382         {
383                 log("Creating the shallows of the main river...");
384                 for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i)
385                 {
386                         let cLocation = randFloat(0.15, 0.85);
387                         let x1 = [fractionToTiles(cLocation), fractionToTiles(0.35)];
388                         let x2 = [fractionToTiles(cLocation), fractionToTiles(0.65)];
389                         if (!horizontal)
390                         {
391                                 x1.reverse();
392                                 x2.reverse();
393                         }
394                         createShallowsPassage(...x1, ...x2, scaleByMapSize(4, 8), -2, -2, 2, clShallow, undefined, waterHeight);
395                 }
396         }
398         if (randBool(2/3))
399                 createTributaryRivers(
400                         horizontal,
401                         randIntInclusive(8, scaleByMapSize(12, 16)),
402                         scaleByMapSize(10, 20),
403                         -4,
404                         [-6, -1.5],
405                         Math.PI / 5,
406                         clWater,
407                         clShallow,
408                         avoidClasses(clPlayerTerritory, 3));
412  * Creates a circular lake in the middle and possibly a river between each player ("pizza slices").
413  */
414 function unknownRiversAndLake()
416         let waterHeight = -4;
417         initHeight(landHeight);
419         let startAngle;
420         if (g_PlayerBases)
421         {
422                 let playerAngle;
423                 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
424                 markPlayerArea("small");
425         }
427         let mid = Math.round(fractionToTiles(0.5));
428         let lake = randBool(3/4);
429         if (lake)
430         {
431                 log("Creating lake...");
432                 createArea(
433                         new ClumpPlacer(mapArea * 0.09 * lSize, 0.7, 0.1, 10, mid, mid),
434                         [
435                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
436                                 paintClass(clWater)
437                         ],
438                         null);
440                 createShoreJaggedness(waterHeight, clWater, 3);
441         }
443         // Don't do this on nomad because the imbalances on the different islands are too drastic
444         if (g_PlayerBases && (!lake || randBool(1/3)))
445         {
446                 log("Creating small rivers separating players...");
447                 let [riverX, riverZ, riverAngle] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, 0.5, 0.5, 0.5);
449                 for (let i = 0; i < numPlayers; ++i)
450                 {
451                         createArea(
452                                 new PathPlacer(mid, mid, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i]), scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
453                                 [
454                                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
455                                         paintClass(clWater)
456                                 ],
457                                 avoidClasses(clPlayer, 5));
459                         createArea(
460                                 new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i])),
461                                 [
462                                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0),
463                                         paintClass(clWater)
464                                 ],
465                                 avoidClasses(clPlayer, 5));
466                 }
468                 log("Creating lake...");
469                 createArea(
470                         new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid),
471                         [
472                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
473                                 paintClass(clWater)
474                         ],
475                         null);
476         }
478         if (lake && randBool())
479         {
480                 log("Creating small central island...");
481                 createArea(
482                         new ClumpPlacer(mapArea * 0.006 * lSize, 0.7, 0.1, 10, mid, mid),
483                         [
484                                 landElevationPainter,
485                                 paintClass(clWater)
486                         ],
487                         null);
488         }
492  * Align players on a land strip with seas bordering on one or both sides that can hold islands.
493  */
494 function unknownEdgeSeas()
496         let waterHeight = -4;
497         initHeight(landHeight);
499         let horizontal = randBool();
500         if (g_PlayerBases)
501         {
502                 [playerIDs, playerX, playerZ] = playerPlacementLine(horizontal, 0.5, 0.2);
503                 // Don't place the shoreline inside the CC, but possibly into the players territory
504                 markPlayerArea("small");
505         }
507         for (let location of pickRandom([["first"], ["second"], ["first", "second"]]))
508         {
509                 let positionX = location == "first" ? [0, 0] : [1, 1];
510                 let positionZ = [0, 1];
512                 if (horizontal)
513                         [positionX, positionZ] = [positionZ, positionX];
515                 paintRiver({
516                         "parallel": true,
517                         "startX": positionX[0],
518                         "startZ": positionZ[0],
519                         "endX": positionX[1],
520                         "endZ": positionZ[1],
521                         "width": 0.62 - randFloat(0, scaleByMapSize(0, 0.1)),
522                         "fadeDist": 0.015,
523                         "deviation": 0,
524                         "waterHeight": waterHeight,
525                         "landHeight": landHeight,
526                         "meanderShort": 20,
527                         "meanderLong": 0
528                 });
529         }
531         createExtensionsOrIslands();
532         paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand);
533         createShoreJaggedness(waterHeight, clLand, 7, false);
537  * Land shaped like a concrescent moon around a central lake.
538  */
539 function unknownGulf()
541         let waterHeight = -3;
542         initHeight(landHeight);
544         let startAngle = randFloat(0, 2) * Math.PI;
545         if (g_PlayerBases)
546         {
547                 log("Determining player locations...");
548                 for (let i = 0; i < numPlayers; ++i)
549                 {
550                         let playerAngle = startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1)));
551                         playerX[i] = 0.5 + 0.35 * Math.cos(playerAngle);
552                         playerZ[i] = 0.5 + 0.35 * Math.sin(playerAngle);
553                 }
554                 markPlayerArea("large");
555         }
557         let placers = [
558                 new ClumpPlacer(mapArea * 0.08, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5)), Math.round(fractionToTiles(0.5))),
559                 new ClumpPlacer(mapArea * 0.13 * lSize, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5 - 0.2 * Math.cos(startAngle))), Math.round(fractionToTiles(0.5 - 0.2 * Math.sin(startAngle)))),
560                 new ClumpPlacer(mapArea * 0.15 * lSize, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5 - 0.49 * Math.cos(startAngle))), Math.round(fractionToTiles(0.5 - 0.49 * Math.sin(startAngle)))),
561         ];
563         for (let placer of placers)
564                 createArea(
565                         placer,
566                         [
567                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
568                                 paintClass(clWater)
569                         ],
570                         avoidClasses(clPlayerTerritory, scaleByMapSize(15, 25)));
574  * Mainland style with some small random lakes.
575  */
576 function unknownLakes()
578         let waterHeight = -5;
580         initHeight(landHeight);
582         if (g_PlayerBases)
583         {
584                 [playerIDs, playerX, playerZ] = radialPlayerPlacement();
585                 markPlayerArea("large");
586         }
588         log("Creating lakes...");
589         createAreas(
590                 new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, 1),
591                 [
592                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5),
593                         paintClass(clWater)
594                 ],
595                 [avoidClasses(clPlayerTerritory, 12), randBool() ? avoidClasses(clWater, 8) : new NullConstraint()],
596                 scaleByMapSize(5, 16));
600  * A large hill leaving players only a small passage to each of the the two neighboring players.
601  */
602 function unknownPasses()
604         let mountainHeight = 24;
605         let waterHeight = -4;
606         initHeight(landHeight);
608         let playerAngle;
609         let startAngle;
610         if (g_PlayerBases)
611         {
612                 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
613                 markPlayerArea("small");
614         }
615         else
616                 startAngle = Math.random(0, 2 * Math.PI);
618         let mid = Math.round(fractionToTiles(0.5));
619         let [mountainX, mountainZ] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mid, mid);
620         let [passesX, passesZ] = distributePointsOnCircle(numPlayers * 2, startAngle, fractionToTiles(0.35), mid, mid);
622         for (let i = 0; i < numPlayers; ++i)
623         {
624                 log("Creating a mountain range between neighboring players...");
625                 createArea(
626                         new PathPlacer(mid, mid, mountainX[i], mountainZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
627                         [
628                                 // More smoothing than this often results in the mountainrange becoming passable to one player.
629                                 new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 1),
630                                 paintClass(clWater)
631                         ],
632                         avoidClasses(clPlayer, 5));
634                 log("Creating small mountain at the map border between the players to ensure separation of players...");
635                 createArea(
636                         new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, mountainX[i], mountainZ[i]),
637                         new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 0),
638                         avoidClasses(clPlayer, 5));
640                 log("Create passages between neighboring players...");
641                 createArea(
642                         new PathPlacer(
643                                 passesX[2 * i],
644                                 passesZ[2 * i],
645                                 passesX[2 * ((i + 1) % numPlayers)],
646                                 passesZ[2 * ((i + 1) % numPlayers)],
647                                 scaleByMapSize(14, 24),
648                                 0.4,
649                                 3 * scaleByMapSize(1, 3),
650                                 0.2,
651                                 0.05),
652                         new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
653                         null);
654         }
656         if (randBool(2/5))
657         {
658                 log("Create central lake...");
659                 createArea(
660                         new ClumpPlacer(mapArea * 0.03 * lSize, 0.7, 0.1, 10, mid, mid),
661                         [
662                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3),
663                                 paintClass(clWater)
664                         ],
665                         null);
666         }
667         else
668         {
669                 log("Fill area between the paths...");
670                 createArea(
671                         new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid),
672                         [
673                                 new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 4),
674                                 paintClass(clWater)
675                         ],
676                         null);
677         }
681  * Land enclosed by a hill that leaves small areas for civic centers and large central place.
682  */
683 function unknownLowlands()
685         let mountainHeight = 30;
687         log("Creating mountain that is going to separate players...");
688         initHeight(mountainHeight);
690         let playerAngle;
691         let startAngle;
692         if (g_PlayerBases)
693         {
694                 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
695                 markPlayerArea("small");
696         }
697         else
698                 startAngle = Math.random(0, 2 * Math.PI);
700         log("Creating valleys enclosed by the mountain...");
701         let valleys = numPlayers;
702         if (mapSize <= 128 && numPlayers <= 2 ||
703             mapSize <= 192 && numPlayers <= 3 ||
704             mapSize <= 320 && numPlayers <= 4 ||
705             mapSize <= 384 && numPlayers <= 5 ||
706             mapSize <= 448 && numPlayers <= 6)
707                 valleys *= 2;
709         let mid = Math.round(fractionToTiles(0.5));
710         let [valleyX, valleyZ] = distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mid, mid);
711         for (let i = 0; i < valleys; ++i)
712                 createArea(
713                         new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, 10, valleyX[i], valleyZ[i]),
714                         [
715                                 new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
716                                 paintClass(clLand)
717                         ],
718                         null);
720         log("Creating the big central area...");
721         createArea(
722                 new ClumpPlacer(mapArea * 0.091 * lSize, 0.7, 0.1, 10, mid, mid),
723                 [
724                         landElevationPainter,
725                         paintClass(clWater)
726                 ],
727                 null);
729         log("Creating passes from player areas to the center...");
730         for (let i = 0; i < valleys; ++i)
731                 createArea(
732                         new PathPlacer(mid, mid, valleyX[i], valleyZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
733                         [
734                                 landElevationPainter,
735                                 paintClass(clWater)
736                         ],
737                         null);
741  * No water, no hills.
742  */
743 function unknownMainland()
745         initHeight(3);
747         if (g_PlayerBases)
748         {
749                 [playerIDs, playerX, playerZ] = radialPlayerPlacement();
750                 markPlayerArea("small");
751         }
754 function centralRiverCoordinates(horizontal)
756         let coord1 = [0, fractionToTiles(0.5)];
757         let coord2 = [fractionToTiles(1), fractionToTiles(0.5)];
759         if (!horizontal)
760         {
761                 coord1.reverse();
762                 coord2.reverse();
763         }
765         return [coord1, coord2];
768 function createShoreJaggedness(waterHeight, borderClass, shoreDist, inwards = true)
770         log("Creating shore jaggedness...");
771         for (let i = 0; i < 2; ++i)
772                 if (i || inwards)
773                         createAreas(
774                                 new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, 1),
775                                 [
776                                                 new SmoothElevationPainter(ELEVATION_SET, i ? landHeight : waterHeight, 4),
777                                                 i ? paintClass(clLand) : unPaintClass(clLand)
778                                 ],
779                                 [
780                                                 avoidClasses(clPlayer, 20, clPeninsulaSteam, 20),
781                                                 borderClasses(borderClass, shoreDist, shoreDist)
782                                 ],
783                                 scaleByMapSize(7, 130) * 2,
784                                 150);
787 function createExtensionsOrIslands()
789         let rnd = randIntInclusive(1, 3);
791         if (rnd == 1)
792         {
793                 log("Creating islands...");
794                 createAreas(
795                         new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)),
796                         [
797                                 landElevationPainter,
798                                 paintClass(clLand)
799                         ],
800                         avoidClasses(clLand, 3, clPlayer, 3),
801                         scaleByMapSize(2, 5) * randIntInclusive(8, 14));
802         }
803         else if (rnd == 2)
804         {
805                 log("Creating extentions...");
806                 createAreas(
807                         new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07),
808                         [
809                                 landElevationPainter,
810                                 paintClass(clLand)
811                         ],
812                         null,
813                         scaleByMapSize(2, 5) * randIntInclusive(8, 14));
814         }
818  * Prevent impassable terrain and resource collisions at the the civic center and starting resources.
819  */
820 function markPlayerArea(size)
822         for (let i = 0; i < numPlayers; ++i)
823         {
824                 addCivicCenterAreaToClass(
825                         Math.round(fractionToTiles(playerX[i])),
826                         Math.round(fractionToTiles(playerZ[i])),
827                         clPlayer);
829                 if (size == "large")
830                         createArea(
831                                 new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, fractionToTiles(playerX[i]), fractionToTiles(playerZ[i])),
832                                 paintClass(clPlayerTerritory),
833                                 null);
834         }
837 function paintUnknownMapBasedOnHeight()
839         paintTerrainBasedOnHeight(cliffHeight, 40, 1, tCliff);
840         paintTerrainBasedOnHeight(3, cliffHeight, 1, tMainTerrain);
841         paintTerrainBasedOnHeight(1, 3, 1, tShore);
842         paintTerrainBasedOnHeight(-8, 1, 2, tWater);
844         unPaintTileClassBasedOnHeight(0, cliffHeight, 1, clWater);
845         unPaintTileClassBasedOnHeight(-6, 0, 1, clLand);
847         paintTileClassBasedOnHeight(-6, 0, 1, clWater);
848         paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand);
849         paintTileClassBasedOnHeight(cliffHeight, 40, 1, clHill);
853  * Place resources and decoratives after the player territory was marked.
854  */
855 function createUnknownObjects()
857         log("Creating bumps...");
858         createAreas(
859                 new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1),
860                 new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2),
861                 [avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 3)],
862                 randIntInclusive(0, scaleByMapSize(1, 2) * 200));
864         log("Creating hills...");
865         createAreas(
866                 new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1),
867                 [
868                         new LayeredPainter([tCliff, tHill], [2]),
869                         new SmoothElevationPainter(ELEVATION_SET, 18, 2),
870                         paintClass(clHill)
871                 ],
872                 [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)],
873                 randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9))
874         );
875         RMS.SetProgress(50);
877         log("Creating forests...");
878         let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1));
880         let types = [
881                 [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]],
882                 [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]]
883         ];
885         let size = numForest / (scaleByMapSize(2, 8) * numPlayers);
886         let num = Math.floor(size / types.length);
887         for (let type of types)
888                 createAreas(
889                         new ClumpPlacer(numForest / num, 0.1, 0.1, 1),
890                         [
891                                 new LayeredPainter(type, [2]),
892                                 paintClass(clForest)
893                         ],
894                         [avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 0), stayClasses(clLand, 4)],
895                         num);
896         RMS.SetProgress(50);
898         log("Creating dirt patches...");
899         let patchCount = (currentBiome() == "savanna" ? 3 : 1) * scaleByMapSize(15, 45);
900         for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)])
901                 createAreas(
902                         new ClumpPlacer(size, 0.3, 0.06, 0.5),
903                         [
904                                 new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]),
905                                 paintClass(clDirt)
906                         ],
907                         [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
908                         patchCount);
910         log("Creating grass patches...");
911         for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)])
912                 createAreas(
913                                 new ClumpPlacer(size, 0.3, 0.06, 0.5),
914                         new TerrainPainter(tTier4Terrain),
915                         [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
916                         patchCount);
918         RMS.SetProgress(55);
920         log("Creating stone mines...");
921         createObjectGroupsDeprecated(
922                 new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock),
923                 0,
924                 [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 3)],
925                 randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
926                 100);
928         log("Creating small stone quarries...");
929         createObjectGroupsDeprecated(
930                 new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock),
931                 0,
932                 [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 3)],
933                 randIntInclusive(scaleByMapSize(2, 9),scaleByMapSize(9, 40)),
934                 100);
936         log("Creating metal mines...");
937         createObjectGroupsDeprecated(
938                 new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal),
939                 0,
940                 [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 1), stayClasses(clLand, 3)],
941                 randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
942                 100);
943         RMS.SetProgress(65);
945         log("Creating small decorative rocks...");
946         createObjectGroupsDeprecated(
947                 new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true),
948                 0,
949                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 3)],
950                 scaleByMapSize(16, 262),
951                 50);
953         log("Creating large decorative rocks...");
954         createObjectGroupsDeprecated(
955                 new SimpleGroup([new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], true),
956                 0,
957                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 3)],
958                 scaleByMapSize(8, 131),
959                 50);
960         RMS.SetProgress(70);
962         log("Creating deer...");
963         createObjectGroupsDeprecated(
964                 new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood),
965                 0,
966                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
967                 randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
968                 50);
970         log("Creating berry bush...");
971         createObjectGroupsDeprecated(
972                 new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood),
973                 0,
974                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
975                 randIntInclusive(1, 4) * numPlayers + 2,
976                 50);
977         RMS.SetProgress(75);
979         log("Creating sheep...");
980         createObjectGroupsDeprecated(
981                 new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood),
982                 0,
983                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
984                 randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
985                 50);
987         log("Creating fish...");
988         createObjectGroupsDeprecated(
989                 new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood),
990                 0,
991                 avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20),
992                 randIntInclusive(15, 40) * numPlayers,
993                 60);
994         RMS.SetProgress(85);
996         log("Creating straggler trees...");
997         types = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
999         num = Math.floor(numStragglers / types.length);
1000         for (let type of types)
1001                 createObjectGroupsDeprecated(
1002                         new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest),
1003                         0,
1004                         [avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)],
1005                         num);
1007         let planetm = currentBiome() == "tropic" ? 8 : 1;
1009         log("Creating small grass tufts...");
1010         createObjectGroupsDeprecated(
1011                 new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]),
1012                 0,
1013                 [avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)],
1014                 planetm * scaleByMapSize(13, 200));
1015         RMS.SetProgress(90);
1017         log("Creating large grass tufts...");
1018         createObjectGroupsDeprecated(
1019                 new SimpleGroup([new SimpleObject(aGrass, 2, 4, 0, 1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5, -Math.PI / 8, Math.PI / 8)]),
1020                 0,
1021                 [avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 3)],
1022                 planetm * scaleByMapSize(13, 200));
1023         RMS.SetProgress(95);
1025         log("Creating shallow flora...");
1026         createObjectGroupsDeprecated(
1027                 new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 2), new SimpleObject(aReeds, 2, 4, 0, 2)]),
1028                 0,
1029                 stayClasses(clShallow, 1),
1030                 60 * scaleByMapSize(13, 200),
1031                 80);
1033         log("Creating bushes...");
1034         createObjectGroupsDeprecated(
1035                 new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]),
1036                 0,
1037                 [avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)],
1038                 planetm * scaleByMapSize(13, 200),
1039                 50);
1041         setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"]));
1042         setSunRotation(randFloat(0, 2 * Math.PI));
1043         setSunElevation(Math.PI * randFloat(1/5, 1/3));
1046 function createUnknownPlayerBases()
1048         for (var i = 0; i < numPlayers; ++i)
1049         {
1050                 var id = playerIDs[i];
1051                 log("Creating base for player " + id + "...");
1053                 var radius = scaleByMapSize(17,29);
1054                 var shoreRadius = 4;
1055                 var elevation = 3;
1057                 var hillSize = PI * radius * radius;
1058                 // get the x and z in tiles
1059                 var fx = fractionToTiles(playerX[i]);
1060                 var fz = fractionToTiles(playerZ[i]);
1061                 var ix = round(fx);
1062                 var iz = round(fz);
1064                 placeCivDefaultEntities(fx, fz, id, { "iberWall": g_IberianWalls });
1066                 placeDefaultChicken(fx, fz, clBaseResource);
1068                 // create berry bushes
1069                 var bbAngle = randFloat(0, TWO_PI);
1070                 var bbDist = 12;
1071                 var bbX = round(fx + bbDist * cos(bbAngle));
1072                 var bbZ = round(fz + bbDist * sin(bbAngle));
1073                 var group = new SimpleGroup(
1074                         [new SimpleObject(oFruitBush, 5,5, 0,3)],
1075                         true, clBaseResource, bbX, bbZ
1076                 );
1077                 createObjectGroup(group, 0);
1079                 if (g_StartingTreasures)
1080                 {
1081                         // create woods
1082                         var bbAngle = randFloat(0, TWO_PI);
1083                         var bbDist = 13;
1084                         var bbX = round(fx + bbDist * cos(bbAngle));
1085                         var bbZ = round(fz + bbDist * sin(bbAngle));
1086                         group = new SimpleGroup(
1087                                 [new SimpleObject(oWoodTreasure, 14, 14, 0, 3)],
1088                                 true, clBaseResource, bbX, bbZ
1089                         );
1090                         createObjectGroup(group, 0);
1091                 }
1093                 // create metal mine
1094                 var mAngle = bbAngle;
1095                 while (abs(mAngle - bbAngle) < PI/3)
1096                 {
1097                         mAngle = randFloat(0, TWO_PI);
1098                 }
1099                 var mDist = 12;
1100                 var mX = round(fx + mDist * cos(mAngle));
1101                 var mZ = round(fz + mDist * sin(mAngle));
1102                 group = new SimpleGroup(
1103                         [new SimpleObject(oMetalLarge, 1,1, 0,0)],
1104                         true, clBaseResource, mX, mZ
1105                 );
1106                 createObjectGroup(group, 0);
1108                 // create stone mines
1109                 mAngle += randFloat(PI/8, PI/4);
1110                 mX = round(fx + mDist * cos(mAngle));
1111                 mZ = round(fz + mDist * sin(mAngle));
1112                 group = new SimpleGroup(
1113                         [new SimpleObject(oStoneLarge, 1,1, 0,2)],
1114                         true, clBaseResource, mX, mZ
1115                 );
1116                 createObjectGroup(group, 0);
1117                 var hillSize = PI * radius * radius;
1118                 // create starting trees
1119                 var num = floor(hillSize / 100);
1120                 var tAngle = randFloat(-PI/3, 4*PI/3);
1121                 var tDist = randFloat(11, 13);
1122                 var tX = round(fx + tDist * cos(tAngle));
1123                 var tZ = round(fz + tDist * sin(tAngle));
1124                 group = new SimpleGroup(
1125                         [new SimpleObject(oTree1, num, num, 0,5)],
1126                         false, clBaseResource, tX, tZ
1127                 );
1128                 createObjectGroup(group, 0, avoidClasses(clBaseResource,2));
1130                 placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius);
1132                 // create the city patch
1133                 var cityRadius = radius/3;
1134                 var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz);
1135                 var painter = new LayeredPainter([tRoadWild, tRoad], [1]);
1136                 createArea(placer, [painter, paintClass(clPlayer)], null);
1137         }