1 Engine.LoadLibrary('rmgen');
2 Engine.LoadLibrary("heightmap");
4 log('Initializing map...');
10 setFogThickness(0.19);
12 setWaterColor(0.501961, 0.501961, 0.501961);
13 setWaterTint(0.25098, 0.501961, 0.501961);
14 setWaterWaviness(0.5);
16 setWaterMurkiness(0.75);
18 setPPSaturation(0.37);
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();
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;
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
114 // Set height limits and water level by map size
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);
127 // Generate base terrain
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);
140 // Setup height limit
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
158 // Place start locations and apply terrain texture and decorative props
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);
179 "PlayerPlacement": [sortAllPlayers(), playerStartLocX.map(tilesToFraction), playerStartLocZ.map(tilesToFraction)],
180 "BaseResourceClass": clBaseResource,
182 // player class painted below
184 "radius": 0.8 * baseRadius,
187 new TerrainPainter([baseTex], [baseRadius/4, baseRadius/4]),
193 "template": "gaia/flora_bush_berry",
201 { "template": templateMetalMine },
202 { "template": templateStoneMine }
205 "minAngle": Math.PI / 2,
209 "template": "gaia/flora_tree_oak_large",
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]])
218 new SimpleGroup([new SimpleObject(template, 1, 1, 0, 4)], true, tileClass),
221 new HeightConstraint(minHeight, maxHeight),
222 avoidClasses(clForest, 4, clPlayer, 20, clMetal, 40, clRock, 40)
224 scaleByMapSize(2, 8),
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;
235 new HeightPlacer(Elevation_IncludeMin_IncludeMax, heighLimits[2], betweenShallowAndShore),
236 new LayeredPainter([terrainBase, terrainBaseBorder], [5]));
238 paintTileClassBasedOnHeight(heighLimits[2], betweenShallowAndShore, 1, clOpen);
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;
254 if (doublePaths === true)
255 var maxI = numPlayers+1;
257 var maxI = numPlayers;
259 for (var i = 0; i < maxI; i++)
261 if (doublePaths === true)
266 for (var j = minJ; j < numPlayers+1; j++)
268 // Setup start and target coordinates
271 var x = playerStartLocX[i];
272 var z = playerStartLocZ[i];
282 var targetX = playerStartLocX[j];
283 var targetZ = playerStartLocZ[j];
287 var targetX = mapCenterX;
288 var targetZ = mapCenterZ;
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;
300 while (targetReached === false && tries < 2*mapSize)
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
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)));
314 else // Straight paths
316 x += Math.round(Math.cos(angle + randFloat(-pathAngleOff, pathAngleOff)));
317 z += Math.round(Math.sin(angle + randFloat(-pathAngleOff, pathAngleOff)));
320 if (Math.euclidDistance2D(x, z, targetX, targetZ) < pathSucsessRadius)
321 targetReached = true;
328 Engine.SetProgress(75);
330 log("Creating decoration...");
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)]
340 scaleByMapSize(16, 262),
341 scaleByMapSize(8, 131),
342 scaleByMapSize(13, 200),
343 scaleByMapSize(13, 200),
344 scaleByMapSize(13, 200)
346 avoidClasses(clForest, 1, clPlayer, 0, clPath, 3, clWater, 3));
348 Engine.SetProgress(80);
350 log("Growing fish...");
353 [new SimpleObject(oFish, 2, 3, 0, 2)]
358 [avoidClasses(clFood, 5), stayClasses(clWater, 4)],
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),
369 borderClasses(clWater, 0, 6),
370 scaleByMapSize(1, 2) * 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++)
379 if (!g_Map.validT(x, z))
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));
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))
396 let border = tDensActual < randFloat(0, bushChance * maxTreeDensity);
398 new RectPlacer(x, z, x, z),
400 new TerrainPainter(border ? terrainWoodBorder : terrainWood),
404 avoidClasses(clPath, 1, clOpen, 2, clWater, 3, clMetal, 4, clRock, 4) :
405 avoidClasses(clPath, 2, clOpen, 3, clWater, 4, clMetal, 4, clRock, 4));
408 placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clForest, 1, clFood, 2, clMetal, 4, clRock, 4));
410 Engine.SetProgress(100);