2 * @file This library is used to generate different map variations on the map Unknown, Unknown Land and Unknown Nomad.
6 * True if city centers should be placed or false for nomad.
11 * True if all players should be connected via land and false if river or islands can split some if not all the players.
15 TILE_CENTERED_HEIGHT_MAP = true;
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];
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();
80 var cliffHeight = 3.12;
81 var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, landHeight, 4);
83 var unknownMapFunctions = {
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.
106 var playerIDs = sortAllPlayers();
110 var g_StartingTreasures = false;
111 var g_IberianWalls = "walls";
113 function createUnknownMap()
115 let funcs = unknownMapFunctions.land;
118 funcs = funcs.concat(unknownMapFunctions.naval);
120 global["unknown" + pickRandom(funcs)]();
122 paintUnknownMapBasedOnHeight();
125 createUnknownPlayerBases();
129 * Chain of islands or many disconnected islands.
131 function unknownArchipelago()
133 g_IberianWalls = "towers";
134 g_StartingTreasures = true;
136 let [pIDs, islandX, islandZ] = radialPlayerPlacement();
139 [playerIDs, playerX, playerZ] = [pIDs, islandX, islandZ];
140 markPlayerArea("large");
143 log("Creating islands...");
144 let islandSize = diskArea(scaleByMapSize(17, 29));
145 for (let i = 0; i < numPlayers; ++i)
147 new ClumpPlacer(islandSize, 0.8, 0.1, 10, fractionToTiles(islandX[i]), fractionToTiles(islandZ[i])),
148 landElevationPainter,
151 let type = randIntInclusive(1, 3);
154 log("Creating archipelago...");
156 new ClumpPlacer(Math.floor(islandSize * randFloat(0.8, 1.2)), 0.8, 0.1, 10),
158 landElevationPainter,
162 scaleByMapSize(2, 5) * randIntInclusive(8, 14));
164 log("Creating shore jaggedness with small puddles...");
166 new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, 1),
168 new SmoothElevationPainter(ELEVATION_SET, landHeight, 4),
171 borderClasses(clLand, 6, 3),
172 scaleByMapSize(12, 130) * 2,
177 log("Creating islands...");
179 new ClumpPlacer(Math.floor(islandSize * randFloat(0.6, 1.4)), 0.8, 0.1, randFloat(0.0, 0.2)),
181 landElevationPainter,
184 avoidClasses(clLand, 3, clPlayerTerritory, 3),
185 scaleByMapSize(6, 10) * randIntInclusive(8, 14));
187 log("Creating small islands...");
189 new ClumpPlacer(Math.floor(islandSize * randFloat(0.3, 0.7)), 0.8, 0.1, 0.07),
191 new SmoothElevationPainter(ELEVATION_SET, landHeight, 6),
194 avoidClasses(clLand, 3, clPlayerTerritory, 3),
195 scaleByMapSize(2, 6) * randIntInclusive(6, 15),
200 log("Creating tight islands...");
202 new ClumpPlacer(Math.floor(islandSize * randFloat(0.8, 1.2)), 0.8, 0.1, 10),
204 landElevationPainter,
207 avoidClasses(clLand, randIntInclusive(8, 16), clPlayerTerritory, 3),
208 scaleByMapSize(2, 5) * randIntInclusive(8, 14));
213 * Disk shaped mainland with water on the edge.
215 function unknownContinent()
217 let waterHeight = -5;
221 log("Ensuring player area...");
222 [playerIDs, playerX, playerZ] = radialPlayerPlacement(0.25);
223 markPlayerArea("small");
225 for (let i = 0; i < numPlayers; ++i)
229 Math.floor(scaleByMapSize(5, 9)),
230 Math.floor(scaleByMapSize(5, 20)),
232 Math.round(fractionToTiles(playerX[i])),
233 Math.round(fractionToTiles(playerZ[i])),
235 [Math.floor(scaleByMapSize(23, 50))]),
237 landElevationPainter,
243 log("Creating continent...");
245 new ClumpPlacer(mapArea * 0.45, 0.9, 0.09, 10, Math.round(fractionToTiles(0.5)), Math.round(fractionToTiles(0.5))),
247 landElevationPainter,
254 log("Creating peninsula (i.e. half the map not being surrounded by water)...");
255 let angle = randFloat(0, 2 * Math.PI);
262 Math.round(fractionToTiles(0.5 + 0.25 * Math.cos(angle))),
263 Math.round(fractionToTiles(0.5 + 0.25 * Math.sin(angle)))),
265 landElevationPainter,
270 log("Remembering to not paint shorelines into the peninsula...");
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),
283 createShoreJaggedness(waterHeight, clLand, 7);
287 * Creates a huge central river, possibly connecting the riversides with a narrow piece of land.
289 function unknownCentralSea()
291 let waterHeight = -3;
292 let horizontal = randBool();
294 let [start, end] = centralRiverCoordinates(horizontal);
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),
304 "waterHeight": waterHeight,
305 "landHeight": landHeight,
308 "waterFunc": (ix, iz, height, riverFraction) => {
310 addToClass(ix, iz, clWater);
312 "landFunc": (ix, iz, shoreDist1, shoreDist2) => {
313 setHeight(ix, iz, 3.1);
314 addToClass(ix, iz, clLand);
320 playerIDs = primeSortAllPlayers();
321 let playerPos = placePlayersRiver();
323 for (let i = 0; i < numPlayers; ++i)
325 playerX[i] = playerPos[i];
326 playerZ[i] = 0.2 + 0.6 * (i % 2);
330 [playerX, playerZ] = [playerZ, playerX];
331 markPlayerArea("small");
334 if (!g_AllowNaval || randBool())
336 log("Creating isthmus (i.e. connecting the two riversides with a big land passage)...");
337 let [coord1, coord2] = centralRiverCoordinates(!horizontal);
342 scaleByMapSize(randIntInclusive(16, 24),
343 randIntInclusive(100, 140)),
345 3 * scaleByMapSize(1, 4),
349 landElevationPainter,
351 unPaintClass(clWater)
356 createExtensionsOrIslands();
357 // Don't createShoreJaggedness since it doesn't fit artistically here
361 * Creates a very small central river.
363 function unknownCentralRiver()
365 let waterHeight = -4;
367 initHeight(landHeight);
369 let horizontal = randBool();
373 playerIDs = primeSortAllPlayers();
374 let playerPos = placePlayersRiver();
376 for (let i = 0; i < numPlayers; ++i)
378 playerX[i] = playerPos[i];
379 playerZ[i] = 0.25 + 0.5*(i%2);
383 [playerX, playerZ] = [playerZ, playerX];
385 markPlayerArea("large");
388 log("Creating the main river...");
389 let [coord1, coord2] = centralRiverCoordinates(horizontal);
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])
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())
404 log("Creating the shallows of the main river...");
405 for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i)
407 let cLocation = randFloat(0.15, 0.85);
408 let x1 = [fractionToTiles(cLocation), fractionToTiles(0.35)];
409 let x2 = [fractionToTiles(cLocation), fractionToTiles(0.65)];
415 createShallowsPassage(...x1, ...x2, scaleByMapSize(4, 8), -2, -2, 2, clShallow, undefined, waterHeight);
420 createTributaryRivers(
422 randIntInclusive(8, scaleByMapSize(12, 16)),
423 scaleByMapSize(10, 20),
429 avoidClasses(clPlayerTerritory, 3));
433 * Creates a circular lake in the middle and possibly a river between each player ("pizza slices").
435 function unknownRiversAndLake()
437 let waterHeight = -4;
438 initHeight(landHeight);
444 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
445 markPlayerArea("small");
448 let mid = Math.round(fractionToTiles(0.5));
449 let lake = randBool(3/4);
452 log("Creating lake...");
454 new ClumpPlacer(mapArea * 0.09 * lSize, 0.7, 0.1, 10, mid, mid),
456 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
461 createShoreJaggedness(waterHeight, clWater, 3);
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)))
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)
473 new PathPlacer(mid, mid, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i]), scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
475 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
478 avoidClasses(clPlayer, 5));
481 new ClumpPlacer(Math.floor(diskArea(scaleByMapSize(10, 50)) / 5), 0.95, 0.6, 10, fractionToTiles(riverX[i]), fractionToTiles(riverZ[i])),
483 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0),
486 avoidClasses(clPlayer, 5));
489 log("Creating lake...");
491 new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid),
493 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
499 if (lake && randBool())
501 log("Creating small central island...");
503 new ClumpPlacer(mapArea * 0.006 * lSize, 0.7, 0.1, 10, mid, mid),
505 landElevationPainter,
513 * Align players on a land strip with seas bordering on one or both sides that can hold islands.
515 function unknownEdgeSeas()
517 let waterHeight = -4;
518 initHeight(landHeight);
520 let horizontal = randBool();
523 for (let i = 0; i < numPlayers; i++)
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;
531 // Don't place the shoreline inside the CC, but possibly into the players territory
532 markPlayerArea("small");
535 for (let location of pickRandom([["first"], ["second"], ["first", "second"]]))
537 let positionX = location == "first" ? [0, 0] : [1, 1];
538 let positionZ = [0, 1];
541 [positionX, positionZ] = [positionZ, positionX];
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)),
552 "waterHeight": waterHeight,
553 "landHeight": landHeight,
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.
567 function unknownGulf()
569 let waterHeight = -3;
570 initHeight(landHeight);
572 let startAngle = randFloat(0, 2) * Math.PI;
575 log("Determining player locations...");
576 for (let i = 0; i < numPlayers; ++i)
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);
582 markPlayerArea("large");
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)))),
591 for (let placer of placers)
595 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
598 avoidClasses(clPlayerTerritory, scaleByMapSize(15, 25)));
602 * Mainland style with some small random lakes.
604 function unknownLakes()
606 let waterHeight = -5;
608 initHeight(landHeight);
612 [playerIDs, playerX, playerZ] = radialPlayerPlacement();
613 markPlayerArea("large");
616 log("Creating lakes...");
618 new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, 1),
620 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5),
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.
630 function unknownPasses()
632 let mountainHeight = 24;
633 let waterHeight = -4;
634 initHeight(landHeight);
640 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
641 markPlayerArea("small");
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)
652 log("Creating a mountain range between neighboring players...");
654 new PathPlacer(mid, mid, mountainX[i], mountainZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
656 // More smoothing than this often results in the mountainrange becoming passable to one player.
657 new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 1),
660 avoidClasses(clPlayer, 5));
662 log("Creating small mountain at the map border between the players to ensure separation of players...");
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...");
673 passesX[2 * ((i + 1) % numPlayers)],
674 passesZ[2 * ((i + 1) % numPlayers)],
675 scaleByMapSize(14, 24),
677 3 * scaleByMapSize(1, 3),
680 new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
686 log("Create central lake...");
688 new ClumpPlacer(mapArea * 0.03 * lSize, 0.7, 0.1, 10, mid, mid),
690 new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3),
697 log("Fill area between the paths...");
699 new ClumpPlacer(mapArea * 0.005, 0.7, 0.1, 10, mid, mid),
701 new SmoothElevationPainter(ELEVATION_SET, mountainHeight, 4),
709 * Land enclosed by a hill that leaves small areas for civic centers and large central place.
711 function unknownLowlands()
713 let mountainHeight = 30;
715 log("Creating mountain that is going to separate players...");
716 initHeight(mountainHeight);
722 [playerIDs, playerX, playerZ, playerAngle, startAngle] = radialPlayerPlacement();
723 markPlayerArea("small");
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)
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)
741 new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, 10, valleyX[i], valleyZ[i]),
743 new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
748 log("Creating the big central area...");
750 new ClumpPlacer(mapArea * 0.091 * lSize, 0.7, 0.1, 10, mid, mid),
752 landElevationPainter,
757 log("Creating passes from player areas to the center...");
758 for (let i = 0; i < valleys; ++i)
760 new PathPlacer(mid, mid, valleyX[i], valleyZ[i], scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
762 landElevationPainter,
769 * No water, no hills.
771 function unknownMainland()
777 [playerIDs, playerX, playerZ] = radialPlayerPlacement();
778 markPlayerArea("small");
782 function centralRiverCoordinates(horizontal)
784 let coord1 = [0, fractionToTiles(0.5)];
785 let coord2 = [fractionToTiles(1), fractionToTiles(0.5)];
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)
802 new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, 1),
804 new SmoothElevationPainter(ELEVATION_SET, i ? landHeight : waterHeight, 4),
805 i ? paintClass(clLand) : unPaintClass(clLand)
808 avoidClasses(clPlayer, 20, clPeninsulaSteam, 20),
809 borderClasses(borderClass, shoreDist, shoreDist)
811 scaleByMapSize(7, 130) * 2,
815 function createExtensionsOrIslands()
817 let rnd = randIntInclusive(1, 3);
821 log("Creating islands...");
823 new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)),
825 landElevationPainter,
828 avoidClasses(clLand, 3, clPlayer, 3),
829 scaleByMapSize(2, 5) * randIntInclusive(8, 14));
833 log("Creating extentions...");
835 new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07),
837 landElevationPainter,
841 scaleByMapSize(2, 5) * randIntInclusive(8, 14));
846 * Prevent impassable terrain and resource collisions at the the civic center and starting resources.
848 function markPlayerArea(size)
850 for (let i = 0; i < numPlayers; ++i)
852 addCivicCenterAreaToClass(
853 Math.round(fractionToTiles(playerX[i])),
854 Math.round(fractionToTiles(playerZ[i])),
859 new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, fractionToTiles(playerX[i]), fractionToTiles(playerZ[i])),
860 paintClass(clPlayerTerritory),
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.
883 function createUnknownObjects()
885 log("Creating bumps...");
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...");
894 new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, 1),
896 new LayeredPainter([tCliff, tHill], [2]),
897 new SmoothElevationPainter(ELEVATION_SET, 18, 2),
900 [avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)],
901 randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9))
905 log("Creating forests...");
906 let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1));
909 [[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]],
910 [[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]]
913 let size = numForest / (scaleByMapSize(2, 8) * numPlayers);
914 let num = Math.floor(size / types.length);
915 for (let type of types)
917 new ClumpPlacer(numForest / num, 0.1, 0.1, 1),
919 new LayeredPainter(type, [2]),
922 [avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 0), stayClasses(clLand, 4)],
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)])
930 new ClumpPlacer(size, 0.3, 0.06, 0.5),
932 new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]),
935 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
938 log("Creating grass patches...");
939 for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)])
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)],
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),
952 [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 3)],
953 randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
956 log("Creating small stone quarries...");
957 createObjectGroupsDeprecated(
958 new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock),
960 [avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 1), stayClasses(clLand, 3)],
961 randIntInclusive(scaleByMapSize(2, 9),scaleByMapSize(9, 40)),
964 log("Creating metal mines...");
965 createObjectGroupsDeprecated(
966 new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal),
968 [avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 1), stayClasses(clLand, 3)],
969 randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
973 log("Creating small decorative rocks...");
974 createObjectGroupsDeprecated(
975 new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true),
977 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 3)],
978 scaleByMapSize(16, 262),
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),
985 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 0), stayClasses(clLand, 3)],
986 scaleByMapSize(8, 131),
990 log("Creating deer...");
991 createObjectGroupsDeprecated(
992 new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood),
994 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
995 randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
998 log("Creating berry bush...");
999 createObjectGroupsDeprecated(
1000 new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood),
1002 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
1003 randIntInclusive(1, 4) * numPlayers + 2,
1005 RMS.SetProgress(75);
1007 log("Creating sheep...");
1008 createObjectGroupsDeprecated(
1009 new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood),
1011 [avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 1, clFood, 20), stayClasses(clLand, 2)],
1012 randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
1015 log("Creating fish...");
1016 createObjectGroupsDeprecated(
1017 new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood),
1019 avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 0, clFood, 20),
1020 randIntInclusive(15, 40) * numPlayers,
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),
1032 [avoidClasses(clWater, 1, clForest, 1, clHill, 1, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)],
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)]),
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)]),
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)]),
1057 stayClasses(clShallow, 1),
1058 60 * scaleByMapSize(13, 200),
1061 log("Creating bushes...");
1062 createObjectGroupsDeprecated(
1063 new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]),
1065 [avoidClasses(clWater, 1, clHill, 1, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)],
1066 planetm * scaleByMapSize(13, 200),
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)
1078 var id = playerIDs[i];
1079 log("Creating base for player " + id + "...");
1081 var radius = scaleByMapSize(17,29);
1082 var shoreRadius = 4;
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]);
1092 placeCivDefaultEntities(fx, fz, id, { "iberWall": g_IberianWalls });
1094 placeDefaultChicken(fx, fz, clBaseResource);
1096 // create berry bushes
1097 var bbAngle = randFloat(0, TWO_PI);
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
1105 createObjectGroup(group, 0);
1107 if (g_StartingTreasures)
1110 var bbAngle = randFloat(0, TWO_PI);
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
1118 createObjectGroup(group, 0);
1121 // create metal mine
1122 var mAngle = bbAngle;
1123 while (abs(mAngle - bbAngle) < PI/3)
1125 mAngle = randFloat(0, TWO_PI);
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
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
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
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);