Remove rmgen euclidian distance helper function, refs rP20328 / D969.
[0ad.git] / binaries / data / mods / public / maps / random / schwarzwald.js
blobaa6500754b849231aacb79fe9fc802061f6a82e9
1 RMS.LoadLibrary('rmgen');
2 RMS.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 clFood = createTileClass();
29 var clBaseResource = createTileClass();
30 var clOpen = createTileClass();
32 var templateStoneMine = 'gaia/geology_stonemine_alpine_quarry';
33 var templateMetalMine = 'gaia/geology_metal_alpine_slabs';
34 var startingResources = ['gaia/flora_tree_pine', 'gaia/flora_tree_pine','gaia/flora_tree_pine', templateStoneMine,
35         'gaia/flora_bush_grapes', 'gaia/flora_tree_aleppo_pine','gaia/flora_tree_aleppo_pine','gaia/flora_tree_aleppo_pine', 'gaia/flora_bush_berry', templateMetalMine];
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 mapRadius = mapSize/2;
86 var playableMapRadius = mapRadius - 5;
87 var mapCenterX = mapRadius;
88 var mapCenterZ = mapRadius;
90 var numPlayers = getNumPlayers();
91 var baseRadius = 15;
92 var minPlayerRadius = min(mapRadius-1.5*baseRadius, 5*mapRadius/8);
93 var maxPlayerRadius = min(mapRadius-baseRadius, 3*mapRadius/4);
95 var playerStartLocX = [];
96 var playerStartLocZ = [];
97 var playerAngle = [];
98 var playerAngleStart = randFloat(0, 2*PI);
99 var playerAngleAddAvrg = 2*PI / numPlayers;
100 var playerAngleMaxOff = playerAngleAddAvrg/4;
102 var pathSucsessRadius = baseRadius/2;
103 var pathAngleOff = PI/2;
104 var pathWidth = 10; // This is not really the path's thickness in tiles but the number of tiles in the clumbs of the path
106 var resourceRadius = 2/3 * mapRadius;
108 // Setup woods
109 // For large maps there are memory errors with too many trees.  A density of 256*192/mapArea works with 0 players.
110 // Around each player there is an area without trees so with more players the max density can increase a bit.
111 var maxTreeDensity = min(256 * (192 + 8 * numPlayers) / (mapSize * mapSize), 1); // Has to be tweeked but works ok
112 var bushChance = 1/3; // 1 means 50% chance in deepest wood, 0.5 means 25% chance in deepest wood
114 ////////////////
116 //  Some general functions
118 ////////////////
120 function HeightPlacer(lowerBound, upperBound) {
121     this.lowerBound = lowerBound;
122     this.upperBound = upperBound;
125 HeightPlacer.prototype.place = function (constraint) {
126         constraint = (constraint || new NullConstraint());
128     var ret = [];
129     for (var x = 0; x < g_Map.size; x++) {
130         for (var y = 0; y < g_Map.size; y++) {
131             if (g_Map.height[x][y] >= this.lowerBound && g_Map.height[x][y] <= this.upperBound && constraint.allows(x, y)) {
132                 ret.push(new PointXZ(x, y));
133             }
134         }
135     }
136     return ret;
139 ////////////////
140 // Set height limits and water level by map size
141 ////////////////
143 // Set target min and max height depending on map size to make average steepness about the same on all map sizes
144 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};
146 // Set average water coverage
147 var averageWaterCoverage = 1/5; // NOTE: Since erosion is not predictable actual water coverage might vary much with the same values
148 var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min);
149 var waterHeightAdjusted = waterHeight + MIN_HEIGHT;
150 setWaterHeight(waterHeight);
152 ////////////////
153 // Generate base terrain
154 ////////////////
156 // Setting a 3x3 Grid as initial heightmap
157 var initialReliefmap = [[heightRange.max, heightRange.max, heightRange.max], [heightRange.max, heightRange.min, heightRange.max], [heightRange.max, heightRange.max, heightRange.max]];
159 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialReliefmap);
160 // Apply simple erosion
161 for (var i = 0; i < 5; i++)
162         globalSmoothHeightmap();
163 rescaleHeightmap(heightRange.min, heightRange.max);
165 RMS.SetProgress(50);
167 //////////
168 // Setup height limit
169 //////////
171 // Height presets
172 var heighLimits = [
173         heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water
174         heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), // 1 Medium Water
175         heightRange.min + (waterHeightAdjusted - heightRange.min), // 2 Shallow water
176         waterHeightAdjusted + 1/8 * (heightRange.max - waterHeightAdjusted), // 3 Shore
177         waterHeightAdjusted + 2/8 * (heightRange.max - waterHeightAdjusted), // 4 Low ground
178         waterHeightAdjusted + 3/8 * (heightRange.max - waterHeightAdjusted), // 5 Player and path height
179         waterHeightAdjusted + 4/8 * (heightRange.max - waterHeightAdjusted), // 6 High ground
180         waterHeightAdjusted + 5/8 * (heightRange.max - waterHeightAdjusted), // 7 Lower forest border
181         waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 8 Forest
182         waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border
183         waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop
185 //////////
186 // Place start locations and apply terrain texture and decorative props
187 //////////
189 // Get start locations
190 var startLocations = getStartLocationsByHeightmap({'min': heighLimits[4], 'max': heighLimits[5]});
192 var playerHeight = (heighLimits[4] + heighLimits[5]) / 2;
194 for (var i=0; i < numPlayers; i++)
196         playerAngle[i] = (playerAngleStart + i*playerAngleAddAvrg + randFloat(0, playerAngleMaxOff))%(2*PI);
198         var x = round(mapCenterX + randFloat(minPlayerRadius, maxPlayerRadius)*cos(playerAngle[i]));
199         var z = round(mapCenterZ + randFloat(minPlayerRadius, maxPlayerRadius)*sin(playerAngle[i]));
201         playerStartLocX[i] = x;
202         playerStartLocZ[i] = z;
204         rectangularSmoothToHeight({"x": x,"y": z} , 20, 20, playerHeight, 0.8);
206         placeCivDefaultEntities(x, z, i+1, { 'iberWall': false });
208         // Place base texture
209         var placer = new ClumpPlacer(2*baseRadius*baseRadius, 2/3, 1/8, 10, x, z);
210         var painter = [new TerrainPainter([baseTex], [baseRadius/4, baseRadius/4]), paintClass(clPlayer)];
211         createArea(placer, painter);
213         // Place starting resources
214         var distToSL = 15;
215         var resStartAngle = playerAngle[i] + PI;
216         var resAddAngle = 2*PI / startingResources.length;
217         for (var rIndex = 0; rIndex < startingResources.length; rIndex++)
218         {
219                 var angleOff = randFloat(-resAddAngle/2, resAddAngle/2);
220                 var placeX = x + distToSL*cos(resStartAngle + rIndex*resAddAngle + angleOff);
221                 var placeZ = z + distToSL*sin(resStartAngle + rIndex*resAddAngle + angleOff);
222                 placeObject(placeX, placeZ, startingResources[rIndex], 0, randFloat(0, 2*PI));
223                 addToClass(round(placeX), round(placeZ), clBaseResource);
224         }
227 // Add further stone and metal mines
228 distributeEntitiesByHeight({ 'min': heighLimits[3], 'max': ((heighLimits[4] + heighLimits[3]) / 2) }, startLocations, 40, [templateStoneMine, templateMetalMine]);
229 distributeEntitiesByHeight({ 'min': ((heighLimits[5] + heighLimits[6]) / 2), 'max': heighLimits[7] }, startLocations, 40, [templateStoneMine, templateMetalMine]);
231 RMS.SetProgress(50);
233 //place water & open terrain textures and assign TileClasses
234 log("Painting textures...");
235 var placer = new HeightPlacer(heighLimits[2], (heighLimits[3]+heighLimits[2])/2);
236 var painter = new LayeredPainter([terrainBase, terrainBaseBorder], [5]);
237 createArea(placer, painter);
238 paintTileClassBasedOnHeight(heighLimits[2], (heighLimits[3]+heighLimits[2])/2, 1, clOpen);
240 var placer = new HeightPlacer(heightRange.min, heighLimits[2]);
241 var painter = new LayeredPainter([tWaterBorder, tWater], [2]);
242 createArea(placer, painter);
243 paintTileClassBasedOnHeight(heightRange.min,  heighLimits[2], 1, clWater);
245 RMS.SetProgress(60);
247 log("Placing paths...");
249 var doublePaths = true;
250 if (numPlayers > 4)
251         doublePaths = false;
253 if (doublePaths === true)
254         var maxI = numPlayers+1;
255 else
256         var maxI = numPlayers;
258 for (var i = 0; i < maxI; i++)
260         if (doublePaths === true)
261                 var minJ = 0;
262         else
263                 var minJ = i+1;
265         for (var j = minJ; j < numPlayers+1; j++)
266         {
267                 // Setup start and target coordinates
268                 if (i < numPlayers)
269                 {
270                         var x = playerStartLocX[i];
271                         var z = playerStartLocZ[i];
272                 }
273                 else
274                 {
275                         var x = mapCenterX;
276                         var z = mapCenterZ;
277                 }
279                 if (j < numPlayers)
280                 {
281                         var targetX = playerStartLocX[j];
282                         var targetZ = playerStartLocZ[j];
283                 }
284                 else
285                 {
286                         var targetX = mapCenterX;
287                         var targetZ = mapCenterZ;
288                 }
290                 // Prepare path placement
291                 var angle = getAngle(x, z, targetX, targetZ);
292                 x += round(pathSucsessRadius*cos(angle));
293                 z += round(pathSucsessRadius*sin(angle));
295                 var targetReached = false;
296                 var tries = 0;
298                 // Placing paths
299                 while (targetReached === false && tries < 2*mapSize)
300                 {
301                         var placer = new ClumpPlacer(pathWidth, 1, 1, 1, x, z);
302                         var painter = [new TerrainPainter(terrainPath), new SmoothElevationPainter(ELEVATION_MODIFY, -0.1, 1.0), paintClass(clPath)];
303                         createArea(placer, painter, avoidClasses(clPath, 0, clOpen, 0 ,clWater, 4, clBaseResource, 4));
305                         // addToClass(x, z, clPath); // Not needed...
306                         // Set vars for next loop
307                         angle = getAngle(x, z, targetX, targetZ);
308                         if (doublePaths === true) // Bended paths
309                         {
310                                 x += round(cos(angle + randFloat(-pathAngleOff/2, 3*pathAngleOff/2)));
311                                 z += round(sin(angle + randFloat(-pathAngleOff/2, 3*pathAngleOff/2)));
312                         }
313                         else // Straight paths
314                         {
315                                 x += round(cos(angle + randFloat(-pathAngleOff, pathAngleOff)));
316                                 z += round(sin(angle + randFloat(-pathAngleOff, pathAngleOff)));
317                         }
319                         if (Math.euclidDistance2D(x, z, targetX, targetZ) < pathSucsessRadius)
320                                 targetReached = true;
322                         tries++;
323                 }
324         }
327 RMS.SetProgress(75);
329 log("Creating decoration...");
330 createDecoration
332  [[new SimpleObject(aRockMedium, 1,3, 0,1)],
333   [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)],
334   [new SimpleObject(aGrassShort, 1,2, 0,1, -PI/8,PI/8)],
335   [new SimpleObject(aGrass, 2,4, 0,1.8, -PI/8,PI/8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -PI/8,PI/8)],
336   [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)]
337  ],
339   scaleByMapSize(16, 262),
340   scaleByMapSize(8, 131),
341   scaleByMapSize(13, 200),
342   scaleByMapSize(13, 200),
343   scaleByMapSize(13, 200)
344  ],
345  avoidClasses(clForest, 1, clPlayer, 0, clPath, 3, clWater, 3)
348 RMS.SetProgress(80);
350 log("Growing fish...");
351 createFood
354   [new SimpleObject(oFish, 2,3, 0,2)]
355  ],
357   100 * numPlayers
358  ],
359  [avoidClasses(clFood, 5), stayClasses(clWater, 4)]
362 RMS.SetProgress(85);
364 log("Planting reeds...");
365 var types = [aReeds];
366 for (let type of types)
367         createObjectGroupsDeprecated(
368                 new SimpleGroup([new SimpleObject(type, 1, 1, 0, 0)], true),
369                 0,
370                 borderClasses(clWater, 0, 6),
371                 scaleByMapSize(1, 2) * 1000,
372                 1000);
374 RMS.SetProgress(90);
376 log("Planting trees...");
377 for (var x = 0; x < mapSize; x++)
379         for (var z = 0;z < mapSize;z++)
380         {
381                 // The 0.5 is a correction for the entities placed on the center of tiles
382                 var radius = Math.euclidDistance2D(x + 0.5, z + 0.5, mapCenterX, mapCenterZ);
383                 var minDistToSL = mapSize;
384                 for (var i=0; i < numPlayers; i++)
385                         minDistToSL = min(minDistToSL, Math.euclidDistance2D(playerStartLocX[i], playerStartLocZ[i], x, z));
387                 // Woods tile based
388                 var tDensFactSL = max(min((minDistToSL - baseRadius) / baseRadius, 1), 0);
389                 var tDensFactRad = abs((resourceRadius - radius) / resourceRadius);
390                 var tDensActual = (maxTreeDensity * tDensFactSL * tDensFactRad)*0.75;
392                 if (randBool(tDensActual) && radius < playableMapRadius)
393                 {
394                         if (tDensActual < randFloat(0, bushChance * maxTreeDensity))
395                         {
396                                 var placer = new ClumpPlacer(1, 1.0, 1.0, 1, x, z);
397                                 var painter = [new TerrainPainter(terrainWoodBorder), paintClass(clForest)];
398                                 createArea(placer, painter, avoidClasses(clPath, 1, clOpen, 2, clWater,3));
399                         }
400                         else
401                         {
402                                 var placer = new ClumpPlacer(1, 1.0, 1.0, 1, x, z);
403                                 var painter = [new TerrainPainter(terrainWood), paintClass(clForest)];
404                                 createArea(placer, painter, avoidClasses(clPath, 2, clOpen, 3, clWater, 4));}
405                 }
406         }
409 RMS.SetProgress(100);
411 ExportMap();