Add randomAngle helper function to abbreviate calls.
[0ad.git] / binaries / data / mods / public / maps / random / schwarzwald.js
blob19def3d00d65e8e465db3a29f057caae6f959d87
1 Engine.LoadLibrary('rmgen');
2 Engine.LoadLibrary("heightmap");
4 log('Initializing map...');
6 InitMap();
8 setSkySet("fog");
9 setFogFactor(0.35);
10 setFogThickness(0.19);
12 setWaterColor(0.501961, 0.501961, 0.501961);
13 setWaterTint(0.25098, 0.501961, 0.501961);
14 setWaterWaviness(0.5);
15 setWaterType("clap");
16 setWaterMurkiness(0.75);
18 setPPSaturation(0.37);
19 setPPContrast(0.4);
20 setPPBrightness(0.4);
21 setPPEffect("hdr");
22 setPPBloom(0.4);
24 var clPlayer = createTileClass();
25 var clPath = createTileClass();
26 var clForest = createTileClass();
27 var clWater = createTileClass();
28 var clMetal = createTileClass();
29 var clRock = createTileClass();
30 var clFood = createTileClass();
31 var clBaseResource = createTileClass();
32 var clOpen = createTileClass();
34 var templateStoneMine = 'gaia/geology_stonemine_alpine_quarry';
35 var templateMetalMine = 'gaia/geology_metal_alpine_slabs';
36 var aGrass = 'actor|props/flora/grass_soft_small_tall.xml';
37 var aGrassShort = 'actor|props/flora/grass_soft_large.xml';
38 var aRockLarge = 'actor|geology/stone_granite_med.xml';
39 var aRockMedium = 'actor|geology/stone_granite_med.xml';
40 var aBushMedium = 'actor|props/flora/bush_medit_me.xml';
41 var aBushSmall = 'actor|props/flora/bush_medit_sm.xml';
42 var aReeds = 'actor|props/flora/reeds_pond_lush_b.xml';
43 var oFish = "gaia/fauna_fish";
45 var terrainWood = ['alpine_forrestfloor|gaia/flora_tree_oak', 'alpine_forrestfloor|gaia/flora_tree_pine'];
47 var terrainWoodBorder = ['new_alpine_grass_mossy|gaia/flora_tree_oak', 'alpine_forrestfloor|gaia/flora_tree_pine',
48         'temp_grass_long|gaia/flora_bush_temperate', 'temp_grass_clovers|gaia/flora_bush_berry', 'temp_grass_clovers_2|gaia/flora_bush_grapes',
49         'temp_grass_plants|gaia/fauna_deer', 'temp_grass_plants|gaia/fauna_rabbit', 'new_alpine_grass_dirt_a'];
51 var terrainBase = ['temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
52         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
53         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
54         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
55         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
56         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
57         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
58         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
59         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
60         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
61         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
62         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants', 'temp_grass_plants|gaia/fauna_sheep'];
64 var terrainBaseBorder = ['temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
65         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
66         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
67         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
68         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
69         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants',
70         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
71         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
72         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
73         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
74         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_d', 'temp_grass_plants',
75         'temp_plants_bog', 'temp_grass_plants', 'temp_grass_plants'];
77 var baseTex = ['temp_road', 'temp_road_overgrown'];
79 var terrainPath = ['temp_road', 'temp_road_overgrown'];
81 var tWater = ['dirt_brown_d'];
82 var tWaterBorder = ['dirt_brown_d'];
84 var mapSize = getMapSize();
85 var mapArea = getMapArea();
86 var mapRadius = mapSize/2;
87 var mapCenterX = mapRadius;
88 var mapCenterZ = mapRadius;
90 var numPlayers = getNumPlayers();
91 var baseRadius = 15;
92 var minPlayerRadius = Math.min(mapRadius - 1.5 * baseRadius, 5/8 * mapRadius);
93 var maxPlayerRadius = Math.min(mapRadius - baseRadius, 3/4 * mapRadius);
95 var playerStartLocX = [];
96 var playerStartLocZ = [];
97 var playerAngleStart = randomAngle();
98 var playerAngleAddAvrg = 2 * Math.PI / numPlayers;
99 var playerAngleMaxOff = playerAngleAddAvrg/4;
101 var pathSucsessRadius = baseRadius/2;
102 var pathAngleOff = Math.PI/2;
103 var pathWidth = 10; // This is not really the path's thickness in tiles but the number of tiles in the clumbs of the path
105 var resourceRadius = 2/3 * mapRadius;
107 // Setup woods
108 // For large maps there are memory errors with too many trees.  A density of 256*192/mapArea works with 0 players.
109 // Around each player there is an area without trees so with more players the max density can increase a bit.
110 var maxTreeDensity = Math.min(256 * (192 + 8 * numPlayers) / mapArea, 1); // Has to be tweeked but works ok
111 var bushChance = 1/3; // 1 means 50% chance in deepest wood, 0.5 means 25% chance in deepest wood
113 ////////////////
114 // Set height limits and water level by map size
115 ////////////////
117 // Set target min and max height depending on map size to make average steepness about the same on all map sizes
118 var heightRange = {'min': MIN_HEIGHT * (g_Map.size + 512) / 8192, 'max': MAX_HEIGHT * (g_Map.size + 512) / 8192, 'avg': (MIN_HEIGHT * (g_Map.size + 512) +MAX_HEIGHT * (g_Map.size + 512))/16384};
120 // Set average water coverage
121 var averageWaterCoverage = 1/5; // NOTE: Since erosion is not predictable actual water coverage might vary much with the same values
122 var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min);
123 var waterHeightAdjusted = waterHeight + MIN_HEIGHT;
124 setWaterHeight(waterHeight);
126 ////////////////
127 // Generate base terrain
128 ////////////////
130 // Setting a 3x3 Grid as initial heightmap
131 var initialReliefmap = [[heightRange.max, heightRange.max, heightRange.max], [heightRange.max, heightRange.min, heightRange.max], [heightRange.max, heightRange.max, heightRange.max]];
133 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialReliefmap);
134 // Apply simple erosion
135 for (var i = 0; i < 5; i++)
136         globalSmoothHeightmap();
137 rescaleHeightmap(heightRange.min, heightRange.max);
139 //////////
140 // Setup height limit
141 //////////
143 // Height presets
144 var heighLimits = [
145         heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water
146         heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), // 1 Medium Water
147         heightRange.min + (waterHeightAdjusted - heightRange.min), // 2 Shallow water
148         waterHeightAdjusted + 1/8 * (heightRange.max - waterHeightAdjusted), // 3 Shore
149         waterHeightAdjusted + 2/8 * (heightRange.max - waterHeightAdjusted), // 4 Low ground
150         waterHeightAdjusted + 3/8 * (heightRange.max - waterHeightAdjusted), // 5 Player and path height
151         waterHeightAdjusted + 4/8 * (heightRange.max - waterHeightAdjusted), // 6 High ground
152         waterHeightAdjusted + 5/8 * (heightRange.max - waterHeightAdjusted), // 7 Lower forest border
153         waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 8 Forest
154         waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border
155         waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop
157 //////////
158 // Place start locations and apply terrain texture and decorative props
159 //////////
161 // Get start locations
162 var startLocations = getStartLocationsByHeightmap({'min': heighLimits[4], 'max': heighLimits[5]});
164 var playerHeight = (heighLimits[4] + heighLimits[5]) / 2;
166 for (var i=0; i < numPlayers; i++)
168         let playerAngle = (playerAngleStart + i * playerAngleAddAvrg + randFloat(0, playerAngleMaxOff)) % (2* Math.PI);
169         let x = Math.round(mapCenterX + randFloat(minPlayerRadius, maxPlayerRadius) * Math.cos(playerAngle));
170         let z = Math.round(mapCenterZ + randFloat(minPlayerRadius, maxPlayerRadius) * Math.sin(playerAngle));
172         playerStartLocX[i] = x;
173         playerStartLocZ[i] = z;
175         rectangularSmoothToHeight({"x": x,"y": z} , 20, 20, playerHeight, 0.8);
178 placePlayerBases({
179         "PlayerPlacement": [sortAllPlayers(), playerStartLocX.map(tilesToFraction), playerStartLocZ.map(tilesToFraction)],
180         "BaseResourceClass": clBaseResource,
181         "Walls": false,
182         // player class painted below
183         "CityPatch": {
184                 "radius": 0.8 * baseRadius,
185                 "smoothness": 1/8,
186                 "painters": [
187                         new TerrainPainter([baseTex], [baseRadius/4, baseRadius/4]),
188                         paintClass(clPlayer)
189                 ]
190         },
191         // No chicken
192         "Berries": {
193                 "template": "gaia/flora_bush_berry",
194                 "minCount": 2,
195                 "maxCount": 2,
196                 "minDist": 10,
197                 "maxDist": 10
198         },
199         "Mines": {
200                 "types": [
201                         { "template": templateMetalMine },
202                         { "template": templateStoneMine }
203                 ],
204                 "distance": 15,
205                 "minAngle": Math.PI / 2,
206                 "maxAngle": Math.PI
207         },
208         "Trees": {
209                 "template": "gaia/flora_tree_oak_large",
210                 "count": 2
211         }
214 log("Creating mines...");
215 for (let [minHeight, maxHeight] of [[heighLimits[3], (heighLimits[4] + heighLimits[3]) / 2], [(heighLimits[5] + heighLimits[6]) / 2, heighLimits[7]]])
216         for (let [template, tileClass] of [[templateStoneMine, clRock], [templateMetalMine, clMetal]])
217                 createObjectGroups(
218                         new SimpleGroup([new SimpleObject(template, 1, 1, 0, 4)], true, tileClass),
219                         0,
220                         [
221                                 new HeightConstraint(minHeight, maxHeight),
222                                 avoidClasses(clForest, 4, clPlayer, 20, clMetal, 40, clRock, 40)
223                         ],
224                         scaleByMapSize(2, 8),
225                         100,
226                         false);
228 Engine.SetProgress(50);
230 //place water & open terrain textures and assign TileClasses
231 log("Painting textures...");
233 var betweenShallowAndShore = (heighLimits[3] + heighLimits[2]) / 2;
234 createArea(
235         new HeightPlacer(Elevation_IncludeMin_IncludeMax, heighLimits[2], betweenShallowAndShore),
236         new LayeredPainter([terrainBase, terrainBaseBorder], [5]));
238 paintTileClassBasedOnHeight(heighLimits[2], betweenShallowAndShore, 1, clOpen);
240 createArea(
241         new HeightPlacer(Elevation_IncludeMin_IncludeMax, heightRange.min, heighLimits[2]),
242         new LayeredPainter([tWaterBorder, tWater], [2]));
244 paintTileClassBasedOnHeight(heightRange.min,  heighLimits[2], 1, clWater);
246 Engine.SetProgress(60);
248 log("Placing paths...");
250 var doublePaths = true;
251 if (numPlayers > 4)
252         doublePaths = false;
254 if (doublePaths === true)
255         var maxI = numPlayers+1;
256 else
257         var maxI = numPlayers;
259 for (var i = 0; i < maxI; i++)
261         if (doublePaths === true)
262                 var minJ = 0;
263         else
264                 var minJ = i+1;
266         for (var j = minJ; j < numPlayers+1; j++)
267         {
268                 // Setup start and target coordinates
269                 if (i < numPlayers)
270                 {
271                         var x = playerStartLocX[i];
272                         var z = playerStartLocZ[i];
273                 }
274                 else
275                 {
276                         var x = mapCenterX;
277                         var z = mapCenterZ;
278                 }
280                 if (j < numPlayers)
281                 {
282                         var targetX = playerStartLocX[j];
283                         var targetZ = playerStartLocZ[j];
284                 }
285                 else
286                 {
287                         var targetX = mapCenterX;
288                         var targetZ = mapCenterZ;
289                 }
291                 // Prepare path placement
292                 var angle = getAngle(x, z, targetX, targetZ);
293                 x += Math.round(pathSucsessRadius * Math.cos(angle));
294                 z += Math.round(pathSucsessRadius * Math.sin(angle));
296                 var targetReached = false;
297                 var tries = 0;
299                 // Placing paths
300                 while (targetReached === false && tries < 2*mapSize)
301                 {
302                         var placer = new ClumpPlacer(pathWidth, 1, 1, 1, x, z);
303                         var painter = [new TerrainPainter(terrainPath), new SmoothElevationPainter(ELEVATION_MODIFY, -0.1, 1.0), paintClass(clPath)];
304                         createArea(placer, painter, avoidClasses(clPath, 0, clOpen, 0 ,clWater, 4, clBaseResource, 4));
306                         // addToClass(x, z, clPath); // Not needed...
307                         // Set vars for next loop
308                         angle = getAngle(x, z, targetX, targetZ);
309                         if (doublePaths === true) // Bended paths
310                         {
311                                 x += Math.round(Math.cos(angle + randFloat(-pathAngleOff/2, 3*pathAngleOff/2)));
312                                 z += Math.round(Math.sin(angle + randFloat(-pathAngleOff/2, 3*pathAngleOff/2)));
313                         }
314                         else // Straight paths
315                         {
316                                 x += Math.round(Math.cos(angle + randFloat(-pathAngleOff, pathAngleOff)));
317                                 z += Math.round(Math.sin(angle + randFloat(-pathAngleOff, pathAngleOff)));
318                         }
320                         if (Math.euclidDistance2D(x, z, targetX, targetZ) < pathSucsessRadius)
321                                 targetReached = true;
323                         tries++;
324                 }
325         }
328 Engine.SetProgress(75);
330 log("Creating decoration...");
331 createDecoration(
332         [
333                 [new SimpleObject(aRockMedium, 1, 3, 0, 1)],
334                 [new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)],
335                 [new SimpleObject(aGrassShort, 1, 2, 0, 1)],
336                 [new SimpleObject(aGrass, 2, 4, 0, 1.8), new SimpleObject(aGrassShort, 3, 6, 1.2, 2.5)],
337                 [new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]
338         ],
339         [
340                 scaleByMapSize(16, 262),
341                 scaleByMapSize(8, 131),
342                 scaleByMapSize(13, 200),
343                 scaleByMapSize(13, 200),
344                 scaleByMapSize(13, 200)
345         ],
346         avoidClasses(clForest, 1, clPlayer, 0, clPath, 3, clWater, 3));
348 Engine.SetProgress(80);
350 log("Growing fish...");
351 createFood(
352         [
353                 [new SimpleObject(oFish, 2, 3, 0, 2)]
354         ],
355         [
356                 100 * numPlayers
357         ],
358         [avoidClasses(clFood, 5), stayClasses(clWater, 4)],
359         clFood);
361 Engine.SetProgress(85);
363 log("Planting reeds...");
364 var types = [aReeds];
365 for (let type of types)
366         createObjectGroupsDeprecated(
367                 new SimpleGroup([new SimpleObject(type, 1, 1, 0, 0)], true),
368                 0,
369                 borderClasses(clWater, 0, 6),
370                 scaleByMapSize(1, 2) * 1000,
371                 1000);
373 Engine.SetProgress(90);
375 log("Planting trees...");
376 for (var x = 0; x < mapSize; x++)
377         for (var z = 0;z < mapSize;z++)
378         {
379                 if (!g_Map.validT(x, z))
380                         continue;
382                 // The 0.5 is a correction for the entities placed on the center of tiles
383                 var radius = Math.euclidDistance2D(x + 0.5, z + 0.5, mapCenterX, mapCenterZ);
384                 var minDistToSL = mapSize;
385                 for (var i=0; i < numPlayers; i++)
386                         minDistToSL = Math.min(minDistToSL, Math.euclidDistance2D(playerStartLocX[i], playerStartLocZ[i], x, z));
388                 // Woods tile based
389                 var tDensFactSL = Math.max(Math.min((minDistToSL - baseRadius) / baseRadius, 1), 0);
390                 var tDensFactRad = Math.abs((resourceRadius - radius) / resourceRadius);
391                 var tDensActual = (maxTreeDensity * tDensFactSL * tDensFactRad)*0.75;
393                 if (!randBool(tDensActual))
394                         continue;
396                 let border = tDensActual < randFloat(0, bushChance * maxTreeDensity);
397                 createArea(
398                         new RectPlacer(x, z, x, z),
399                         [
400                                 new TerrainPainter(border ? terrainWoodBorder : terrainWood),
401                                 paintClass(clForest)
402                         ],
403                         border ?
404                                 avoidClasses(clPath, 1, clOpen, 2, clWater, 3, clMetal, 4, clRock, 4) :
405                                 avoidClasses(clPath, 2, clOpen, 3, clWater, 4, clMetal, 4, clRock, 4));
406         }
408 placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clFood, 2, clMetal, 4, clRock, 4));
410 Engine.SetProgress(100);
412 ExportMap();