Merge 'remotes/trunk'
[0ad.git] / binaries / data / mods / public / maps / random / island_stronghold.js
blobeb841b8a4eab7bd58536604a62fa1568052612cd
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;
11         setSelectedBiome();
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;
47         const pForest1 = [
48                 tForestFloor2 + TERRAIN_SEPARATOR + oTree1,
49                 tForestFloor2 + TERRAIN_SEPARATOR + oTree2,
50                 tForestFloor2
51         ];
52         const pForest2 = [
53                 tForestFloor1 + TERRAIN_SEPARATOR + oTree4,
54                 tForestFloor1 + TERRAIN_SEPARATOR + oTree5,
55                 tForestFloor1
56         ];
58         const heightSeaGround = -10;
59         const heightLand = 3;
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;
82         const teamPosition =
83                 distributePointsOnCircle(numTeams, startAngle, fractionToTiles(0.3), mapCenter)[0];
84         const teamRadius = fractionToTiles(0.05);
86         let teamNo = 0;
88         g_Map.log("Creating player islands and bases");
90         for (let i = 0; i < teams.length; ++i)
91         {
92                 if (!teams[i] || isNomad())
93                         continue;
95                 ++teamNo;
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)
102                 {
103                         addCivicCenterAreaToClass(playerPosition[p], clPlayer);
105                         createArea(
106                                 new ChainPlacer(
107                                         2,
108                                         Math.floor(scaleByMapSize(5, 11)),
109                                         Math.floor(scaleByMapSize(60, 250)),
110                                         Infinity,
111                                         playerPosition[p],
112                                         Infinity,
113                                         [defaultPlayerBaseRadius() * 3/4]),
114                                 [
115                                         new TerrainPainter(tMainTerrain),
116                                         new SmoothElevationPainter(ELEVATION_SET, heightLand, 2),
117                                         new TileClassPainter(clLand)
118                                 ]);
120                         placeCivDefaultStartingEntities(playerPosition[p], teams[i][p], false);
121                 }
123                 const mineAngle = randFloat(-1, 1) * Math.PI / teams[i].length;
124                 const mines = [
125                         { "template": oMetalLarge, "angle": mineAngle },
126                         { "template": oStoneLarge, "angle": mineAngle + Math.PI / 4 }
127                 ];
129                 // Mines
130                 for (let p = 0; p < teams[i].length; ++p)
131                         for (const mine of mines)
132                         {
133                                 const position = Vector2D.add(playerPosition[p],
134                                         new Vector2D(g_InitialMineDistance, 0).rotate(-playerAngle[p] - mine.angle));
135                                 createObjectGroup(
136                                         new SimpleGroup([new SimpleObject(mine.template, 1, 1, 0, 4)], true,
137                                                 clBaseResource, position),
138                                         0,
139                                         [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 5)]);
140                         }
142                 // Trees
143                 for (let p = 0; p < teams[i].length; ++p)
144                 {
145                         const tries = 10;
146                         for (let x = 0; x < tries; ++x)
147                         {
148                                 const tAngle = playerAngle[p] + randFloat(-1, 1) * 2 * Math.PI / teams[i].length;
149                                 const treePosition =
150                                         Vector2D.add(playerPosition[p], new Vector2D(16, 0).rotate(-tAngle)).round();
151                                 if (createObjectGroup(
152                                         new SimpleGroup(
153                                                 [new SimpleObject(oTree2, g_InitialTrees, g_InitialTrees, 0, 7)],
154                                                 true,
155                                                 clBaseResource,
156                                                 treePosition),
157                                         0,
158                                         [avoidClasses(clBaseResource, 4, clPlayer, 4), stayClasses(clLand, 4)]))
159                                         break;
160                         }
161                 }
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)])
171                         });
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)])
180                         });
182                 // Huntable animals
183                 for (let p = 0; p < teams[i].length; ++p)
184                 {
185                         createObjectGroup(
186                                 new SimpleGroup(
187                                         [new SimpleObject(
188                                                 oMainHuntableAnimal,
189                                                 2 * numPlayers / numTeams,
190                                                 2 * numPlayers / numTeams,
191                                                 0,
192                                                 Math.floor(fractionToTiles(0.2)))],
193                                         true, clBaseResource, teamPosition[i]),
194                                 0,
195                                 [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
197                         createObjectGroup(
198                                 new SimpleGroup(
199                                         [new SimpleObject(
200                                                 oSecondaryHuntableAnimal,
201                                                 4 * numPlayers / numTeams,
202                                                 4 * numPlayers / numTeams,
203                                                 0,
204                                                 Math.floor(fractionToTiles(0.2)))],
205                                         true, clBaseResource, teamPosition[i]),
206                                 0,
207                                 [avoidClasses(clBaseResource, 2, clPlayer, 10), stayClasses(clLand, 5)]);
208                 }
209         }
211         yield 40;
213         g_Map.log("Creating big islands");
214         createAreas(
215                 new ChainPlacer(
216                         Math.floor(scaleByMapSize(4, 8) * (isNomad() ? 2 : 1)),
217                         Math.floor(scaleByMapSize(8, 16) * (isNomad() ? 2 : 1)),
218                         Math.floor(scaleByMapSize(25, 60)),
219                         0.07,
220                         undefined,
221                         scaleByMapSize(30, 70)),
222                 [
223                         new TerrainPainter(tMainTerrain),
224                         new SmoothElevationPainter(ELEVATION_SET, heightLand, 6),
225                         new TileClassPainter(clLand)
226                 ],
227                 avoidClasses(clLand, 3, clPlayer, 3),
228                 scaleByMapSize(4, 14) * (isNomad() ? 2 : 1),
229                 1);
231         g_Map.log("Creating small islands");
232         createAreas(
233                 new ChainPlacer(
234                         Math.floor(scaleByMapSize(4, 7)),
235                         Math.floor(scaleByMapSize(7, 10)),
236                         Math.floor(scaleByMapSize(16, 40)),
237                         0.07,
238                         undefined,
239                         scaleByMapSize(22, 40)),
240                 [
241                         new LayeredPainter([tMainTerrain, tMainTerrain], [2]),
242                         new SmoothElevationPainter(ELEVATION_SET, heightLand, 6),
243                         new TileClassPainter(clLand)
244                 ],
245                 avoidClasses(clLand, 3, clPlayer, 3),
246                 scaleByMapSize(6, 55),
247                 1);
249         yield 70;
251         g_Map.log("Smoothing heightmap");
252         createArea(
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);
260         yield 85;
262         createBumps(avoidClasses(clPlayer, 20));
264         createMines(
265                 [
266                         [new SimpleObject(oMetalLarge, 1, 1, 0, 4)]
267                 ],
268                 [avoidClasses(clForest, 1, clPlayer, 40, clRock, 20), stayClasses(clLand, 4)],
269                 clMetal);
271         createMines(
272                 [
273                         [
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)
276                         ]
277                 ],
278                 [avoidClasses(clForest, 1, clPlayer, 40, clMetal, 20), stayClasses(clLand, 4)],
279                 clRock);
281         const [forestTrees, stragglerTrees] = getTreeCounts(...rBiomeTreeCount(1));
282         createForests(
283                 [tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
284                 [
285                         avoidClasses(clPlayer, 10, clForest, 20, clBaseResource, 5, clRock, 6, clMetal, 6),
286                         stayClasses(clLand, 3)
287                 ],
288                 clForest,
289                 forestTrees);
291         g_Map.log("Creating hills");
292         createAreas(
293                 new ChainPlacer(1, Math.floor(scaleByMapSize(4, 6)), Math.floor(scaleByMapSize(16, 40)), 0.5),
294                 [
295                         new LayeredPainter([tCliff, tHill], [2]),
296                         new SmoothElevationPainter(ELEVATION_SET, heightHill, 2),
297                         new TileClassPainter(clHill)
298                 ],
299                 [avoidClasses(clBaseResource, 20, clHill, 15, clRock, 6, clMetal, 6), stayClasses(clLand, 0)],
300                 scaleByMapSize(4, 13)
301         );
303         g_Map.log("Smoothing heightmap");
304         createArea(
305                 new MapBoundsPlacer(),
306                 new SmoothingPainter(1, 0.8, 3));
308         createStragglerTrees(
309                 [oTree1, oTree2, oTree4, oTree3],
310                 [
311                         avoidClasses(clForest, 10, clPlayer, 20, clMetal, 6, clRock, 6, clHill, 1),
312                         stayClasses(clLand, 4)
313                 ],
314                 clForest,
315                 stragglerTrees);
317         createFood(
318                 [
319                         [new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)],
320                         [new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)]
321                 ],
322                 [3 * numPlayers, 3 * numPlayers],
323                 [
324                         avoidClasses(clForest, 0, clPlayer, 20, clHill, 1, clRock, 6, clMetal, 6),
325                         stayClasses(clLand, 2)
326                 ],
327                 clFood);
329         createFood(
330                 [
331                         [new SimpleObject(oFruitBush, 5, 7, 0, 4)]
332                 ],
333                 [3 * numPlayers],
334                 [
335                         avoidClasses(clForest, 0, clPlayer, 15, clHill, 1, clFood, 4, clRock, 6, clMetal, 6),
336                         stayClasses(clLand, 2)
337                 ],
338                 clFood);
340         if (currentBiome() == "generic/sahara")
341         {
342                 g_Map.log("Creating obelisks");
343                 const group = new SimpleGroup(
344                         [new SimpleObject(oObelisk, 1, 1, 0, 1)],
345                         true
346                 );
347                 createObjectGroupsDeprecated(
348                         group, 0,
349                         [
350                                 avoidClasses(clBaseResource, 0, clHill, 0, clRock, 0, clMetal, 0, clFood, 0),
351                                 stayClasses(clLand, 1)
352                         ],
353                         scaleByMapSize(3, 8), 1000
354                 );
355         }
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)])
360                 createAreas(
361                         new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5)), size, 0.5),
362                         [
363                                 new LayeredPainter(
364                                         [
365                                                 [tMainTerrain, tTier1Terrain],
366                                                 [tTier1Terrain, tTier2Terrain],
367                                                 [tTier2Terrain, tTier3Terrain]
368                                         ],
369                                         [1, 1]),
370                                 new TileClassPainter(clDirt)
371                         ],
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)])
377                 createAreas(
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)],
386                 true
387         );
388         createObjectGroupsDeprecated(
389                 group, 0,
390                 [avoidClasses(clForest, 0, clHill, 0), stayClasses(clLand, 2)],
391                 scaleByMapSize(16, 262), 50
392         );
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)],
397                 true
398         );
399         createObjectGroupsDeprecated(
400                 group, 0,
401                 [avoidClasses(clForest, 0, clHill, 0), stayClasses(clLand, 2)],
402                 scaleByMapSize(8, 131), 50
403         );
405         g_Map.log("Creating fish");
406         group = new SimpleGroup(
407                 [new SimpleObject(oFish, 2, 3, 0, 2)],
408                 true, clFood
409         );
410         createObjectGroupsDeprecated(group, 0,
411                 avoidClasses(clLand, 4, clFood, 20),
412                 25 * numPlayers, 60
413         );
415         g_Map.log("Creating Whales");
416         group = new SimpleGroup(
417                 [new SimpleObject(oWhale, 1, 1, 0, 3)],
418                 true, clFood
419         );
420         createObjectGroupsDeprecated(group, 0,
421                 [avoidClasses(clLand, 4), avoidClasses(clFood, 8)],
422                 scaleByMapSize(5, 20), 100
423         );
425         g_Map.log("Creating shipwrecks");
426         group = new SimpleGroup(
427                 [new SimpleObject(oShipwreck, 1, 1, 0, 1)],
428                 true, clFood
429         );
430         createObjectGroupsDeprecated(group, 0,
431                 [avoidClasses(clLand, 4), avoidClasses(clFood, 8)],
432                 scaleByMapSize(12, 16), 100
433         );
435         g_Map.log("Creating shipwreck debris");
436         group = new SimpleGroup(
437                 [new SimpleObject(oShipDebris, 1, 1, 0, 1)],
438                 true, clFood
439         );
440         createObjectGroupsDeprecated(group, 0,
441                 [avoidClasses(clLand, 4), avoidClasses(clFood, 8)],
442                 scaleByMapSize(10, 20), 100
443         );
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)]
449         );
450         createObjectGroupsDeprecated(group, 0,
451                 [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)],
452                 planetm * scaleByMapSize(13, 200)
453         );
455         yield 95;
457         g_Map.log("Creating large grass tufts");
458         group = new SimpleGroup(
459                 [
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)
462                 ]
463         );
464         createObjectGroupsDeprecated(group, 0,
465                 [avoidClasses(clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 5)],
466                 planetm * scaleByMapSize(13, 200)
467         );
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)
475         ]);
477         setSkySet(pickRandom(["cloudless", "cumulus", "overcast"]));
478         setSunRotation(randomAngle());
479         setSunElevation(randFloat(1/5, 1/3) * Math.PI);
480         setWaterWaviness(2);
482         yield 100;
484         return g_Map;