Fix some unintended obliteration in rP20420.
[0ad.git] / binaries / data / mods / public / maps / random / the_unknown / unknown_common.js
blob03654581abf8d77b20eecca486386fa155d75bf3
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 = primeSortAllPlayers();
321                 let playerPos = placePlayersRiver();
323                 for (let i = 0; i < numPlayers; ++i)
324                 {
325                         playerX[i] = playerPos[i];
326                         playerZ[i] = 0.2 + 0.6 * (i % 2);
327                 }
329                 if (!horizontal)
330                         [playerX, playerZ] = [playerZ, playerX];
331                 markPlayerArea("small");
332         }
334         if (!g_AllowNaval || randBool())
335         {
336                 log("Creating isthmus (i.e. connecting the two riversides with a big land passage)...");
337                 let [coord1, coord2] = centralRiverCoordinates(!horizontal);
338                 createArea(
339                         new PathPlacer(
340                                 ...coord1,
341                                 ...coord2,
342                                 scaleByMapSize(randIntInclusive(16, 24),
343                                 randIntInclusive(100, 140)),
344                                 0.5,
345                                 3 * scaleByMapSize(1, 4),
346                                 0.1,
347                                 0.01),
348                         [
349                                 landElevationPainter,
350                                 paintClass(clLand),
351                                 unPaintClass(clWater)
352                         ],
353                         null);
354         }
356         createExtensionsOrIslands();
357         // Don't createShoreJaggedness since it doesn't fit artistically here
361  * Creates a very small central river.
362  */
363 function unknownCentralRiver()
365         let waterHeight = -4;
367         initHeight(landHeight);
369         let horizontal = randBool();
371         if (g_PlayerBases)
372         {
373                 playerIDs = primeSortAllPlayers();
374                 let playerPos = placePlayersRiver();
376                 for (let i = 0; i < numPlayers; ++i)
377                 {
378                         playerX[i] = playerPos[i];
379                         playerZ[i] = 0.25 + 0.5*(i%2);
380                 }
382                 if (!horizontal)
383                         [playerX, playerZ] = [playerZ, playerX];
385                 markPlayerArea("large");
386         }
388         log("Creating the main river...");
389         let [coord1, coord2] = centralRiverCoordinates(horizontal);
390         createArea(
391                 new PathPlacer(...coord1, ...coord2, scaleByMapSize(14, 24), 0.5, scaleByMapSize(3, 12), 0.1, 0.01),
392                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
393                 avoidClasses(clPlayerTerritory, 4));
395         log("Creating small water spots at the map border to ensure separation of players...");
396         for (let coord of [coord1, coord2])
397                 createArea(
398                         new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(5, 10))), 0.95, 0.6, 10, ...coord),
399                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 2),
400                         avoidClasses(clPlayerTerritory, 8));
402         if (!g_AllowNaval || randBool())
403         {
404                 log("Creating the shallows of the main river...");
405                 for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i)
406                 {
407                         let cLocation = randFloat(0.15, 0.85);
408                         let x1 = [fractionToTiles(cLocation), fractionToTiles(0.35)];
409                         let x2 = [fractionToTiles(cLocation), fractionToTiles(0.65)];
410                         if (!horizontal)
411                         {
412                                 x1.reverse();
413                                 x2.reverse();
414                         }
415                         createShallowsPassage(...x1, ...x2, scaleByMapSize(4, 8), -2, -2, 2, clShallow, undefined, waterHeight);
416                 }
417         }
419         if (randBool(2/3))
420                 createTributaryRivers(
421                         horizontal,
422                         randIntInclusive(8, scaleByMapSize(12, 16)),
423                         scaleByMapSize(10, 20),
424                         -4,
425                         [-6, -1.5],
426                         Math.PI / 5,
427                         clWater,
428                         clShallow,
429                         avoidClasses(clPlayerTerritory, 3));
433  * Creates a circular lake in the middle and possibly a river between each player ("pizza slices").
434  */
435 function unknownRiversAndLake()
437         let waterHeight = -4;
438         initHeight(landHeight);
440         let startAngle;
441         if (g_PlayerBases)
442         {
443                 let playerAngle;
444                 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
445                 markPlayerArea("small");
446         }
448         let mid = Math.round(fractionToTiles(0.5));
449         let lake = randBool(3/4);
450         if (lake)
451         {
452                 log("Creating lake...");
453                 createArea(
454                         new ClumpPlacer(mapArea * 0.09 * lSize, 0.7, 0.1, 10, mid, mid),
455                         [
456                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
457                                 paintClass(clWater)
458                         ],
459                         null);
461                 createShoreJaggedness(waterHeight, clWater, 3);
462         }
464         // Don't do this on nomad because the imbalances on the different islands are too drastic
465         if (g_PlayerBases && (!lake || randBool(1/3)))
466         {
467                 log("Creating small rivers separating players...");
468                 let [riverX, riverZ, riverAngle] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, 0.5, 0.5, 0.5);
470                 for (let i = 0; i < numPlayers; ++i)
471                 {
472                         createArea(
473                                 new PathPlacer(mid, mid, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i]), scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
474                                 [
475                                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
476                                         paintClass(clWater)
477                                 ],
478                                 avoidClasses(clPlayer, 5));
480                         createArea(
481                                 new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i])),
482                                 [
483                                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0),
484                                         paintClass(clWater)
485                                 ],
486                                 avoidClasses(clPlayer, 5));
487                 }
489                 log("Creating lake...");
490                 createArea(
491                         new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid),
492                         [
493                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
494                                 paintClass(clWater)
495                         ],
496                         null);
497         }
499         if (lake && randBool())
500         {
501                 log("Creating small central island...");
502                 createArea(
503                         new ClumpPlacer(mapArea * 0.006 * lSize, 0.7, 0.1, 10, mid, mid),
504                         [
505                                 landElevationPainter,
506                                 paintClass(clWater)
507                         ],
508                         null);
509         }
513  * Align players on a land strip with seas bordering on one or both sides that can hold islands.
514  */
515 function unknownEdgeSeas()
517         let waterHeight = -4;
518         initHeight(landHeight);
520         let horizontal = randBool();
521         if (g_PlayerBases)
522         {
523                 for (let i = 0; i < numPlayers; i++)
524                 {
525                         let playerPos1 = (i + 1) / (numPlayers + 1);
526                         let playerPos2 = 0.4 + 0.2 * (i % 2);
528                         playerX[i] = horizontal ? playerPos1 : playerPos2;
529                         playerZ[i] = horizontal ? playerPos2 : playerPos1;
530                 }
531                 // Don't place the shoreline inside the CC, but possibly into the players territory
532                 markPlayerArea("small");
533         }
535         for (let location of pickRandom([["first"], ["second"], ["first", "second"]]))
536         {
537                 let positionX = location == "first" ? [0, 0] : [1, 1];
538                 let positionZ = [0, 1];
540                 if (horizontal)
541                         [positionX, positionZ] = [positionZ, positionX];
543                 paintRiver({
544                         "parallel": true,
545                         "startX": positionX[0],
546                         "startZ": positionZ[0],
547                         "endX": positionX[1],
548                         "endZ": positionZ[1],
549                         "width": 0.62 - randFloat(0, scaleByMapSize(0, 0.1)),
550                         "fadeDist": 0.015,
551                         "deviation": 0,
552                         "waterHeight": waterHeight,
553                         "landHeight": landHeight,
554                         "meanderShort": 20,
555                         "meanderLong": 0
556                 });
557         }
559         createExtensionsOrIslands();
560         paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand);
561         createShoreJaggedness(waterHeight, clLand, 7, false);
565  * Land shaped like a concrescent moon around a central lake.
566  */
567 function unknownGulf()
569         let waterHeight = -3;
570         initHeight(landHeight);
572         let startAngle = randFloat(0, 2) * Math.PI;
573         if (g_PlayerBases)
574         {
575                 log("Determining player locations...");
576                 for (let i = 0; i < numPlayers; ++i)
577                 {
578                         let playerAngle = startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1)));
579                         playerX[i] = 0.5 + 0.35 * Math.cos(playerAngle);
580                         playerZ[i] = 0.5 + 0.35 * Math.sin(playerAngle);
581                 }
582                 markPlayerArea("large");
583         }
585         let placers = [
586                 new ClumpPlacer(mapArea * 0.08, 0.7, 0.05, 10, Math.round(fractionToTiles(0.5)), Math.round(fractionToTiles(0.5))),
587                 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)))),
588                 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)))),
589         ];
591         for (let placer of placers)
592                 createArea(
593                         placer,
594                         [
595                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
596                                 paintClass(clWater)
597                         ],
598                         avoidClasses(clPlayerTerritory, scaleByMapSize(15, 25)));
602  * Mainland style with some small random lakes.
603  */
604 function unknownLakes()
606         let waterHeight = -5;
608         initHeight(landHeight);
610         if (g_PlayerBases)
611         {
612                 [playerIDs, playerX, playerZ] = radialPlayerPlacement();
613                 markPlayerArea("large");
614         }
616         log("Creating lakes...");
617         createAreas(
618                 new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, 1),
619                 [
620                         new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5),
621                         paintClass(clWater)
622                 ],
623                 [avoidClasses(clPlayerTerritory, 12), randBool() ? avoidClasses(clWater, 8) : new NullConstraint()],
624                 scaleByMapSize(5, 16));
628  * A large hill leaving players only a small passage to each of the the two neighboring players.
629  */
630 function unknownPasses()
632         let mountainHeight = 24;
633         let waterHeight = -4;
634         initHeight(landHeight);
636         let playerAngle;
637         let startAngle;
638         if (g_PlayerBases)
639         {
640                 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
641                 markPlayerArea("small");
642         }
643         else
644                 startAngle = Math.random(0, 2 * Math.PI);
646         let mid = Math.round(fractionToTiles(0.5));
647         let [mountainX, mountainZ] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mid, mid);
648         let [passesX, passesZ] = distributePointsOnCircle(numPlayers * 2, startAngle, fractionToTiles(0.35), mid, mid);
650         for (let i = 0; i < numPlayers; ++i)
651         {
652                 log("Creating a mountain range between neighboring players...");
653                 createArea(
654                         new PathPlacer(mid, mid, mountainX[i], mountainZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
655                         [
656                                 // More smoothing than this often results in the mountainrange becoming passable to one player.
657                                 new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 1),
658                                 paintClass(clWater)
659                         ],
660                         avoidClasses(clPlayer, 5));
662                 log("Creating small mountain at the map border between the players to ensure separation of players...");
663                 createArea(
664                         new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, mountainX[i], mountainZ[i]),
665                         new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 0),
666                         avoidClasses(clPlayer, 5));
668                 log("Create passages between neighboring players...");
669                 createArea(
670                         new PathPlacer(
671                                 passesX[2 * i],
672                                 passesZ[2 * i],
673                                 passesX[2 * ((i + 1) % numPlayers)],
674                                 passesZ[2 * ((i + 1) % numPlayers)],
675                                 scaleByMapSize(14, 24),
676                                 0.4,
677                                 3 * scaleByMapSize(1, 3),
678                                 0.2,
679                                 0.05),
680                         new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
681                         null);
682         }
684         if (randBool(2/5))
685         {
686                 log("Create central lake...");
687                 createArea(
688                         new ClumpPlacer(mapArea * 0.03 * lSize, 0.7, 0.1, 10, mid, mid),
689                         [
690                                 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3),
691                                 paintClass(clWater)
692                         ],
693                         null);
694         }
695         else
696         {
697                 log("Fill area between the paths...");
698                 createArea(
699                         new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid),
700                         [
701                                 new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 4),
702                                 paintClass(clWater)
703                         ],
704                         null);
705         }
709  * Land enclosed by a hill that leaves small areas for civic centers and large central place.
710  */
711 function unknownLowlands()
713         let mountainHeight = 30;
715         log("Creating mountain that is going to separate players...");
716         initHeight(mountainHeight);
718         let playerAngle;
719         let startAngle;
720         if (g_PlayerBases)
721         {
722                 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
723                 markPlayerArea("small");
724         }
725         else
726                 startAngle = Math.random(0, 2 * Math.PI);
728         log("Creating valleys enclosed by the mountain...");
729         let valleys = numPlayers;
730         if (mapSize <= 128 && numPlayers <= 2 ||
731             mapSize <= 192 && numPlayers <= 3 ||
732             mapSize <= 320 && numPlayers <= 4 ||
733             mapSize <= 384 && numPlayers <= 5 ||
734             mapSize <= 448 && numPlayers <= 6)
735                 valleys *= 2;
737         let mid = Math.round(fractionToTiles(0.5));
738         let [valleyX, valleyZ] = distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mid, mid);
739         for (let i = 0; i < valleys; ++i)
740                 createArea(
741                         new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, 10, valleyX[i], valleyZ[i]),
742                         [
743                                 new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
744                                 paintClass(clLand)
745                         ],
746                         null);
748         log("Creating the big central area...");
749         createArea(
750                 new ClumpPlacer(mapArea * 0.091 * lSize, 0.7, 0.1, 10, mid, mid),
751                 [
752                         landElevationPainter,
753                         paintClass(clWater)
754                 ],
755                 null);
757         log("Creating passes from player areas to the center...");
758         for (let i = 0; i < valleys; ++i)
759                 createArea(
760                         new PathPlacer(mid, mid, valleyX[i], valleyZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
761                         [
762                                 landElevationPainter,
763                                 paintClass(clWater)
764                         ],
765                         null);
769  * No water, no hills.
770  */
771 function unknownMainland()
773         initHeight(3);
775         if (g_PlayerBases)
776         {
777                 [playerIDs, playerX, playerZ] = radialPlayerPlacement();
778                 markPlayerArea("small");
779         }
782 function centralRiverCoordinates(horizontal)
784         let coord1 = [0, fractionToTiles(0.5)];
785         let coord2 = [fractionToTiles(1), fractionToTiles(0.5)];
787         if (!horizontal)
788         {
789                 coord1.reverse();
790                 coord2.reverse();
791         }
793         return [coord1, coord2];
796 function createShoreJaggedness(waterHeight, borderClass, shoreDist, inwards = true)
798         log("Creating shore jaggedness...");
799         for (let i = 0; i < 2; ++i)
800                 if (i || inwards)
801                         createAreas(
802                                 new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, 1),
803                                 [
804                                                 new SmoothElevationPainter(ELEVATION_SET, i ? landHeight : waterHeight, 4),
805                                                 i ? paintClass(clLand) : unPaintClass(clLand)
806                                 ],
807                                 [
808                                                 avoidClasses(clPlayer, 20, clPeninsulaSteam, 20),
809                                                 borderClasses(borderClass, shoreDist, shoreDist)
810                                 ],
811                                 scaleByMapSize(7, 130) * 2,
812                                 150);
815 function createExtensionsOrIslands()
817         let rnd = randIntInclusive(1, 3);
819         if (rnd == 1)
820         {
821                 log("Creating islands...");
822                 createAreas(
823                         new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)),
824                         [
825                                 landElevationPainter,
826                                 paintClass(clLand)
827                         ],
828                         avoidClasses(clLand, 3, clPlayer, 3),
829                         scaleByMapSize(2, 5) * randIntInclusive(8, 14));
830         }
831         else if (rnd == 2)
832         {
833                 log("Creating extentions...");
834                 createAreas(
835                         new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07),
836                         [
837                                 landElevationPainter,
838                                 paintClass(clLand)
839                         ],
840                         null,
841                         scaleByMapSize(2, 5) * randIntInclusive(8, 14));
842         }
846  * Prevent impassable terrain and resource collisions at the the civic center and starting resources.
847  */
848 function markPlayerArea(size)
850         for (let i = 0; i < numPlayers; ++i)
851         {
852                 addCivicCenterAreaToClass(
853                         Math.round(fractionToTiles(playerX[i])),
854                         Math.round(fractionToTiles(playerZ[i])),
855                         clPlayer);
857                 if (size == "large")
858                         createArea(
859                                 new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, fractionToTiles(playerX[i]), fractionToTiles(playerZ[i])),
860                                 paintClass(clPlayerTerritory),
861                                 null);
862         }
865 function paintUnknownMapBasedOnHeight()
867         paintTerrainBasedOnHeight(cliffHeight, 40, 1, tCliff);
868         paintTerrainBasedOnHeight(3, cliffHeight, 1, tMainTerrain);
869         paintTerrainBasedOnHeight(1, 3, 1, tShore);
870         paintTerrainBasedOnHeight(-8, 1, 2, tWater);
872         unPaintTileClassBasedOnHeight(0, cliffHeight, 1, clWater);
873         unPaintTileClassBasedOnHeight(-6, 0, 1, clLand);
875         paintTileClassBasedOnHeight(-6, 0, 1, clWater);
876         paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand);
877         paintTileClassBasedOnHeight(cliffHeight, 40, 1, clHill);
881  * Place resources and decoratives after the player territory was marked.
882  */
883 function createUnknownObjects()
885         log("Creating bumps...");
886         createAreas(
887                 new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, 1),
888                 new SmoothElevationPainter(ELEVATION_MODIFY, 2, 2),
889                 [avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 3)],
890                 randIntInclusive(0, scaleByMapSize(1, 2) * 200));
892         log("Creating hills...");
893         createAreas(
894                 new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1),
895                 [
896                         new LayeredPainter([tCliff, tHill], [2]),
897                         new SmoothElevationPainter(ELEVATION_SET, 18, 2),
898                         paintClass(clHill)
899                 ],
900                 [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)],
901                 randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9))
902         );
903         RMS.SetProgress(50);
905         log("Creating forests...");
906         let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1));
908         let types = [
909                 [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]],
910                 [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]]
911         ];
913         let size = numForest / (scaleByMapSize(2, 8) * numPlayers);
914         let num = Math.floor(size / types.length);
915         for (let type of types)
916                 createAreas(
917                         new ClumpPlacer(numForest / num, 0.1, 0.1, 1),
918                         [
919                                 new LayeredPainter(type, [2]),
920                                 paintClass(clForest)
921                         ],
922                         [avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 0), stayClasses(clLand, 4)],
923                         num);
924         RMS.SetProgress(50);
926         log("Creating dirt patches...");
927         let patchCount = (currentBiome() == "savanna" ? 3 : 1) * scaleByMapSize(15, 45);
928         for (let size of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)])
929                 createAreas(
930                         new ClumpPlacer(size, 0.3, 0.06, 0.5),
931                         [
932                                 new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]),
933                                 paintClass(clDirt)
934                         ],
935                         [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
936                         patchCount);
938         log("Creating grass patches...");
939         for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)])
940                 createAreas(
941                                 new ClumpPlacer(size, 0.3, 0.06, 0.5),
942                         new TerrainPainter(tTier4Terrain),
943                         [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
944                         patchCount);
946         RMS.SetProgress(55);
948         log("Creating stone mines...");
949         createObjectGroupsDeprecated(
950                 new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4), new SimpleObject(oStoneLarge, 1, 1, 0, 4)], true, clRock),
951                 0,
952                 [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 3)],
953                 randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
954                 100);
956         log("Creating small stone quarries...");
957         createObjectGroupsDeprecated(
958                 new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock),
959                 0,
960                 [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 3)],
961                 randIntInclusive(scaleByMapSize(2, 9),scaleByMapSize(9, 40)),
962                 100);
964         log("Creating metal mines...");
965         createObjectGroupsDeprecated(
966                 new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal),
967                 0,
968                 [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 1), stayClasses(clLand, 3)],
969                 randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
970                 100);
971         RMS.SetProgress(65);
973         log("Creating small decorative rocks...");
974         createObjectGroupsDeprecated(
975                 new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true),
976                 0,
977                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 3)],
978                 scaleByMapSize(16, 262),
979                 50);
981         log("Creating large decorative rocks...");
982         createObjectGroupsDeprecated(
983                 new SimpleGroup([new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], true),
984                 0,
985                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 3)],
986                 scaleByMapSize(8, 131),
987                 50);
988         RMS.SetProgress(70);
990         log("Creating deer...");
991         createObjectGroupsDeprecated(
992                 new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood),
993                 0,
994                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
995                 randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
996                 50);
998         log("Creating berry bush...");
999         createObjectGroupsDeprecated(
1000                 new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood),
1001                 0,
1002                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
1003                 randIntInclusive(1, 4) * numPlayers + 2,
1004                 50);
1005         RMS.SetProgress(75);
1007         log("Creating sheep...");
1008         createObjectGroupsDeprecated(
1009                 new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood),
1010                 0,
1011                 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
1012                 randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
1013                 50);
1015         log("Creating fish...");
1016         createObjectGroupsDeprecated(
1017                 new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood),
1018                 0,
1019                 avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20),
1020                 randIntInclusive(15, 40) * numPlayers,
1021                 60);
1022         RMS.SetProgress(85);
1024         log("Creating straggler trees...");
1025         types = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
1027         num = Math.floor(numStragglers / types.length);
1028         for (let type of types)
1029                 createObjectGroupsDeprecated(
1030                         new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest),
1031                         0,
1032                         [avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)],
1033                         num);
1035         let planetm = currentBiome() == "tropic" ? 8 : 1;
1037         log("Creating small grass tufts...");
1038         createObjectGroupsDeprecated(
1039                 new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]),
1040                 0,
1041                 [avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)],
1042                 planetm * scaleByMapSize(13, 200));
1043         RMS.SetProgress(90);
1045         log("Creating large grass tufts...");
1046         createObjectGroupsDeprecated(
1047                 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)]),
1048                 0,
1049                 [avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 3)],
1050                 planetm * scaleByMapSize(13, 200));
1051         RMS.SetProgress(95);
1053         log("Creating shallow flora...");
1054         createObjectGroupsDeprecated(
1055                 new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 2), new SimpleObject(aReeds, 2, 4, 0, 2)]),
1056                 0,
1057                 stayClasses(clShallow, 1),
1058                 60 * scaleByMapSize(13, 200),
1059                 80);
1061         log("Creating bushes...");
1062         createObjectGroupsDeprecated(
1063                 new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]),
1064                 0,
1065                 [avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)],
1066                 planetm * scaleByMapSize(13, 200),
1067                 50);
1069         setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"]));
1070         setSunRotation(randFloat(0, 2 * Math.PI));
1071         setSunElevation(Math.PI * randFloat(1/5, 1/3));
1074 function createUnknownPlayerBases()
1076         for (var i = 0; i < numPlayers; ++i)
1077         {
1078                 var id = playerIDs[i];
1079                 log("Creating base for player " + id + "...");
1081                 var radius = scaleByMapSize(17,29);
1082                 var shoreRadius = 4;
1083                 var elevation = 3;
1085                 var hillSize = PI * radius * radius;
1086                 // get the x and z in tiles
1087                 var fx = fractionToTiles(playerX[i]);
1088                 var fz = fractionToTiles(playerZ[i]);
1089                 var ix = round(fx);
1090                 var iz = round(fz);
1092                 placeCivDefaultEntities(fx, fz, id, { "iberWall": g_IberianWalls });
1094                 placeDefaultChicken(fx, fz, clBaseResource);
1096                 // create berry bushes
1097                 var bbAngle = randFloat(0, TWO_PI);
1098                 var bbDist = 12;
1099                 var bbX = round(fx + bbDist * cos(bbAngle));
1100                 var bbZ = round(fz + bbDist * sin(bbAngle));
1101                 var group = new SimpleGroup(
1102                         [new SimpleObject(oFruitBush, 5,5, 0,3)],
1103                         true, clBaseResource, bbX, bbZ
1104                 );
1105                 createObjectGroup(group, 0);
1107                 if (g_StartingTreasures)
1108                 {
1109                         // create woods
1110                         var bbAngle = randFloat(0, TWO_PI);
1111                         var bbDist = 13;
1112                         var bbX = round(fx + bbDist * cos(bbAngle));
1113                         var bbZ = round(fz + bbDist * sin(bbAngle));
1114                         group = new SimpleGroup(
1115                                 [new SimpleObject(oWoodTreasure, 14, 14, 0, 3)],
1116                                 true, clBaseResource, bbX, bbZ
1117                         );
1118                         createObjectGroup(group, 0);
1119                 }
1121                 // create metal mine
1122                 var mAngle = bbAngle;
1123                 while (abs(mAngle - bbAngle) < PI/3)
1124                 {
1125                         mAngle = randFloat(0, TWO_PI);
1126                 }
1127                 var mDist = 12;
1128                 var mX = round(fx + mDist * cos(mAngle));
1129                 var mZ = round(fz + mDist * sin(mAngle));
1130                 group = new SimpleGroup(
1131                         [new SimpleObject(oMetalLarge, 1,1, 0,0)],
1132                         true, clBaseResource, mX, mZ
1133                 );
1134                 createObjectGroup(group, 0);
1136                 // create stone mines
1137                 mAngle += randFloat(PI/8, PI/4);
1138                 mX = round(fx + mDist * cos(mAngle));
1139                 mZ = round(fz + mDist * sin(mAngle));
1140                 group = new SimpleGroup(
1141                         [new SimpleObject(oStoneLarge, 1,1, 0,2)],
1142                         true, clBaseResource, mX, mZ
1143                 );
1144                 createObjectGroup(group, 0);
1145                 var hillSize = PI * radius * radius;
1146                 // create starting trees
1147                 var num = floor(hillSize / 100);
1148                 var tAngle = randFloat(-PI/3, 4*PI/3);
1149                 var tDist = randFloat(11, 13);
1150                 var tX = round(fx + tDist * cos(tAngle));
1151                 var tZ = round(fz + tDist * sin(tAngle));
1152                 group = new SimpleGroup(
1153                         [new SimpleObject(oTree1, num, num, 0,5)],
1154                         false, clBaseResource, tX, tZ
1155                 );
1156                 createObjectGroup(group, 0, avoidClasses(clBaseResource,2));
1158                 placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius);
1160                 // create the city patch
1161                 var cityRadius = radius/3;
1162                 var placer = new ClumpPlacer(PI*cityRadius*cityRadius, 0.6, 0.3, 10, ix, iz);
1163                 var painter = new LayeredPainter([tRoadWild, tRoad], [1]);
1164                 createArea(placer, [painter, paintClass(clPlayer)], null);
1165         }