Pay missing file in the previous commit rP21377 with a large pyramid, refs #5040.
[0ad.git] / binaries / data / mods / public / maps / random / lower_nubia.js
blob6171ec20f649dcd32a73b9cfdd2043c33b0e3959
1 /**
2  * Heightmap image source:
3  * Imagery by Jesse Allen, NASA's Earth Observatory,
4  * using data from the General Bathymetric Chart of the Oceans (GEBCO)
5  * produced by the British Oceanographic Data Centre.
6  * https://visibleearth.nasa.gov/view.php?id=73934
7  * https://visibleearth.nasa.gov/view.php?id=74393
8  * Licensing: Public Domain, https://visibleearth.nasa.gov/useterms.php
9  *
10  * Since the elevation does not correlate with water distribution in lower_nubia,
11  * this map additionally uses composite photography to paint the water correctly.
12  *
13  * To reproduce the heightmaps, first set the coordinates:
14  * lat=23.25; lon=31.75; width=6;
15  * lat1=$(bc <<< ";scale=5;$lat-$width/2"); lon1=$(bc <<< ";scale=5;$lon+$width/2"); lat2=$(bc <<< ";scale=5;$lat+$width/2"); lon2=$(bc <<< ";scale=5;$lon-$width/2")
16  *
17  * The land heightmap image is reproduced using:
18  * wget https://eoimages.gsfc.nasa.gov/images/imagerecords/73000/73934/gebco_08_rev_elev_C1_grey_geo.tif
19  * gdal_translate -projwin $lon2 $lat2 $lon1 $lat1 gebco_08_rev_elev_C1_grey_geo.tif lower_nubia.tif
20  * convert lower_nubia.tif -resize 512 -contrast-stretch 0 lower_nubia_heightmap.png
21  * convert lower_nubia_heightmap.png -threshold 25% lower_nubia_land_threshold.png
22  *
23  * The watermap image is reproduced using:
24  * wget https://eoimages.gsfc.nasa.gov/images/imagerecords/74000/74393/world.topo.200407.3x21600x21600.C1.jpg
25  * gdal_translate -a_srs EPSG:4326 -a_ullr 0 90 90 0 world.topo.200407.3x21600x21600.C1.jpg world.topo.200407.3x21600x21600.C1.jpg.tif
26  * gdal_translate -projwin $lon2 $lat2 $lon1 $lat1 world.topo.200407.3x21600x21600.C1.jpg.tif lower_nubia_water.tif
27  * convert lower_nubia_water.tif -set colorspace Gray -resize 512 -separate -average -threshold 51% lower_nubia_water_threshold.png
28  *
29  * No further changes should be applied to the images to keep them easily interchangeable.
30  */
32 Engine.LoadLibrary("rmgen");
33 Engine.LoadLibrary("rmgen-common");
35 TILE_CENTERED_HEIGHT_MAP = true;
37 const tSand = "desert_sand_dunes_100";
38 const tPlateau = ["savanna_dirt_a", "savanna_dirt_b"];
39 const tNilePlants = "desert_plants_a";
40 const tCliffUpper = ["medit_cliff_italia", "medit_cliff_italia", "medit_cliff_italia_grass"];
41 const tRoad = "savanna_tile_a";
42 const tWater = "desert_sand_wet";
44 const oAcacia = "gaia/flora_tree_acacia";
45 const oTreeDead = "gaia/flora_tree_dead";
46 const oBerryBush = "gaia/flora_bush_berry_desert";
47 const oPalms = [
48         "gaia/flora_tree_cretan_date_palm_tall",
49         "gaia/flora_tree_cretan_date_palm_short",
50         "gaia/flora_tree_palm_tropic",
51         "gaia/flora_tree_date_palm",
52         "gaia/flora_tree_senegal_date_palm",
53         "gaia/flora_tree_medit_fan_palm"
55 const oStoneLarge = "gaia/geology_stonemine_savanna_quarry";
56 const oStoneSmall = "gaia/geology_stone_desert_small";
57 const oMetalLarge = "gaia/geology_metal_savanna_slabs";
58 const oMetalSmall = "gaia/geology_metal_desert_small";
59 const oWoodTreasure = "gaia/treasure/wood";
60 const oGazelle = "gaia/fauna_gazelle";
61 const oElephant = "gaia/fauna_elephant_african_bush";
62 const oElephantInfant = "gaia/fauna_elephant_african_infant";
63 const oLion = "gaia/fauna_lion";
64 const oLioness = "gaia/fauna_lioness";
65 const oHawk = "gaia/fauna_hawk";
66 const oPyramid = "structures/kush_pyramid_large";
68 const aRock = actorTemplate("geology/stone_savanna_med");
69 const aBushes = [
70         "props/flora/bush_dry_a",
71         "props/flora/bush_medit_la_dry",
72         "props/flora/bush_medit_me_dry",
73         "props/flora/bush_medit_sm",
74         "props/flora/bush_medit_sm_dry",
75         "props/flora/bush_tempe_me_dry",
76         "props/flora/grass_soft_dry_large_tall",
77         "props/flora/grass_soft_dry_small_tall"
78 ].map(actorTemplate);
80 const heightScale = num => num * g_MapSettings.Size / 320;
82 const heightSeaGround = heightScale(-3);
83 const heightWaterLevel = heightScale(0);
84 const heightNileForests = heightScale(15);
85 const heightPlateau2 = heightScale(38);
86 const minHeight = -3;
87 const maxHeight = 150;
89 const g_Map = new RandomMap(0, tSand);
90 const mapCenter = g_Map.getCenter();
91 const mapBounds = g_Map.getBounds();
93 const clWater = g_Map.createTileClass();
94 const clCliff = g_Map.createTileClass();
95 const clPlayer = g_Map.createTileClass();
96 const clBaseResource = g_Map.createTileClass();
97 const clForest = g_Map.createTileClass();
98 const clRock = g_Map.createTileClass();
99 const clMetal = g_Map.createTileClass();
100 const clFood = g_Map.createTileClass();
101 const clPyramid = g_Map.createTileClass();
103 g_Map.log("Loading heightmaps");
104 const heightmapLand = convertHeightmap1Dto2D(Engine.LoadHeightmapImage("maps/random/lower_nubia_heightmap.png"));
105 const heightmapLandThreshold = convertHeightmap1Dto2D(Engine.LoadHeightmapImage("maps/random/lower_nubia_land_threshold.png"));
106 const heightmapWaterThreshold = convertHeightmap1Dto2D(Engine.LoadHeightmapImage("maps/random/lower_nubia_water_threshold.png"));
108 var heightmapCombined = [];
109 for (let x = 0; x < heightmapLand.length; ++x)
111         heightmapCombined[x] = new Float32Array(heightmapLand.length);
112         for (let y = 0; y < heightmapLand.length; ++y)
113                 // Reduce ahistorical Lake Nasser and lakes in the valleys west of the Nile.
114                 // The heightmap does not correlate with water distribution in this arid climate at all.
115                 heightmapCombined[x][y] = heightmapLandThreshold[x][y] || heightmapWaterThreshold[x][y] ? heightmapLand[x][y] : minHeight;
118 g_Map.log("Copying heightmap");
119 createArea(
120         new MapBoundsPlacer(),
121         new HeightmapPainter(heightmapCombined, minHeight, maxHeight));
123 g_Map.log("Lowering sea ground");
124 createArea(
125         new MapBoundsPlacer(),
126         [
127                 new SmoothElevationPainter(ELEVATION_SET, heightSeaGround, 3),
128                 new TileClassPainter(clWater)
129         ],
130         new HeightConstraint(-Infinity, heightSeaGround));
132 g_Map.log("Creating shallows...");
133 const riverAngle = Math.PI * 3 / 4;
134 for (let i = 0; i < scaleByMapSize(3, 8); ++i)
136         let x = fractionToTiles(randFloat(0, 1));
137         createPassage({
138                 "start": new Vector2D(x, mapBounds.bottom).rotateAround(riverAngle, mapCenter),
139                 "end": new Vector2D(x, mapBounds.top).rotateAround(riverAngle, mapCenter),
140                 "startWidth": scaleByMapSize(4, 6),
141                 "endWidth": scaleByMapSize(4, 6),
142                 "smoothWidth": 1,
143                 "startHeight": heightNileForests,
144                 "endHeight": heightNileForests,
145                 "constraint": new NearTileClassConstraint(clWater, 2)
146         });
149 g_Map.log("Smoothing heightmap");
150 createArea(
151         new MapBoundsPlacer(),
152         new SmoothingPainter(1, scaleByMapSize(0.5, 1), 1));
154 g_Map.log("Smoothing plateau passage");
155 const passageStart = new Vector2D(263, 189).div(heightmapLand.length);
156 const passageEnd = new Vector2D(110, 350).div(heightmapLand.length);
157 createArea(
158         new PathPlacer(
159                 new Vector2D(fractionToTiles(passageStart.x), mapBounds.top - fractionToTiles(passageStart.y)),
160                 new Vector2D(fractionToTiles(passageEnd.x), mapBounds.top - fractionToTiles(passageEnd.y)),
161                 scaleByMapSize(10, 40),
162                 0,
163                 scaleByMapSize(3, 9),
164                 0.2,
165                 0.05),
166         new SmoothingPainter(4, 1, 1));
168 g_Map.log("Marking water");
169 createArea(
170         new MapBoundsPlacer(),
171         new TileClassPainter(clWater),
172         new HeightConstraint(-Infinity, heightSeaGround));
174 g_Map.log("Marking cliffs");
175 createArea(
176         new MapBoundsPlacer(),
177         new TileClassPainter(clCliff),
178         new SlopeConstraint(2, Infinity));
180 g_Map.log("Painting water and shoreline");
181 createArea(
182         new MapBoundsPlacer(),
183         new TerrainPainter(tWater),
184         new HeightConstraint(-Infinity, heightWaterLevel));
186 g_Map.log("Painting plateau");
187 createArea(
188         new MapBoundsPlacer(),
189         new TerrainPainter(tPlateau),
190         new HeightConstraint(heightPlateau2, Infinity));
192 var playerIDs = [];
193 var playerPosition = [];
194 if (!isNomad())
196         g_Map.log("Finding player locations...");
197         [playerIDs, playerPosition] = playerPlacementRandom(sortAllPlayers(), avoidClasses(clWater, scaleByMapSize(8, 12), clCliff, scaleByMapSize(8, 12)));
199         g_Map.log("Flatten the initial CC area...");
200         for (let position of playerPosition)
201                 createArea(
202                         new ClumpPlacer(diskArea(defaultPlayerBaseRadius() * 0.8), 0.95, 0.6, Infinity, position),
203                         new SmoothElevationPainter(ELEVATION_SET, g_Map.getHeight(position), 6));
206 placePlayerBases({
207         "PlayerPlacement": [playerIDs, playerPosition],
208         "PlayerTileClass": clPlayer,
209         "BaseResourceClass": clBaseResource,
210         "baseResourceConstraint": avoidClasses(clCliff, 0, clWater, 0),
211         "CityPatch": {
212                 "outerTerrain": tRoad,
213                 "innerTerrain": tRoad
214         },
215         "Chicken": {
216                 "template": oGazelle,
217                 "distance": 18,
218                 "minGroupDistance": 2,
219                 "maxGroupDistance": 4,
220                 "minGroupCount": 2,
221                 "maxGroupCount": 3
222         },
223         "Berries": {
224                 "template": oBerryBush
225         },
226         "Mines": {
227                 "types": [
228                         { "template": oMetalLarge },
229                         { "template": oStoneLarge }
230                 ]
231         },
232         "Trees": {
233                 "template": oAcacia,
234                 "count": scaleByMapSize(3, 12),
235                 "minDistGroup": 2,
236                 "maxDistGroup": 6,
237                 "minDist": 15,
238                 "maxDist": 16
239         },
240         "Treasures": {
241                 "types": [
242                         {
243                                 "template": oWoodTreasure,
244                                 "count": 14
245                         }
246                 ]
247         },
248         "Decoratives": {
249                 "template": pickRandom(aBushes)
250         }
253 g_Map.log("Painting lower cliffs");
254 createArea(
255         new MapBoundsPlacer(),
256         new TerrainPainter(tNilePlants),
257         [
258                 new SlopeConstraint(2, Infinity),
259                 new NearTileClassConstraint(clWater, 2)
260         ]);
262 g_Map.log("Painting upper cliffs");
263 createArea(
264         new MapBoundsPlacer(),
265         new TerrainPainter(tCliffUpper),
266         [
267                 avoidClasses(clWater, 2),
268                 new SlopeConstraint(2, Infinity)
269         ]);
271 g_Map.log("Creating stone mines");
272 createMines(
273         [
274                 [new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)],
275                 [new SimpleObject(oStoneSmall, 3, 6, 1, 3, 0, 2 * Math.PI, 1)]
276         ],
277         avoidClasses(clWater, 4, clCliff, 4, clPlayer, 20, clRock, 10),
278         clRock,
279         scaleByMapSize(10, 30));
281 g_Map.log("Creating metal mines");
282 createMines(
283         [
284                 [new SimpleObject(oMetalSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oMetalLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)],
285                 [new SimpleObject(oMetalSmall, 3, 6, 1, 3, 0, 2 * Math.PI, 1)]
286         ],
287         avoidClasses(clWater, 4, clCliff, 4, clPlayer, 20, clMetal, 10, clRock, 5),
288         clMetal,
289         scaleByMapSize(10, 30));
291 g_Map.log("Creating pyramid");
292 createObjectGroups(
293         new SimpleGroup([new SimpleObject(oPyramid, 1, 1, 1, 1)], true, clPyramid),
294         0,
295         [new NearTileClassConstraint(clWater, 10), avoidClasses(clWater, 6, clCliff, 6, clPlayer, 30, clMetal, 6, clRock, 6)],
296         1,
297         500);
299 g_Map.log("Creating forests");
300 createObjectGroups(
301         new SimpleGroup([new RandomObject(oPalms, 1, 2, 1, 1)], true, clForest),
302         0,
303         [
304                 new NearTileClassConstraint(clWater, scaleByMapSize(1, 8)),
305                 new HeightConstraint(heightNileForests, Infinity),
306                 avoidClasses(clWater, 0, clCliff, 0, clForest, 1, clPlayer, 12, clBaseResource, 5, clPyramid, 6)
307         ],
308         scaleByMapSize(100, 1000),
309         200);
311 createStragglerTrees(
312         [oAcacia, oTreeDead],
313         avoidClasses(clWater, 10, clCliff, 1, clPlayer, 12, clBaseResource, 5, clPyramid, 6),
314         clForest,
315         scaleByMapSize(15, 400),
316         200);
318 const avoidCollisions = avoidClasses(clPlayer, 12, clBaseResource, 5, clWater, 1, clForest, 1, clRock, 4, clMetal, 4, clFood, 6, clCliff, 0, clPyramid, 6);
320 g_Map.log("Creating gazelles");
321 createObjectGroups(
322         new SimpleGroup([new SimpleObject(oGazelle, 5, 7, 2, 4)], true, clFood),
323         0,
324         avoidCollisions,
325         scaleByMapSize(2, 10),
326         50);
328 if (!isNomad())
330         g_Map.log("Creating lions");
331         createObjectGroups(
332                 new SimpleGroup([new SimpleObject(oLion, 1, 2, 2, 4), new SimpleObject(oLioness, 2, 3, 2, 4)], true, clFood),
333                 0,
334                 avoidCollisions,
335                 scaleByMapSize(2, 10),
336                 50);
339 g_Map.log("Creating elephants");
340 createObjectGroups(
341         new SimpleGroup([new SimpleObject(oElephant, 2, 3, 2, 4), new SimpleObject(oElephantInfant, 2, 3, 2, 4)], true, clFood),
342         0,
343         avoidCollisions,
344         scaleByMapSize(2, 10),
345         50);
347 placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 2, clRock, 4, clMetal, 4, clFood, 2, clCliff, 2));
349 g_Map.log("Creating hawk");
350 for (let i = 0; i < scaleByMapSize(0, 2); ++i)
351         g_Map.placeEntityAnywhere(oHawk, 0, mapCenter, randomAngle());
353 createDecoration(
354         aBushes.map(bush => [new SimpleObject(bush, 0, 3, 2, 4)]),
355         aBushes.map(bush => scaleByMapSize(200, 800) * randIntInclusive(1, 3)),
356         [
357                 new NearTileClassConstraint(clWater, 2),
358                 new HeightConstraint(heightWaterLevel, Infinity),
359                 avoidClasses(clForest, 0)
360         ]);
362 createDecoration(
363         [[new SimpleObject(aRock, 0, 4, 2, 4)]],
364         [[scaleByMapSize(100, 600)]],
365         avoidClasses(clWater, 0));
367 setWindAngle(-0.43);
368 setWaterTint(0.161, 0.286, 0.353);
369 setWaterColor(0.129, 0.176, 0.259);
370 setWaterWaviness(8);
371 setWaterMurkiness(0.87);
372 setWaterType("lake");
374 setTerrainAmbientColor(0.58, 0.443, 0.353);
376 setSunColor(0.733, 0.746, 0.574);
377 setSunRotation(Math.PI * 1.1);
378 setSunElevation(Math.PI / 7);
380 setFogFactor(0);
381 setFogThickness(0);
382 setFogColor(0.69, 0.616, 0.541);
384 setPPEffect("hdr");
385 setPPContrast(0.67);
386 setPPSaturation(0.42);
387 setPPBloom(0.23);
389 g_Map.ExportMap();