1 Engine.LoadLibrary("rmgen");
2 Engine.LoadLibrary("rmgen-common");
3 Engine.LoadLibrary("rmgen2");
4 Engine.LoadLibrary("rmbiome");
6 function* GenerateMap()
8 const g_InitialMineDistance = 14;
9 const g_InitialTrees = 50;
13 const tMainTerrain = g_Terrains.mainTerrain;
14 const tForestFloor1 = g_Terrains.forestFloor1;
15 const tForestFloor2 = g_Terrains.forestFloor2;
16 const tCliff = g_Terrains.cliff;
17 const tTier1Terrain = g_Terrains.tier1Terrain;
18 const tTier2Terrain = g_Terrains.tier2Terrain;
19 const tTier3Terrain = g_Terrains.tier3Terrain;
20 const tHill = g_Terrains.hill;
21 const tTier4Terrain = g_Terrains.tier4Terrain;
22 const tShore = g_Terrains.shore;
23 const tWater = g_Terrains.water;
25 const oTree1 = g_Gaia.tree1;
26 const oTree2 = g_Gaia.tree2;
27 const oTree3 = g_Gaia.tree3;
28 const oTree4 = g_Gaia.tree4;
29 const oTree5 = g_Gaia.tree5;
30 const oFruitBush = g_Gaia.fruitBush;
31 const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal;
32 const oFish = g_Gaia.fish;
33 const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal;
34 const oStoneLarge = g_Gaia.stoneLarge;
35 const oStoneSmall = g_Gaia.stoneSmall;
36 const oMetalLarge = g_Gaia.metalLarge;
37 const oWhale = "gaia/fauna_whale_humpback";
38 const oShipwreck = "gaia/treasure/shipwreck";
39 const oShipDebris = "gaia/treasure/shipwreck_debris";
40 const oObelisk = "structures/obelisk";
42 const aGrass = g_Decoratives.grass;
43 const aGrassShort = g_Decoratives.grassShort;
44 const aRockLarge = g_Decoratives.rockLarge;
45 const aRockMedium = g_Decoratives.rockMedium;
48 tForestFloor2 + TERRAIN_SEPARATOR + oTree1,
49 tForestFloor2 + TERRAIN_SEPARATOR + oTree2,
53 tForestFloor1 + TERRAIN_SEPARATOR + oTree4,
54 tForestFloor1 + TERRAIN_SEPARATOR + oTree5,
58 const heightSeaGround = -10;
60 const heightHill = 18;
62 globalThis.g_Map = new RandomMap(heightSeaGround, tWater);
64 const numPlayers = getNumPlayers();
65 const mapSize = g_Map.getSize();
66 const mapCenter = g_Map.getCenter();
68 const clPlayer = g_Map.createTileClass();
69 const clHill = g_Map.createTileClass();
70 const clForest = g_Map.createTileClass();
71 const clDirt = g_Map.createTileClass();
72 const clRock = g_Map.createTileClass();
73 const clMetal = g_Map.createTileClass();
74 const clFood = g_Map.createTileClass();
75 const clBaseResource = g_Map.createTileClass();
76 const clLand = g_Map.createTileClass();
78 const startAngle = randomAngle();
80 const teams = getTeamsArray();
81 const numTeams = teams.filter(team => team).length;
83 distributePointsOnCircle(numTeams, startAngle, fractionToTiles(0.3), mapCenter)[0];
84 const teamRadius = fractionToTiles(0.05);
88 g_Map.log("Creating player islands and bases");
90 for (let i = 0; i < teams.length; ++i)
92 if (!teams[i] || isNomad())
97 const [playerPosition, playerAngle] = distributePointsOnCircle(teams[i].length,
98 startAngle + 2 * Math.PI / teams[i].length, teamRadius, teamPosition[i]);
99 playerPosition.forEach(position => position.round());
101 for (let p = 0; p < teams[i].length; ++p)
103 addCivicCenterAreaToClass(playerPosition[p], clPlayer);
108 Math.floor(scaleByMapSize(5, 11)),
109 Math.floor(scaleByMapSize(60, 250)),
113 [defaultPlayerBaseRadius() * 3/4]),
115 new TerrainPainter(tMainTerrain),
116 new SmoothElevationPainter(ELEVATION_SET, heightLand, 2),
117 new TileClassPainter(clLand)
120 placeCivDefaultStartingEntities(playerPosition[p], teams[i][p], false);
123 const mineAngle = randFloat(-1, 1) * Math.PI / teams[i].length;
125 { "template": oMetalLarge, "angle": mineAngle },
126 { "template": oStoneLarge, "angle": mineAngle + Math.PI / 4 }
130 for (let p = 0; p < teams[i].length; ++p)
131 for (const mine of mines)
133 const position = Vector2D.add(playerPosition[p],
134 new Vector2D(g_InitialMineDistance, 0).rotate(-playerAngle[p] - mine.angle));
136 new SimpleGroup([new SimpleObject(mine.template, 1, 1, 0, 4)], true,
137 clBaseResource, position),
139 [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 5)]);
143 for (let p = 0; p < teams[i].length; ++p)
146 for (let x = 0; x < tries; ++x)
148 const tAngle = playerAngle[p] + randFloat(-1, 1) * 2 * Math.PI / teams[i].length;
150 Vector2D.add(playerPosition[p], new Vector2D(16, 0).rotate(-tAngle)).round();
151 if (createObjectGroup(
153 [new SimpleObject(oTree2, g_InitialTrees, g_InitialTrees, 0, 7)],
158 [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 4)]))
163 for (let p = 0; p < teams[i].length; ++p)
164 placePlayerBaseBerries({
165 "template": oFruitBush,
166 "playerID": teams[i][p],
167 "playerPosition": playerPosition[p],
168 "BaseResourceClass": clBaseResource,
169 "baseResourceConstraint":
170 new AndConstraint([avoidClasses(clPlayer, 4), stayClasses(clLand, 5)])
173 for (let p = 0; p < teams[i].length; ++p)
174 placePlayerBaseStartingAnimal({
175 "playerID": teams[i][p],
176 "playerPosition": playerPosition[p],
177 "BaseResourceClass": clBaseResource,
178 "baseResourceConstraint":
179 new AndConstraint([avoidClasses(clPlayer, 4), stayClasses(clLand, 5)])
183 for (let p = 0; p < teams[i].length; ++p)
189 2 * numPlayers / numTeams,
190 2 * numPlayers / numTeams,
192 Math.floor(fractionToTiles(0.2)))],
193 true, clBaseResource, teamPosition[i]),
195 [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
200 oSecondaryHuntableAnimal,
201 4 * numPlayers / numTeams,
202 4 * numPlayers / numTeams,
204 Math.floor(fractionToTiles(0.2)))],
205 true, clBaseResource, teamPosition[i]),
207 [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
213 g_Map.log("Creating big islands");
216 Math.floor(scaleByMapSize(4, 8) * (isNomad() ? 2 : 1)),
217 Math.floor(scaleByMapSize(8, 16) * (isNomad() ? 2 : 1)),
218 Math.floor(scaleByMapSize(25, 60)),
221 scaleByMapSize(30, 70)),
223 new TerrainPainter(tMainTerrain),
224 new SmoothElevationPainter(ELEVATION_SET, heightLand, 6),
225 new TileClassPainter(clLand)
227 avoidClasses(clLand, 3, clPlayer, 3),
228 scaleByMapSize(4, 14) * (isNomad() ? 2 : 1),
231 g_Map.log("Creating small islands");
234 Math.floor(scaleByMapSize(4, 7)),
235 Math.floor(scaleByMapSize(7, 10)),
236 Math.floor(scaleByMapSize(16, 40)),
239 scaleByMapSize(22, 40)),
241 new LayeredPainter([tMainTerrain, tMainTerrain], [2]),
242 new SmoothElevationPainter(ELEVATION_SET, heightLand, 6),
243 new TileClassPainter(clLand)
245 avoidClasses(clLand, 3, clPlayer, 3),
246 scaleByMapSize(6, 55),
251 g_Map.log("Smoothing heightmap");
253 new MapBoundsPlacer(),
254 new SmoothingPainter(1, 0.8, 5));
256 // repaint clLand to compensate for smoothing
257 unPaintTileClassBasedOnHeight(-10, 10, 3, clLand);
258 paintTileClassBasedOnHeight(0, 5, 3, clLand);
262 createBumps(avoidClasses(clPlayer, 20));
266 [new SimpleObject(oMetalLarge, 1, 1, 0, 4)]
268 [avoidClasses(clForest, 1, clPlayer, 40, clRock, 20), stayClasses(clLand, 4)],
274 new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1),
275 new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)
278 [avoidClasses(clForest, 1, clPlayer, 40, clMetal, 20), stayClasses(clLand, 4)],
281 const [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1));
283 [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
285 avoidClasses(clPlayer, 10, clForest, 20, clBaseResource, 5, clRock, 6, clMetal, 6),
286 stayClasses(clLand, 3)
291 g_Map.log("Creating hills");
293 new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5),
295 new LayeredPainter([tCliff, tHill], [2]),
296 new SmoothElevationPainter(ELEVATION_SET, heightHill, 2),
297 new TileClassPainter(clHill)
299 [avoidClasses(clBaseResource, 20, clHill, 15, clRock, 6, clMetal, 6), stayClasses(clLand, 0)],
300 scaleByMapSize(4, 13)
303 g_Map.log("Smoothing heightmap");
305 new MapBoundsPlacer(),
306 new SmoothingPainter(1, 0.8, 3));
308 createStragglerTrees(
309 [oTree1, oTree2, oTree4, oTree3],
311 avoidClasses(clForest, 10, clPlayer, 20, clMetal, 6, clRock, 6, clHill, 1),
312 stayClasses(clLand, 4)
319 [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)],
320 [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)]
322 [3 * numPlayers, 3 * numPlayers],
324 avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clRock, 6, clMetal, 6),
325 stayClasses(clLand, 2)
331 [new SimpleObject(oFruitBush, 5, 7, 0, 4)]
335 avoidClasses(clForest, 0, clPlayer, 15, clHill, 1, clFood, 4, clRock, 6, clMetal, 6),
336 stayClasses(clLand, 2)
340 if (currentBiome() == "generic/sahara")
342 g_Map.log("Creating obelisks");
343 const group = new SimpleGroup(
344 [new SimpleObject(oObelisk, 1, 1, 0, 1)],
347 createObjectGroupsDeprecated(
350 avoidClasses(clBaseResource, 0, clHill, 0, clRock, 0, clMetal, 0, clFood, 0),
351 stayClasses(clLand, 1)
353 scaleByMapSize(3, 8), 1000
357 g_Map.log("Creating dirt patches");
358 const numb = currentBiome() == "generic/savanna" ? 3 : 1;
359 for (const size of [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)])
361 new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5),
365 [tMainTerrain, tTier1Terrain],
366 [tTier1Terrain, tTier2Terrain],
367 [tTier2Terrain, tTier3Terrain]
370 new TileClassPainter(clDirt)
372 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), stayClasses(clLand, 4)],
373 numb*scaleByMapSize(15, 45));
375 g_Map.log("Creating grass patches");
376 for (const size of [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)])
378 new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5),
379 new TerrainPainter(tTier4Terrain),
380 [avoidClasses(clForest, 0, clHill, 0, clDirt, 5, clPlayer, 0), stayClasses(clLand, 4)],
381 numb * scaleByMapSize(15, 45));
383 g_Map.log("Creating small decorative rocks");
384 let group = new SimpleGroup(
385 [new SimpleObject(aRockMedium, 1, 3, 0, 1)],
388 createObjectGroupsDeprecated(
390 [avoidClasses(clForest, 0, clHill, 0), stayClasses(clLand, 2)],
391 scaleByMapSize(16, 262), 50
394 g_Map.log("Creating large decorative rocks");
395 group = new SimpleGroup(
396 [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)],
399 createObjectGroupsDeprecated(
401 [avoidClasses(clForest, 0, clHill, 0), stayClasses(clLand, 2)],
402 scaleByMapSize(8, 131), 50
405 g_Map.log("Creating fish");
406 group = new SimpleGroup(
407 [new SimpleObject(oFish, 2, 3, 0, 2)],
410 createObjectGroupsDeprecated(group, 0,
411 avoidClasses(clLand, 4, clFood, 20),
415 g_Map.log("Creating Whales");
416 group = new SimpleGroup(
417 [new SimpleObject(oWhale, 1, 1, 0, 3)],
420 createObjectGroupsDeprecated(group, 0,
421 [avoidClasses(clLand, 4), avoidClasses(clFood, 8)],
422 scaleByMapSize(5, 20), 100
425 g_Map.log("Creating shipwrecks");
426 group = new SimpleGroup(
427 [new SimpleObject(oShipwreck, 1, 1, 0, 1)],
430 createObjectGroupsDeprecated(group, 0,
431 [avoidClasses(clLand, 4), avoidClasses(clFood, 8)],
432 scaleByMapSize(12, 16), 100
435 g_Map.log("Creating shipwreck debris");
436 group = new SimpleGroup(
437 [new SimpleObject(oShipDebris, 1, 1, 0, 1)],
440 createObjectGroupsDeprecated(group, 0,
441 [avoidClasses(clLand, 4), avoidClasses(clFood, 8)],
442 scaleByMapSize(10, 20), 100
445 g_Map.log("Creating small grass tufts");
446 const planetm = currentBiome() == "generic/india" ? 8 : 1;
447 group = new SimpleGroup(
448 [new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]
450 createObjectGroupsDeprecated(group, 0,
451 [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)],
452 planetm * scaleByMapSize(13, 200)
457 g_Map.log("Creating large grass tufts");
458 group = new SimpleGroup(
460 new SimpleObject(aGrass, 2, 4, 0, 1.8, -Math.PI / 8, Math.PI / 8),
461 new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5, -Math.PI / 8, Math.PI / 8)
464 createObjectGroupsDeprecated(group, 0,
465 [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 5)],
466 planetm * scaleByMapSize(13, 200)
469 paintTerrainBasedOnHeight(1, 2, 0, tShore);
470 paintTerrainBasedOnHeight(heightSeaGround, 1, 3, tWater);
472 placePlayersNomad(clPlayer, [
473 stayClasses(clLand, 4),
474 avoidClasses(clHill, 2, clForest, 1, clMetal, 4, clRock, 4, clFood, 2)
477 setSkySet(pickRandom(["cloudless", "cumulus", "overcast"]));
478 setSunRotation(randomAngle());
479 setSunElevation(randFloat(1/5, 1/3) * Math.PI);