Merge 'remotes/trunk'
[0ad.git] / binaries / data / mods / public / maps / random / oasis.js
blobbe1fc6c31332fe5ae7c8fda8044265eefcec8719
1 Engine.LoadLibrary("rmgen");
2 Engine.LoadLibrary("rmgen-common");
4 function* GenerateMap()
6         const tSand = [
7                 "desert_sand_dunes_100",
8                 "desert_dirt_cracks",
9                 "desert_sand_smooth",
10                 "desert_dirt_rough",
11                 "desert_dirt_rough_2",
12                 "desert_sand_smooth"
13         ];
14         const tDune = ["desert_sand_dunes_50"];
15         const tForestFloor = "desert_forestfloor_palms";
16         const tDirt = [
17                 "desert_dirt_rough",
18                 "desert_dirt_rough",
19                 "desert_dirt_rough",
20                 "desert_dirt_rough_2",
21                 "desert_dirt_rocks_2"
22         ];
23         const tRoad = "desert_city_tile";
24         const tRoadWild = "desert_city_tile";
25         const tShore = "dirta";
26         const tWater = "desert_sand_wet";
28         const ePalmShort = "gaia/tree/cretan_date_palm_short";
29         const ePalmTall = "gaia/tree/cretan_date_palm_tall";
30         const eBush = "gaia/fruit/grapes";
31         const eCamel = "gaia/fauna_camel";
32         const eGazelle = "gaia/fauna_gazelle";
33         const eLion = "gaia/fauna_lion";
34         const eLioness = "gaia/fauna_lioness";
35         const eStoneMine = "gaia/rock/desert_large";
36         const eMetalMine = "gaia/ore/desert_large";
38         const aFlower1 = "actor|props/flora/decals_flowers_daisies.xml";
39         const aWaterFlower = "actor|props/flora/water_lillies.xml";
40         const aReedsA = "actor|props/flora/reeds_pond_lush_a.xml";
41         const aReedsB = "actor|props/flora/reeds_pond_lush_b.xml";
42         const aRock = "actor|geology/stone_desert_med.xml";
43         const aBushA = "actor|props/flora/bush_desert_dry_a.xml";
44         const aBushB = "actor|props/flora/bush_desert_dry_a.xml";
45         const aSand = "actor|particle/blowing_sand.xml";
47         const pForestMain = [
48                 tForestFloor + TERRAIN_SEPARATOR + ePalmShort,
49                 tForestFloor + TERRAIN_SEPARATOR + ePalmTall,
50                 tForestFloor
51         ];
52         const pOasisForestLight = [
53                 tForestFloor + TERRAIN_SEPARATOR + ePalmShort,
54                 tForestFloor + TERRAIN_SEPARATOR + ePalmTall,
55                 tForestFloor,
56                 tForestFloor,
57                 tForestFloor,
58                 tForestFloor,
59                 tForestFloor,
60                 tForestFloor,
61                 tForestFloor
62         ];
64         const heightSeaGround = -3;
65         const heightFloraMin = -2.5;
66         const heightFloraReedsMax = -1.9;
67         const heightFloraMax = -1;
68         const heightLand = 1;
69         const heightSand = 3.4;
70         const heightOasisPath = 4;
71         const heightOffsetBump = 4;
72         const heightOffsetDune = 18;
74         globalThis.g_Map = new RandomMap(heightLand, tSand);
76         const numPlayers = getNumPlayers();
77         const mapSize = g_Map.getSize();
78         const mapCenter = g_Map.getCenter();
80         const clPlayer = g_Map.createTileClass();
81         const clHill = g_Map.createTileClass();
82         const clForest = g_Map.createTileClass();
83         const clOasis = g_Map.createTileClass();
84         const clPassage = g_Map.createTileClass();
85         const clRock = g_Map.createTileClass();
86         const clMetal = g_Map.createTileClass();
87         const clFood = g_Map.createTileClass();
88         const clBaseResource = g_Map.createTileClass();
90         const waterRadius = scaleByMapSize(7, 50);
91         const shoreDistance = scaleByMapSize(4, 10);
92         const forestDistance = scaleByMapSize(6, 20);
94         const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
96         g_Map.log("Creating small oasis near the players...");
97         const forestDist = 1.2 * defaultPlayerBaseRadius();
98         for (let i = 0; i < numPlayers; ++i)
99         {
100                 let forestPosition;
101                 let forestAngle;
102                 do
103                 {
104                         forestAngle = Math.PI / 3 * randFloat(1, 2);
105                         forestPosition =
106                                 Vector2D.add(playerPosition[i], new Vector2D(forestDist, 0).rotate(-forestAngle));
107                 } while (
108                         !createArea(
109                                 new ClumpPlacer(70, 1, 0.5, Infinity, forestPosition),
110                                 [
111                                         new LayeredPainter([tForestFloor, pForestMain], [0]),
112                                         new TileClassPainter(clBaseResource)
113                                 ],
114                                 avoidClasses(clBaseResource, 0)));
116                 let waterPosition;
117                 let flowerPosition;
118                 let reedsPosition;
119                 do
120                 {
121                         const waterAngle = forestAngle + randFloat(1, 5) / 3 * Math.PI;
122                         waterPosition =
123                                 Vector2D.add(forestPosition, new Vector2D(6, 0).rotate(-waterAngle)).round();
124                         flowerPosition =
125                                 Vector2D.add(forestPosition, new Vector2D(3, 0).rotate(-waterAngle)).round();
126                         reedsPosition =
127                                 Vector2D.add(forestPosition, new Vector2D(5, 0).rotate(-waterAngle)).round();
128                 } while (
129                         !createArea(
130                                 new ClumpPlacer(diskArea(4.5), 0.9, 0.4, Infinity, waterPosition),
131                                 [
132                                         new LayeredPainter([tShore, tWater], [1]),
133                                         new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 3)
134                                 ],
135                                 avoidClasses(clBaseResource, 0)));
137                 createObjectGroup(new SimpleGroup([new SimpleObject(aFlower1, 1, 5, 0, 3)], true, undefined,
138                         flowerPosition), 0);
139                 createObjectGroup(new SimpleGroup([new SimpleObject(aReedsA, 1, 3, 0, 0)], true, undefined,
140                         reedsPosition), 0);
141         }
142         yield 20;
144         placePlayerBases({
145                 "PlayerPlacement": [playerIDs, playerPosition],
146                 "PlayerTileClass": clPlayer,
147                 "BaseResourceClass": clBaseResource,
148                 "CityPatch": {
149                         "outerTerrain": tRoadWild,
150                         "innerTerrain": tRoad,
151                         "painters": [
152                                 new TileClassPainter(clPlayer)
153                         ]
154                 },
155                 "StartingAnimal": {
156                 },
157                 "Berries": {
158                         "template": eBush
159                 },
160                 "Mines": {
161                         "types": [
162                                 { "template": eMetalMine },
163                                 { "template": eStoneMine },
164                         ],
165                         "distance": defaultPlayerBaseRadius(),
166                         "maxAngle": Math.PI / 2,
167                         "groupElements": shuffleArray([aBushA, aBushB, ePalmShort, ePalmTall])
168                                 .map(t => new SimpleObject(t, 1, 1, 3, 4))
169                 }
170                 // Starting trees were set above
171                 // No decoratives
172         });
173         yield 30;
175         g_Map.log("Creating central oasis");
176         createArea(
177                 new ClumpPlacer(diskArea(forestDistance + shoreDistance + waterRadius), 0.8, 0.2, Infinity,
178                         mapCenter),
179                 [
180                         new LayeredPainter([pOasisForestLight, tWater], [forestDistance]),
181                         new SmoothElevationPainter(ELEVATION_SET, heightSeaGround,
182                                 forestDistance + shoreDistance),
183                         new TileClassPainter(clOasis)
184                 ]);
186         yield 40;
188         g_Map.log("Creating bumps");
189         createAreas(
190                 new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, Infinity),
191                 new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 3),
192                 avoidClasses(clPlayer, 10, clBaseResource, 6, clOasis, 4),
193                 scaleByMapSize(30, 70));
195         g_Map.log("Creating dirt patches");
196         createAreas(
197                 new ClumpPlacer(80, 0.3, 0.06, Infinity),
198                 new TerrainPainter(tDirt),
199                 avoidClasses(clPlayer, 10, clBaseResource, 6, clOasis, 4, clForest, 4),
200                 scaleByMapSize(15, 50));
202         g_Map.log("Creating dunes");
203         createAreas(
204                 new ClumpPlacer(120, 0.3, 0.06, Infinity),
205                 [
206                         new TerrainPainter(tDune),
207                         new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetDune, 30)
208                 ],
209                 avoidClasses(clPlayer, 10, clBaseResource, 6, clOasis, 4, clForest, 4),
210                 scaleByMapSize(15, 50));
212         yield 50;
214         if (mapSize > 150 && randBool())
215         {
216                 g_Map.log("Creating path though the oasis");
217                 const pathWidth = scaleByMapSize(7, 18);
218                 const points = distributePointsOnCircle(2, randomAngle(),
219                         waterRadius + shoreDistance + forestDistance + pathWidth, mapCenter)[0];
220                 createArea(
221                         new PathPlacer(points[0], points[1], pathWidth, 0.4, 1, 0.2, 0),
222                         [
223                                 new TerrainPainter(tSand),
224                                 new SmoothElevationPainter(ELEVATION_SET, heightOasisPath, 5),
225                                 new TileClassPainter(clPassage)
226                         ]);
227         }
228         g_Map.log("Creating some straggler trees around the passage");
229         let group = new SimpleGroup(
230                 [
231                         new SimpleObject(ePalmTall, 1, 1, 0, 0),
232                         new SimpleObject(ePalmShort, 1, 2, 1, 2),
233                         new SimpleObject(aBushA, 0, 2, 1, 3)
234                 ], true, clForest);
235         createObjectGroupsDeprecated(group, 0, stayClasses(clPassage, 3), scaleByMapSize(60, 250), 100);
237         g_Map.log("Creating stone mines");
238         group = new SimpleGroup(
239                 [
240                         new SimpleObject(eStoneMine, 1, 1, 0, 0),
241                         new SimpleObject(ePalmShort, 1, 2, 3, 3),
242                         new SimpleObject(ePalmTall, 0, 1, 3, 3),
243                         new SimpleObject(aBushB, 1, 1, 2, 2),
244                         new SimpleObject(aBushA, 0, 2, 1, 3)
245                 ], true, clRock);
246         createObjectGroupsDeprecated(group, 0,
247                 avoidClasses(clOasis, 10, clForest, 1, clPlayer, 30, clRock, 10, clBaseResource, 2, clHill, 1),
248                 scaleByMapSize(6, 25), 100
249         );
251         g_Map.log("Creating metal mines");
252         group = new SimpleGroup(
253                 [
254                         new SimpleObject(eMetalMine, 1, 1, 0, 0),
255                         new SimpleObject(ePalmShort, 1, 2, 2, 3),
256                         new SimpleObject(ePalmTall, 0, 1, 2, 2),
257                         new SimpleObject(aBushB, 1, 1, 2, 2),
258                         new SimpleObject(aBushA, 0, 2, 1, 3)
259                 ], true, clMetal);
260         createObjectGroupsDeprecated(group, 0,
261                 avoidClasses(
262                         clOasis, 10,
263                         clForest, 1,
264                         clPlayer, 30,
265                         clMetal, 10,
266                         clBaseResource, 2,
267                         clRock, 10,
268                         clHill, 1),
269                 scaleByMapSize(6, 25), 100
270         );
271         yield 65;
273         g_Map.log("Creating small decorative rocks");
274         group = new SimpleGroup([new SimpleObject(aRock, 2, 4, 0, 2)], true, undefined);
275         createObjectGroupsDeprecated(group, 0,
276                 avoidClasses(
277                         clOasis, 3,
278                         clForest, 0,
279                         clPlayer, 10,
280                         clHill, 1,
281                         clFood, 20),
282                 30, scaleByMapSize(10, 50));
284         yield 70;
286         g_Map.log("Creating camels");
287         group = new SimpleGroup(
288                 [new SimpleObject(eCamel, 1, 2, 0, 4)],
289                 true, clFood
290         );
291         createObjectGroupsDeprecated(group, 0,
292                 avoidClasses(clOasis, 3, clForest, 0, clPlayer, 10, clHill, 1, clFood, 20),
293                 1 * numPlayers, 50
294         );
295         yield 75;
297         g_Map.log("Creating gazelles");
298         group = new SimpleGroup(
299                 [new SimpleObject(eGazelle, 2, 4, 0, 2)],
300                 true, clFood
301         );
302         createObjectGroupsDeprecated(group, 0,
303                 avoidClasses(clOasis, 3, clForest, 0, clPlayer, 10, clHill, 1, clFood, 20),
304                 1 * numPlayers, 50
305         );
306         yield 85;
308         g_Map.log("Creating oasis animals");
309         for (let i = 0; i < scaleByMapSize(5, 30); ++i)
310         {
311                 const animalPos =
312                         Vector2D.add(mapCenter, new Vector2D(forestDistance + shoreDistance + waterRadius, 0)
313                                 .rotate(randomAngle()));
315                 createObjectGroup(
316                         new RandomGroup(
317                                 [
318                                         new SimpleObject(eLion, 1, 2, 0, 4),
319                                         new SimpleObject(eLioness, 1, 2, 2, 4),
320                                         new SimpleObject(eGazelle, 4, 6, 1, 5),
321                                         new SimpleObject(eCamel, 1, 2, 1, 5)
322                                 ],
323                                 true,
324                                 clFood,
325                                 animalPos),
326                         0);
327         }
328         yield 90;
330         g_Map.log("Creating bushes");
331         group = new SimpleGroup(
332                 [new SimpleObject(aBushB, 1, 2, 0, 2), new SimpleObject(aBushA, 2, 4, 0, 2)]
333         );
334         createObjectGroupsDeprecated(group, 0,
335                 avoidClasses(clOasis, 2, clHill, 1, clPlayer, 1, clPassage, 1),
336                 scaleByMapSize(10, 40), 20
337         );
339         const objectsWaterFlora = [
340                 new SimpleObject(aReedsA, 5, 12, 0, 2),
341                 new SimpleObject(aReedsB, 5, 12, 0, 2)
342         ];
344         g_Map.log("Creating sand blows and beautifications");
345         for (let sandx = 0; sandx < mapSize; sandx += 4)
346                 for (let sandz = 0; sandz < mapSize; sandz += 4)
347                 {
348                         const position = new Vector2D(sandx, sandz);
349                         const height = g_Map.getHeight(position);
351                         if (height > heightSand)
352                         {
353                                 if (randBool((height - heightSand) / 1.4))
354                                         createObjectGroup(new SimpleGroup([new SimpleObject(aSand, 0, 1, 0, 2)], true,
355                                                 undefined, position), 0);
356                         }
357                         else if (height > heightFloraMin && height < heightFloraMax)
358                         {
359                                 if (randBool(0.4))
360                                         createObjectGroup(
361                                                 new SimpleGroup(
362                                                         [new SimpleObject(aWaterFlower, 1, 4, 1, 2)], true, undefined,
363                                                         position),
364                                                 0);
365                                 else if (randBool(0.7) && height < heightFloraReedsMax)
366                                         createObjectGroup(
367                                                 new SimpleGroup(objectsWaterFlora, true, undefined, position),
368                                                 0);
370                                 if (clPassage.countMembersInRadius(position, 2))
371                                 {
372                                         if (randBool(0.4))
373                                                 createObjectGroup(
374                                                         new SimpleGroup(
375                                                                 [new SimpleObject(aWaterFlower, 1, 4, 1, 2)], true,
376                                                                 undefined, position),
377                                                         0);
378                                         else if (randBool(0.7) && height < heightFloraReedsMax)
379                                                 createObjectGroup(
380                                                         new SimpleGroup(objectsWaterFlora, true, undefined, position),
381                                                         0);
382                                 }
383                         }
384                 }
386         placePlayersNomad(clPlayer,
387                 avoidClasses(clOasis, 4, clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2));
389         setSkySet("sunny");
390         setSunColor(0.914, 0.827, 0.639);
391         setSunRotation(Math.PI/3);
392         setSunElevation(0.5);
393         setWaterColor(0, 0.227, 0.843);
394         setWaterTint(0, 0.545, 0.859);
395         setWaterWaviness(1.0);
396         setWaterType("clap");
397         setWaterMurkiness(0.5);
398         setAmbientColor(0.501961, 0.501961, 0.501961);
400         return g_Map;