Merge 'remotes/trunk'
[0ad.git] / binaries / data / mods / public / maps / random / corsica.js
blob957ffe563f2d9618c08b3faa6d4a49f3667b62c1
1 Engine.LoadLibrary("rmgen");
2 Engine.LoadLibrary("rmgen-common");
4 function* GenerateMap()
6         const tGrass = ["medit_grass_field", "medit_grass_field_b", "temp_grass_c"];
7         const tLushGrass = ["medit_grass_field", "medit_grass_field_a"];
9         const tSteepCliffs = ["temp_cliff_b", "temp_cliff_a"];
10         const tCliffs = ["temp_cliff_b", "medit_cliff_italia", "medit_cliff_italia_grass"];
11         const tHill = [
12                 "medit_cliff_italia_grass",
13                 "medit_cliff_italia_grass",
14                 "medit_grass_field",
15                 "medit_grass_field",
16                 "temp_grass"
17         ];
18         const tMountain = ["medit_cliff_italia_grass", "medit_cliff_italia"];
20         const tRoad = ["medit_city_tile", "medit_rocks_grass", "medit_grass_field_b"];
21         const tRoadWild = ["medit_rocks_grass", "medit_grass_field_b"];
23         const tShoreBlend = ["medit_sand_wet", "medit_rocks_wet"];
24         const tShore = ["medit_rocks", "medit_sand", "medit_sand"];
25         const tSandTransition = ["medit_sand", "medit_rocks_grass", "medit_rocks_grass", "medit_rocks_grass"];
26         const tVeryDeepWater = ["medit_sea_depths", "medit_sea_coral_deep"];
27         const tDeepWater = ["medit_sea_coral_deep", "tropic_ocean_coral"];
28         const tCreekWater = "medit_sea_coral_plants";
30         const ePine = "gaia/tree/aleppo_pine";
31         const ePalmTall = "gaia/tree/cretan_date_palm_tall";
32         const eFanPalm = "gaia/tree/medit_fan_palm";
33         const eApple = "gaia/fruit/apple";
34         const eBush = "gaia/fruit/berry_01";
35         const eFish = "gaia/fish/generic";
36         const ePig = "gaia/fauna_pig";
37         const eStoneMine = "gaia/rock/mediterranean_large";
38         const eMetalMine = "gaia/ore/mediterranean_large";
40         const aRock = "actor|geology/stone_granite_med.xml";
41         const aLargeRock = "actor|geology/stone_granite_large.xml";
42         const aBushA = "actor|props/flora/bush_medit_sm_lush.xml";
43         const aBushB = "actor|props/flora/bush_medit_me_lush.xml";
44         const aPlantA = "actor|props/flora/plant_medit_artichoke.xml";
45         const aPlantB = "actor|props/flora/grass_tufts_a.xml";
46         const aPlantC = "actor|props/flora/grass_soft_tuft_a.xml";
48         const aStandingStone = "actor|props/special/eyecandy/standing_stones.xml";
50         const heightSeaGround = -8;
51         const heightCreeks = -5;
52         const heightBeaches = -1;
53         const heightMain = 5;
55         const heightOffsetMainRelief = 30;
56         const heightOffsetLevel1 = 9;
57         const heightOffsetLevel2 = 8;
58         const heightOffsetBumps = 2;
59         const heightOffsetAntiBumps = -5;
61         globalThis.g_Map = new RandomMap(heightSeaGround, tVeryDeepWater);
63         const numPlayers = getNumPlayers();
64         const mapSize = g_Map.getSize();
65         const mapCenter = g_Map.getCenter();
67         const clIsland = g_Map.createTileClass();
68         const clCreek = g_Map.createTileClass();
69         const clWater = g_Map.createTileClass();
70         const clCliffs = g_Map.createTileClass();
71         const clForest = g_Map.createTileClass();
72         const clShore = g_Map.createTileClass();
73         const clPlayer = g_Map.createTileClass();
74         const clBaseResource = g_Map.createTileClass();
75         const clPassage = g_Map.createTileClass();
76         const clSettlement = g_Map.createTileClass();
78         const radiusBeach = fractionToTiles(0.57);
79         const radiusCreeks = fractionToTiles(0.52);
80         const radiusIsland = fractionToTiles(0.4);
81         const radiusLevel1 = fractionToTiles(0.35);
82         const radiusPlayer = fractionToTiles(0.25);
83         const radiusLevel2 = fractionToTiles(0.2);
85         const creeksArea = () => randBool() ? randFloat(10, 50) : scaleByMapSize(75, 100) + randFloat(0, 20);
87         const nbCreeks = scaleByMapSize(6, 15);
88         const nbSubIsland = 5;
89         const nbBeaches = scaleByMapSize(2, 5);
90         const nbPassagesLevel1 = scaleByMapSize(4, 8);
91         const nbPassagesLevel2 = scaleByMapSize(2, 4);
93         g_Map.log("Creating Corsica and Sardinia");
94         const swapAngle = randBool() ? Math.PI / 2 : 0;
95         const islandLocations = [new Vector2D(0.05, 0.05), new Vector2D(0.95, 0.95)]
96                 .map(v => v.mult(mapSize).rotateAround(-swapAngle, mapCenter));
98         for (let island = 0; island < 2; ++island)
99         {
100                 g_Map.log("Creating island area");
101                 createArea(
102                         new ClumpPlacer(diskArea(radiusIsland), 1, 0.5, Infinity, islandLocations[island]),
103                         [
104                                 new LayeredPainter([tCliffs, tGrass], [2]),
105                                 new SmoothElevationPainter(ELEVATION_SET, heightMain, 0),
106                                 new TileClassPainter(clIsland)
107                         ]);
109                 g_Map.log("Creating subislands");
110                 for (let i = 0; i < nbSubIsland + 1; ++i)
111                 {
112                         const angle = Math.PI * (island + i / (nbSubIsland * 2)) + swapAngle;
113                         const location =
114                                 Vector2D.add(islandLocations[island], new Vector2D(radiusIsland, 0).rotate(-angle));
115                         createArea(
116                                 new ClumpPlacer(diskArea(fractionToTiles(0.09)), 0.6, 0.03, Infinity, location),
117                                 [
118                                         new LayeredPainter([tCliffs, tGrass], [2]),
119                                         new SmoothElevationPainter(ELEVATION_SET, heightMain, 1),
120                                         new TileClassPainter(clIsland)
121                                 ]);
122                 }
124                 g_Map.log("Creating creeks");
125                 for (let i = 0; i < nbCreeks + 1; ++i)
126                 {
127                         const angle = Math.PI * (island + i * (1 / (nbCreeks * 2))) + swapAngle;
128                         const location =
129                                 Vector2D.add(islandLocations[island], new Vector2D(radiusCreeks, 0).rotate(-angle));
130                         createArea(
131                                 new ClumpPlacer(creeksArea(), 0.4, 0.01, Infinity, location),
132                                 [
133                                         new TerrainPainter(tSteepCliffs),
134                                         new SmoothElevationPainter(ELEVATION_SET, heightCreeks, 0),
135                                         new TileClassPainter(clCreek)
136                                 ]);
137                 }
139                 g_Map.log("Creating beaches");
140                 for (let i = 0; i < nbBeaches + 1; ++i)
141                 {
142                         const angle = Math.PI * (island +
143                                 (i / (nbBeaches * 2.5)) + 1 / (nbBeaches * 6) +
144                                 randFloat(-1, 1) / (nbBeaches * 7)) +
145                                 swapAngle;
146                         const start =
147                                 Vector2D.add(islandLocations[island], new Vector2D(radiusIsland, 0).rotate(-angle));
148                         const end =
149                                 Vector2D.add(islandLocations[island], new Vector2D(radiusBeach, 0).rotate(-angle));
151                         createArea(
152                                 new ClumpPlacer(130, 0.7, 0.8, Infinity,
153                                         Vector2D.add(start, Vector2D.mult(end, 3)).div(4)),
154                                 new SmoothElevationPainter(ELEVATION_SET, heightBeaches, 5));
156                         createPassage({
157                                 "start": start,
158                                 "end": end,
159                                 "startWidth": 18,
160                                 "endWidth": 25,
161                                 "smoothWidth": 4,
162                                 "tileClass": clShore
163                         });
164                 }
166                 g_Map.log("Creating main relief");
167                 createArea(
168                         new ClumpPlacer(diskArea(radiusIsland), 1, 0.2, Infinity, islandLocations[island]),
169                         new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetMainRelief,
170                                 fractionToTiles(0.45)));
172                 g_Map.log("Creating first level plateau");
173                 createArea(
174                         new ClumpPlacer(diskArea(radiusLevel1), 0.95, 0.02, Infinity, islandLocations[island]),
175                         new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetLevel1, 1));
177                 g_Map.log("Creating first level passages");
178                 for (let i = 0; i <= nbPassagesLevel1; ++i)
179                 {
180                         const angle = Math.PI * (i / 7 + 1 / 9 + island) + swapAngle;
181                         createPassage({
182                                 "start": Vector2D.add(islandLocations[island],
183                                         new Vector2D(radiusLevel1 + 10, 0).rotate(-angle)),
184                                 "end": Vector2D.add(islandLocations[island],
185                                         new Vector2D(radiusLevel1 - 4, 0).rotate(-angle)),
186                                 "startWidth": 10,
187                                 "endWidth": 6,
188                                 "smoothWidth": 3,
189                                 "tileClass": clPassage
190                         });
191                 }
193                 if (mapSize > 150)
194                 {
195                         g_Map.log("Creating second level plateau");
196                         createArea(
197                                 new ClumpPlacer(diskArea(radiusLevel2), 0.98, 0.04, Infinity,
198                                         islandLocations[island]),
199                                 [
200                                         new LayeredPainter([tCliffs, tGrass], [2]),
201                                         new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetLevel2, 1)
202                                 ]);
204                         g_Map.log("Creating second level passages");
205                         for (let i = 0; i < nbPassagesLevel2; ++i)
206                         {
207                                 const angle =
208                                         Math.PI * (i / (2 * nbPassagesLevel2) + 1 / (4 * nbPassagesLevel2) + island) +
209                                         swapAngle;
210                                 createPassage({
211                                         "start": Vector2D.add(islandLocations[island],
212                                                 new Vector2D(radiusLevel2 + 3, 0).rotate(-angle)),
213                                         "end": Vector2D.add(islandLocations[island],
214                                                 new Vector2D(radiusLevel2 - 6, 0).rotate(-angle)),
215                                         "startWidth": 4,
216                                         "endWidth": 6,
217                                         "smoothWidth": 2,
218                                         "tileClass": clPassage
219                                 });
220                         }
221                 }
222         }
223         yield 30;
225         g_Map.log("Determining player locations");
226         const playerIDs = sortAllPlayers();
227         const playerPosition = [];
228         const playerAngle = [];
229         let p = 0;
230         for (let island = 0; island < 2; ++island)
231         {
232                 const playersPerIsland = island == 0 ? Math.ceil(numPlayers / 2) : Math.floor(numPlayers / 2);
234                 for (let i = 0; i < playersPerIsland; ++i)
235                 {
236                         playerAngle[p] = Math.PI * ((i + 0.5) / (2 * playersPerIsland) + island) + swapAngle;
237                         playerPosition[p] = Vector2D.add(islandLocations[island],
238                                 new Vector2D(radiusPlayer).rotate(-playerAngle[p]));
239                         addCivicCenterAreaToClass(playerPosition[p], clPlayer);
240                         ++p;
241                 }
242         }
244         placePlayerBases({
245                 "PlayerPlacement": [sortAllPlayers(), playerPosition],
246                 "BaseResourceClass": clBaseResource,
247                 "baseResourceConstraint": avoidClasses(clPlayer, 2),
248                 "Walls": false,
249                 "CityPatch": {
250                         "outerTerrain": tRoadWild,
251                         "innerTerrain": tRoad,
252                         "coherence": 0.8,
253                         "radius": 6,
254                         "painters": [
255                                 new TileClassPainter(clSettlement)
256                         ]
257                 },
258                 "StartingAnimal": {
259                 },
260                 "Berries": {
261                         "template": eBush
262                 },
263                 "Mines": {
264                         "types": [
265                                 { "template": eMetalMine },
266                                 { "template": eStoneMine }
267                         ]
268                 }
269                 // Sufficient starting trees around, no decoratives
270         });
271         yield 40;
273         g_Map.log("Creating bumps");
274         createAreas(
275                 new ClumpPlacer(70, 0.6, 0.1, Infinity),
276                 [new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBumps, 3)],
277                 [
278                         stayClasses(clIsland, 2),
279                         avoidClasses(clPlayer, 6, clPassage, 2)
280                 ],
281                 scaleByMapSize(20, 100),
282                 5);
284         g_Map.log("Creating anti bumps");
285         createAreas(
286                 new ClumpPlacer(120, 0.3, 0.1, Infinity),
287                 new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetAntiBumps, 6),
288                 avoidClasses(clPlayer, 6, clPassage, 2, clIsland, 2),
289                 scaleByMapSize(20, 100),
290                 5);
292         g_Map.log("Painting water");
293         paintTileClassBasedOnHeight(-Infinity, 0, Elevation_ExcludeMin_ExcludeMax, clWater);
295         g_Map.log("Painting land");
296         for (let mapX = 0; mapX < mapSize; ++mapX)
297                 for (let mapZ = 0; mapZ < mapSize; ++mapZ)
298                 {
299                         const position = new Vector2D(mapX, mapZ);
300                         const terrain = getCosricaSardiniaTerrain(position);
301                         if (!terrain)
302                                 continue;
304                         createTerrain(terrain).place(position);
306                         if (terrain == tCliffs || terrain == tSteepCliffs)
307                                 clCliffs.add(position);
308                 }
310         function getCosricaSardiniaTerrain(position)
311         {
312                 const isWater = clWater.countMembersInRadius(position, 3);
313                 const isShore = clShore.countMembersInRadius(position, 2);
314                 const isPassage = clPassage.countMembersInRadius(position, 2);
315                 const isSettlement = clSettlement.countMembersInRadius(position, 2);
317                 if (isSettlement)
318                         return undefined;
320                 const height = g_Map.getHeight(position);
321                 const slope = g_Map.getSlope(position);
323                 if (height >= 0.5 && height < 1.5 && isShore)
324                         return tSandTransition;
326                 // Paint land cliffs and grass
327                 if (height >= 1 && !isWater)
328                 {
329                         if (isPassage)
330                                 return tGrass;
332                         if (slope >= 1.25)
333                                 return height > 25 ? tSteepCliffs : tCliffs;
335                         if (height < 17)
336                                 return tGrass;
338                         if (slope < 0.625)
339                                 return tHill;
341                         return tMountain;
342                 }
344                 if (slope >= 1.125)
345                         return tCliffs;
347                 if (height >= 1.5)
348                         return undefined;
350                 if (height >= -0.75)
351                         return tShore;
353                 if (height >= -3)
354                         return tShoreBlend;
356                 if (height >= -6)
357                         return tCreekWater;
359                 if (height > -10 && slope < 0.75)
360                         return tDeepWater;
362                 return undefined;
363         }
365         yield 65;
367         g_Map.log("Creating mines");
368         for (const mine of [eMetalMine, eStoneMine])
369                 createObjectGroupsDeprecated(
370                         new SimpleGroup(
371                                 [
372                                         new SimpleObject(mine, 1, 1, 0, 0),
373                                         new SimpleObject(aBushB, 1, 1, 2, 2),
374                                         new SimpleObject(aBushA, 0, 2, 1, 3)
375                                 ],
376                                 true,
377                                 clBaseResource),
378                         0,
379                         [
380                                 stayClasses(clIsland, 1),
381                                 avoidClasses(
382                                         clWater, 3,
383                                         clPlayer, 6,
384                                         clBaseResource, 4,
385                                         clPassage, 2,
386                                         clCliffs, 1)
387                         ],
388                         scaleByMapSize(6, 25),
389                         1000);
391         g_Map.log("Creating grass patches");
392         createAreas(
393                 new ClumpPlacer(20, 0.3, 0.06, 0.5),
394                 [
395                         new TerrainPainter(tLushGrass),
396                         new TileClassPainter(clForest)
397                 ],
398                 avoidClasses(
399                         clWater, 1,
400                         clPlayer, 6,
401                         clBaseResource, 3,
402                         clCliffs, 1),
403                 scaleByMapSize(10, 40));
405         g_Map.log("Creating forests");
406         createObjectGroupsDeprecated(
407                 new SimpleGroup(
408                         [
409                                 new SimpleObject(ePine, 3, 6, 1, 3),
410                                 new SimpleObject(ePalmTall, 1, 3, 1, 3),
411                                 new SimpleObject(eFanPalm, 0, 2, 0, 2),
412                                 new SimpleObject(eApple, 0, 1, 1, 2)
413                         ],
414                         true,
415                         clForest),
416                 0,
417                 [
418                         stayClasses(clIsland, 3),
419                         avoidClasses(
420                                 clWater, 1,
421                                 clForest, 0,
422                                 clPlayer, 3,
423                                 clBaseResource, 4,
424                                 clPassage, 2,
425                                 clCliffs, 2)
426                 ],
427                 scaleByMapSize(350, 2500),
428                 100);
430         yield 75;
432         g_Map.log("Creating small decorative rocks");
433         createObjectGroupsDeprecated(
434                 new SimpleGroup(
435                         [
436                                 new SimpleObject(aRock, 1, 3, 0, 1),
437                                 new SimpleObject(aStandingStone, 0, 2, 0, 3)
438                         ],
439                         true),
440                 0,
441                 avoidClasses(
442                         clWater, 0,
443                         clForest, 0,
444                         clPlayer, 6,
445                         clBaseResource, 4,
446                         clPassage, 2),
447                 scaleByMapSize(16, 262),
448                 50);
450         g_Map.log("Creating large decorative rocks");
451         const rocksGroup = new SimpleGroup(
452                 [
453                         new SimpleObject(aLargeRock, 1, 2, 0, 1),
454                         new SimpleObject(aRock, 1, 3, 0, 2)
455                 ],
456                 true);
458         createObjectGroupsDeprecated(
459                 rocksGroup,
460                 0,
461                 avoidClasses(
462                         clWater, 0,
463                         clForest, 0,
464                         clPlayer, 6,
465                         clBaseResource, 4,
466                         clPassage, 2),
467                 scaleByMapSize(8, 131),
468                 50);
470         createObjectGroupsDeprecated(
471                 rocksGroup,
472                 0,
473                 borderClasses(clWater, 5, 10),
474                 scaleByMapSize(100, 800),
475                 500);
477         g_Map.log("Creating decorative plants");
478         const plantGroups = [
479                 new SimpleGroup(
480                         [
481                                 new SimpleObject(aPlantA, 3, 7, 0, 3),
482                                 new SimpleObject(aPlantB, 3, 6, 0, 3),
483                                 new SimpleObject(aPlantC, 1, 4, 0, 4)
484                         ],
485                         true),
486                 new SimpleGroup(
487                         [
488                                 new SimpleObject(aPlantB, 5, 20, 0, 5),
489                                 new SimpleObject(aPlantC, 4, 10, 0, 4)
490                         ],
491                         true)
492         ];
493         for (const group of plantGroups)
494                 createObjectGroupsDeprecated(
495                         group,
496                         0,
497                         avoidClasses(
498                                 clWater, 0,
499                                 clBaseResource, 4,
500                                 clShore, 3),
501                         scaleByMapSize(100, 600),
502                         50);
504         yield 80;
506         g_Map.log("Creating animals");
507         createObjectGroupsDeprecated(
508                 new SimpleGroup([new SimpleObject(ePig, 2, 4, 0, 3)]),
509                 0,
510                 avoidClasses(
511                         clWater, 3,
512                         clBaseResource, 4,
513                         clPlayer, 6),
514                 scaleByMapSize(20, 100),
515                 50);
517         g_Map.log("Creating fish");
518         createObjectGroupsDeprecated(
519                 new SimpleGroup([new SimpleObject(eFish, 1, 2, 0, 3)]),
520                 0,
521                 [
522                         stayClasses(clWater, 3),
523                         avoidClasses(clCreek, 3, clShore, 3)
524                 ],
525                 scaleByMapSize(50, 150),
526                 100);
528         yield 95;
530         placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clBaseResource, 4, clCliffs, 4));
532         setSkySet(pickRandom(["cumulus", "sunny"]));
534         setSunColor(0.8, 0.66, 0.48);
535         setSunElevation(0.828932);
536         setSunRotation((swapAngle ? 0.288 : 0.788) * Math.PI);
538         setAmbientColor(0.564706, 0.543726, 0.419608);
539         setWaterColor(0.2, 0.294, 0.49);
540         setWaterTint(0.208, 0.659, 0.925);
541         setWaterMurkiness(0.72);
542         setWaterWaviness(2.0);
543         setWaterType("ocean");
545         return g_Map;