1 RMS.LoadLibrary('rmgen');
2 RMS.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 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();
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 = [];
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;
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
116 // Some general functions
120 function HeightPlacer(lowerBound, upperBound) {
121 this.lowerBound = lowerBound;
122 this.upperBound = upperBound;
125 HeightPlacer.prototype.place = function (constraint) {
126 constraint = (constraint || new NullConstraint());
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));
140 // Set height limits and water level by map size
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);
153 // Generate base terrain
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);
168 // Setup height limit
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
186 // Place start locations and apply terrain texture and decorative props
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
215 var resStartAngle = playerAngle[i] + PI;
216 var resAddAngle = 2*PI / startingResources.length;
217 for (var rIndex = 0; rIndex < startingResources.length; rIndex++)
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);
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]);
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);
247 log("Placing paths...");
249 var doublePaths = true;
253 if (doublePaths === true)
254 var maxI = numPlayers+1;
256 var maxI = numPlayers;
258 for (var i = 0; i < maxI; i++)
260 if (doublePaths === true)
265 for (var j = minJ; j < numPlayers+1; j++)
267 // Setup start and target coordinates
270 var x = playerStartLocX[i];
271 var z = playerStartLocZ[i];
281 var targetX = playerStartLocX[j];
282 var targetZ = playerStartLocZ[j];
286 var targetX = mapCenterX;
287 var targetZ = mapCenterZ;
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;
299 while (targetReached === false && tries < 2*mapSize)
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
310 x += round(cos(angle + randFloat(-pathAngleOff/2, 3*pathAngleOff/2)));
311 z += round(sin(angle + randFloat(-pathAngleOff/2, 3*pathAngleOff/2)));
313 else // Straight paths
315 x += round(cos(angle + randFloat(-pathAngleOff, pathAngleOff)));
316 z += round(sin(angle + randFloat(-pathAngleOff, pathAngleOff)));
319 if (Math.euclidDistance2D(x, z, targetX, targetZ) < pathSucsessRadius)
320 targetReached = true;
329 log("Creating decoration...");
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)]
339 scaleByMapSize(16, 262),
340 scaleByMapSize(8, 131),
341 scaleByMapSize(13, 200),
342 scaleByMapSize(13, 200),
343 scaleByMapSize(13, 200)
345 avoidClasses(clForest, 1, clPlayer, 0, clPath, 3, clWater, 3)
350 log("Growing fish...");
354 [new SimpleObject(oFish, 2,3, 0,2)]
359 [avoidClasses(clFood, 5), stayClasses(clWater, 4)]
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),
370 borderClasses(clWater, 0, 6),
371 scaleByMapSize(1, 2) * 1000,
376 log("Planting trees...");
377 for (var x = 0; x < mapSize; x++)
379 for (var z = 0;z < mapSize;z++)
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));
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)
394 if (tDensActual < randFloat(0, bushChance * maxTreeDensity))
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));
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));}
409 RMS.SetProgress(100);