Ticket #3764: realistic_terrain_demo2016-1-31c-noStyle.diff
File realistic_terrain_demo2016-1-31c-noStyle.diff, 61.7 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/maps/random/BaseTerrainDiamondSquare.js
1 /** 2 * ToDo: 3 * Add resources at start locations 4 * Add stone and metal 5 * Place start locations of one team close to each other 6 * 7 * Add parameters for match setup screen (for later use and demonstration) 8 * 9 * Extract some functions to an RMGEN lib 10 * Sort out unneeded functions calling other functions 11 * Clean up 12 */ 13 14 RMS.LoadLibrary("rmgen"); 15 16 const BUILDING_ANGlE = -PI/4; 17 18 /** 19 * initialize map 20 */ 21 22 log("Initializing map..."); 23 24 InitMap(); 25 26 var numPlayers = g_MapSettings.PlayerData.length; 27 var mapSize = getMapSize(); 28 29 30 /** 31 * Actually do stuff 32 */ 33 34 /** 35 * Set target min and max height depending on map size to make average steepness about the same on all map sizes 36 */ 37 var heightRange = {"min": MIN_HEIGHT * (mapSize + 512) / 1024 / 8, "max": MAX_HEIGHT * (mapSize + 512) / 1024 / 8}; 38 39 /** 40 * Set average water coverage 41 */ 42 var averageWaterCoverage = 1/5; // NOTE: Since erosion is not predictable actual water coverage might vary much with the same values 43 var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); 44 var waterHeightAdjusted = waterHeight + MIN_HEIGHT; 45 setWaterHeight(waterHeight); 46 47 /** 48 * Generate initial reliefmap 49 */ 50 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, 0.5); 51 52 53 /** 54 * Apply simple erosion 55 */ 56 for (var i = 0; i < 5; i++) 57 globalSmoothHeightmap(0.5); 58 59 60 /** 61 * Place start locations and apply terrain texture and decorative props by height 62 */ 63 64 /** 65 * Set random biome 66 */ 67 randomizeBiome(); 68 69 /** 70 * Define texture textures and decorative props (and density) by height 71 */ 72 var textueByHeight = []; 73 textueByHeight.push({"height": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "texture": rbt15, "actor": [rbe9, 0.2]}); 74 textueByHeight.push({"height": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "texture": rbt15, "actor": [undefined, 0]}); 75 textueByHeight.push({"height": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "texture": rbt15, "actor": [rba3, 0.2]}); 76 textueByHeight.push({"height": waterHeightAdjusted + 1/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt14, "actor": [rbe5, 0.1]}); 77 textueByHeight.push({"height": waterHeightAdjusted + 2/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt8, "actor": [undefined, 0]}); 78 textueByHeight.push({"height": waterHeightAdjusted + 3/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt9, "actor": [undefined, 0]}); 79 textueByHeight.push({"height": waterHeightAdjusted + 4/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt1, "actor": [undefined, 0]}); 80 textueByHeight.push({"height": waterHeightAdjusted + 5/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt5, "actor": [rbe3, 0.2]}); 81 textueByHeight.push({"height": waterHeightAdjusted + 6/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt2, "actor": [rbe1, 0.4]}); 82 textueByHeight.push({"height": waterHeightAdjusted + 7/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt3, "actor": [rbe2, 0.4]}); 83 textueByHeight.push({"height": waterHeightAdjusted + 8/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt7, "actor": [rbe4, 0.2]}); 84 textueByHeight.push({"height": waterHeightAdjusted + 9/9 * (heightRange.max - waterHeightAdjusted), "texture": rbt4, "actor": [undefined, 0]}); 85 86 /** 87 * Get start locations 88 */ 89 rescaleHeightmap(heightRange.min, heightRange.max); 90 var startLocations = getStartLocationsByHeightmap({"min": textueByHeight[4].height, "max": textueByHeight[5].height}); 91 92 /** 93 * Smooth ground at start locations and place starting entities 94 */ 95 var playerHeight = (textueByHeight[4].height + textueByHeight[5].height) / 2; 96 for (var p = 0; p < g_MapSettings.PlayerData.length - 1; p++) 97 { 98 log("p = " + uneval(p)); 99 log("startLocations[" + p + "] = " + uneval(startLocations[p])); 100 rectangularSmoothToHeight(startLocations[p], 30, 30, playerHeight); 101 placeCivDefaultEntities(startLocations[p].x, startLocations[p].y, p + 1, BUILDING_ANGlE, {"iberWall": true}); 102 } 103 104 /** 105 * Normalize and apply reliefmap 106 */ 107 rescaleHeightmap(heightRange.min, heightRange.max); 108 109 /** 110 * Place terrain texture and decorative props by height 111 */ 112 for(var x = 0; x < mapSize; x++) 113 { 114 for (var y = 0; y < mapSize; y++) 115 { 116 var textureMinHeight = heightRange.min; 117 for (var i = 0; i < textueByHeight.length; i++) 118 { 119 if (g_Map.height[x][y] >= textureMinHeight && g_Map.height[x][y] <= textueByHeight[i].height) 120 { 121 if (typeof(textueByHeight[i].texture) == "string") 122 g_Map.setTexture(x, y, textueByHeight[i].texture); 123 else 124 g_Map.setTexture(x, y, textueByHeight[i].texture[randInt(textueByHeight[i].texture.length)]); 125 if (textueByHeight[i].actor[0]) 126 { 127 var scaleByDistToPlayer = 1; // ToDo: Make an approximate faster range check!!! 128 for (var p = 0; p < g_MapSettings.PlayerData.length; p++) 129 scaleByDistToPlayer = min(getDistance(x, y, startLocations[p].x, startLocations[p].y) / 50, scaleByDistToPlayer); 130 if (randFloat() < textueByHeight[i].actor[1] * scaleByDistToPlayer) 131 placeObject(x + 0.5, y + 0.5, textueByHeight[i].actor[0], 0, randFloat(0, 2*PI)); 132 } 133 break; 134 } 135 else 136 { 137 textureMinHeight = textueByHeight[i].height; 138 } 139 } 140 } 141 } 142 143 /** 144 * Export map data 145 */ 146 ExportMap(); -
binaries/data/mods/public/maps/random/BaseTerrainDiamondSquare.json
1 { 2 "settings" : { 3 "Name" : "Base Terrain Diamond Square Test", 4 "Script" : "BaseTerrainDiamondSquare.js", 5 "Description" : "Testmap for the diamond-square base terrain generation.", 6 "CircularMap" : false, 7 "BaseTerrain" : "grass1_spring", 8 "BaseHeight" : 0, 9 "Keywords": ["demo"], 10 "XXXXXX" : "Optionally define other things here, like we would for a scenario" 11 } 12 } 13 No newline at end of file -
binaries/data/mods/public/maps/random/belgian_uplands.js
17 22 var mapSize = getMapSize(); 18 23 19 24 20 / /////////21 //Heightmap functionality22 //////////25 /** 26 * Heightmap functionality 27 */ 23 28 24 // Some general heightmap settings 25 const MIN_HEIGHT = - SEA_LEVEL; // 20, should be set in the libs! 26 const MAX_HEIGHT = 0xFFFF/HEIGHT_UNITS_PER_METRE - SEA_LEVEL; // A bit smaler than 90, should be set in the libs! 27 28 // Add random heightmap generation functionality 29 /** 30 * Add random heightmap generation functionality 31 */ 29 32 function getRandomReliefmap(minHeight, maxHeight) 30 33 { 31 34 minHeight = (minHeight || MIN_HEIGHT); … … 118 128 } 119 129 120 130 121 / /////////122 //Prepare for hightmap munipulation123 //////////131 /** 132 * Prepare for hightmap munipulation 133 */ 124 134 125 // Set target min and max height depending on map size to make average stepness the same on all map sizes 126 var heightRange = {"min": MIN_HEIGHT * mapSize / 8192, "max": MAX_HEIGHT * mapSize / 8192}; 135 /** 136 * Set target min and max height depending on map size to make average stepness the same on all map sizes 137 */ 138 var heightRange = { "min": MIN_HEIGHT * (mapSize + 256) / 8192, "max": MAX_HEIGHT * (mapSize + 256) / 8192 }; 127 139 128 // Set average water coverage 140 /** 141 * Set average water coverage 142 */ 129 143 var averageWaterCoverage = 1/3; // NOTE: Since errosion is not predictable actual water coverage might differ much with the same value 130 144 if (mapSize < 200) // Sink the waterlevel on tiny maps to ensure enough space 131 145 averageWaterCoverage = 2/3 * averageWaterCoverage; … … 134 148 setWaterHeight(waterHeight); 135 149 136 150 137 ////////// 138 // Prepare terrain texture by height placement 139 ////////// 140 151 /** 152 * Prepare terrain texture by height placement 153 */ 141 154 var textueByHeight = []; 142 155 143 // Deep water 144 textueByHeight.push({"upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks"}); 145 // Medium deep water (with fish) 146 var terreins = ["temp_sea_weed"]; 147 terreins = terreins.concat(terreins, terreins, terreins, terreins); 148 terreins = terreins.concat(terreins, terreins, terreins, terreins); 149 terreins.push("temp_sea_weed|gaia/fauna_fish"); 150 textueByHeight.push({"upperHeightLimit": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "terrain": terreins}); 151 // Flat Water 152 textueByHeight.push({"upperHeightLimit": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_mud_a"}); 153 // Water surroundings/bog (with stone/metal some rabits and bushes) 154 var terreins = ["temp_plants_bog", "temp_plants_bog_aut", "temp_dirt_gravel_plants", "temp_grass_d"]; 155 terreins = terreins.concat(terreins, terreins, terreins, terreins, terreins); 156 terreins = ["temp_plants_bog|gaia/flora_bush_temperate"].concat(terreins, terreins); 157 terreins = ["temp_dirt_gravel_plants|gaia/geology_metal_temperate", "temp_dirt_gravel_plants|gaia/geology_stone_temperate", "temp_plants_bog|gaia/fauna_rabbit"].concat(terreins, terreins); 158 terreins = ["temp_plants_bog_aut|gaia/flora_tree_dead"].concat(terreins, terreins); 159 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 1/6 * (heightRange.max - waterHeightAdjusted), "terrain": terreins}); 160 // Juicy grass near bog 161 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 2/6 * (heightRange.max - waterHeightAdjusted), 162 "terrain": ["temp_grass", "temp_grass_d", "temp_grass_long_b", "temp_grass_plants"]}); 163 // Medium level grass 164 // var testActor = "actor|geology/decal_stone_medit_a.xml"; 165 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 3/6 * (heightRange.max - waterHeightAdjusted), 166 "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_mossy"]}); 167 // Long grass near forest border 168 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 4/6 * (heightRange.max - waterHeightAdjusted), 169 "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_d", "temp_grass_long_b", "temp_grass_clovers_2", "temp_grass_mossy", "temp_grass_plants"]}); 170 // Forest border (With wood/food plants/deer/rabits) 171 var terreins = ["temp_grass_plants|gaia/flora_tree_euro_beech", "temp_grass_mossy|gaia/flora_tree_poplar", "temp_grass_mossy|gaia/flora_tree_poplar_lombardy", 156 /** 157 * Deep water 158 */ 159 textueByHeight.push({ "upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks" }); 160 161 /** 162 * Medium water (with fish) 163 */ 164 var terrains = ["temp_sea_weed"]; 165 terrains = terrains.concat(terrains, terrains, terrains, terrains); 166 terrains = terrains.concat(terrains, terrains, terrains, terrains); 167 terrains.push("temp_sea_weed|gaia/fauna_fish"); 168 textueByHeight.push({ "upperHeightLimit": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "terrain": terrains }); 169 170 /** 171 * Flat Water 172 */ 173 textueByHeight.push({ "upperHeightLimit": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_mud_a" }); 174 175 /** 176 * Water surroundings/bog (with stone/metal some rabits and bushes) 177 */ 178 var terrains = ["temp_plants_bog", "temp_plants_bog_aut", "temp_dirt_gravel_plants", "temp_grass_d"]; 179 terrains = terrains.concat(terrains, terrains, terrains, terrains, terrains); 180 terrains = ["temp_plants_bog|gaia/flora_bush_temperate"].concat(terrains, terrains); 181 terrains = ["temp_dirt_gravel_plants|gaia/geology_metal_temperate", "temp_dirt_gravel_plants|gaia/geology_stone_temperate", "temp_plants_bog|gaia/fauna_rabbit"].concat(terrains, terrains); 182 terrains = ["temp_plants_bog_aut|gaia/flora_tree_dead"].concat(terrains, terrains); 183 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 1/6 * (heightRange.max - waterHeightAdjusted), "terrain": terrains }); 184 185 /** 186 * Juicy grass near bog 187 */ 188 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 2/6 * (heightRange.max - waterHeightAdjusted), 189 "terrain": ["temp_grass", "temp_grass_d", "temp_grass_long_b", "temp_grass_plants"] }); 190 191 /** 192 * Medium level grass 193 */ 194 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 3/6 * (heightRange.max - waterHeightAdjusted), 195 "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_mossy"] }); 196 197 /** 198 * Long grass near forest border 199 */ 200 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 4/6 * (heightRange.max - waterHeightAdjusted), 201 "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_d", "temp_grass_long_b", "temp_grass_clovers_2", "temp_grass_mossy", "temp_grass_plants"] }); 202 203 /** 204 * Forest border (With wood/food plants/deer/rabits) 205 */ 206 var terrains = ["temp_grass_plants|gaia/flora_tree_euro_beech", "temp_grass_mossy|gaia/flora_tree_poplar", "temp_grass_mossy|gaia/flora_tree_poplar_lombardy", 172 207 "temp_grass_long|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_badlands", 173 208 "temp_grass_long|gaia/flora_tree_apple", "temp_grass_clovers|gaia/flora_bush_berry", "temp_grass_clovers_2|gaia/flora_bush_grapes", 174 209 "temp_grass_plants|gaia/fauna_deer", "temp_grass_long_b|gaia/fauna_rabbit"]; 175 var numTerreins = terreins.length; 176 for (var i = 0; i < numTerreins; i++) 177 terreins.push("temp_grass_plants"); 178 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 5/6 * (heightRange.max - waterHeightAdjusted), "terrain": terreins}); 179 // Unpassable woods 180 textueByHeight.push({"upperHeightLimit": waterHeightAdjusted + 6/6 * (heightRange.max - waterHeightAdjusted), 210 var numTerreins = terrains.length; 211 for (var i = 0; i < numTerreins; ++i) 212 terrains.push("temp_grass_plants"); 213 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 5/6 * (heightRange.max - waterHeightAdjusted), "terrain": terrains }); 214 215 /** 216 * Impassable woods 217 */ 218 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 6/6 * (heightRange.max - waterHeightAdjusted), 181 219 "terrain": ["temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine", 182 220 "temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine", 183 221 "temp_mud_plants|gaia/flora_tree_dead", "temp_plants_bog|gaia/flora_tree_oak_large", 184 "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", "temp_forestfloor_autumn|gaia/flora_tree_carob"] });222 "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", "temp_forestfloor_autumn|gaia/flora_tree_carob"] }); 185 223 var minTerrainDistToBorder = 3; 186 224 187 // Time check 1 225 /** 226 * Time check 1 227 */ 188 228 timeArray.push(new Date().getTime()); 189 229 RMS.SetProgress(5); 190 230 191 192 // START THE GIANT WHILE LOOP: 193 // - Generate Heightmap 194 // - Search valid start position tiles 195 // - Choose a good start position derivation (largest distance between closest players) 196 // - Restart the loop if start positions are invalid or two players are to close to each other 231 /** 232 * START THE GIANT WHILE LOOP: 233 * - Generate Heightmap 234 * - Search valid start position tiles 235 * - Choose a good start position derivation (largest distance between closest players) 236 * - Restart the loop if start positions are invalid or two players are to close to each other 237 */ 197 238 var goodStartPositionsFound = false; 198 239 var minDistBetweenPlayers = 16 + mapSize / 16; // Don't set this higher than 25 for tiny maps! It will take forever with 8 players! 199 240 var enoughTiles = false; 200 241 var tries = 0; 201 242 while (!goodStartPositionsFound) 202 243 { 203 tries++;244 ++tries; 204 245 log("Starting giant while loop try " + tries); 205 246 // Generate reliefmap 206 247 var myReliefmap = getRandomReliefmap(heightRange.min, heightRange.max); 207 for (var i = 0; i < 50 + mapSize/4; i++) // Cycles depend on mapsize (more cycles -> bigger structures)248 for (var i = 0; i < 50 + mapSize/4; ++i) // Cycles depend on mapsize (more cycles -> bigger structures) 208 249 myReliefmap = getHeightErrosionedReliefmap(myReliefmap, 1); 209 250 myReliefmap = getRescaledReliefmap(myReliefmap, heightRange.min, heightRange.max); 210 251 setReliefmap(myReliefmap); … … 241 282 if (isPossible) 242 283 { 243 284 possibleStartPositions.push([x, y]); 244 // placeTerrain(x, y, "blue"); // For debug reasons. Plz don't remove. // Only works properly for 1 loop245 285 } 246 286 } 247 287 } … … 260 300 if (distToCenter < maxDistToCenter) 261 301 { 262 302 possibleStartPositionsTemp.push(possibleStartPositions[i]); 263 // placeTerrain(possibleStartPositions[i][0], possibleStartPositions[i][1], "purple"); // Only works properly for 1 loop264 303 } 265 304 } 266 305 possibleStartPositions = deepcopy(possibleStartPositionsTemp); … … 292 331 if (numLowTiles > minNumLowTiles && numHighTiles > minNumHighTiles) 293 332 { 294 333 possibleStartPositionsTemp.push(possibleStartPositions[i]); 295 // placeTerrain(possibleStartPositions[i][0], possibleStartPositions[i][1], "red"); // Only works properly for 1 loop296 334 } 297 335 } 298 336 possibleStartPositions = deepcopy(possibleStartPositionsTemp); -
binaries/data/mods/public/maps/random/realisticTerrainDemo.js
1 /** 2 * ToDo: 3 * Add further biomes 4 * Place start locations of one team close to each other 5 * Raise the entire map if to much water 6 * 7 * Add support for circular maps 8 * Add parameters for match setup screen (for later use and demonstration) 9 * 10 * Extract some functions to an RMGEN lib 11 * Sort out unneeded functions calling other functions 12 * Clean up 13 */ 14 15 /** 16 * Start Timer 17 */ 18 var genStartTime = new Date().getTime(); 19 20 RMS.LoadLibrary("rmgen"); 21 22 const BUILDING_ANGlE = -PI/4; 23 24 /** 25 * initialize map 26 */ 27 log("Initializing map..."); 28 InitMap(); 29 30 31 /** 32 * Some general functions 33 */ 34 35 /** 36 * Takes an array of 2D points (arrays of length 2) 37 * Returns the order to go through the points for the shortest closed path (array of indices) 38 */ 39 function getOrderOfPointsForShortestClosePath(points) 40 { 41 var order = []; 42 var distances = []; 43 if (points.length <= 3) 44 { 45 for (var i = 0; i < points.length; ++i) 46 order.push(i); 47 48 return order; 49 } 50 51 // Just add the first 3 points 52 var pointsToAdd = deepcopy(points); 53 for (var i = 0; i < min(points.length, 3); ++i) 54 { 55 order.push(i) 56 pointsToAdd.shift(i); 57 if (i) 58 distances.push(getDistance(points[order[i]].x, points[order[i]].y, points[order[i - 1]].x, points[order[i - 1]].y)); 59 } 60 distances.push(getDistance(points[order[0]].x, points[order[0]].y, points[order[order.length - 1]].x, points[order[order.length - 1]].y)) 61 62 // Add remaining points so the path lengthens the least 63 var numPointsToAdd = pointsToAdd.length; 64 for (var i = 0; i < numPointsToAdd; ++i) 65 { 66 var indexToAddTo = undefined; 67 var minEnlengthen = Infinity; 68 var minDist1 = 0; 69 var minDist2 = 0; 70 for (var k = 0; k < order.length; ++k) 71 { 72 var dist1 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[k]].x, points[order[k]].y); 73 var dist2 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[(k + 1) % order.length]].x, points[order[(k + 1) % order.length]].y); 74 var enlengthen = dist1 + dist2 - distances[k]; 75 if (enlengthen < minEnlengthen) 76 { 77 indexToAddTo = k; 78 minEnlengthen = enlengthen; 79 minDist1 = dist1; 80 minDist2 = dist2; 81 } 82 } 83 order.splice(indexToAddTo + 1, 0, i + 3); 84 distances.splice(indexToAddTo, 1, minDist1, minDist2); 85 pointsToAdd.shift(); 86 } 87 88 return order; 89 } 90 91 /* 92 Drags a path to a target height smoothing it at the edges and return some points on the path. 93 94 TODO: 95 Would be nice to tell the function what to do and how often in the arguments 96 Adding painted tiles to a tile class 97 */ 98 function placeRandomPathToHeight(start, paintPathWithTexture, target, targetHeight, width, occurrence, strength, heightmap) 99 { 100 if (paintPathWithTexture === false) 101 paintPathWithTexture = [false, ""]; 102 else if (paintPathWithTexture === true) 103 paintPathWithTexture = [true, ['temp_road', "temp_road_overgrown", 'temp_grass_b']] 104 else 105 paintPathWithTexture = (paintPathWithTexture || [false, ['temp_road', "temp_road_overgrown", 'temp_grass_b']]); 106 var paintPath = paintPathWithTexture[0]; 107 var pathTexture = paintPathWithTexture[1]; 108 width = (width || 10); 109 occurrence = (occurrence || 2); 110 strength = (strength || 0.5); 111 heightmap = (heightmap || g_Map.height); 112 113 var clTempPath = createTileClass(); 114 var targetReached = false; 115 var position = deepcopy(start); 116 while (!targetReached) 117 { 118 rectangularSmoothToHeight(position, width, width, targetHeight, strength, heightmap); 119 if (paintPath) 120 { 121 var placer = new ClumpPlacer(0.2 * width * width, 1, 1, 1, round(position.x), round(position.y)); 122 var painter = [new TerrainPainter(pathTexture), paintClass(clTempPath)]; 123 createArea(placer, painter); 124 } 125 126 // Set vars for next loop 127 var angleToTarget = getAngle(position.x, position.y, target.x, target.y); 128 var angleOff = PI * (randFloat() - 0.5); 129 position.x += occurrence * cos(angleToTarget + angleOff); 130 position.y += occurrence * sin(angleToTarget + angleOff); 131 if (getDistance(position.x, position.y, target.x, target.y) < occurrence / 2) 132 targetReached = true; 133 } 134 return clTempPath; 135 } 136 137 138 /** 139 * Actually do stuff 140 */ 141 142 /** 143 * Set height limits and water level by map size 144 */ 145 146 /** 147 * Set target min and max height depending on map size to make average steepness about the same on all map sizes 148 */ 149 var heightRange = { "min": MIN_HEIGHT * (g_Map.size + 512) / 8192, "max": MAX_HEIGHT * (g_Map.size + 512) / 8192 }; 150 151 /** 152 * Set average water coverage 153 */ 154 var averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value 155 var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine 156 var waterHeightAdjusted = waterHeight + MIN_HEIGHT; // Water height in RMGEN 157 setWaterHeight(waterHeight); 158 159 160 /** 161 * Generate base terrain 162 */ 163 164 var initialReliefmap = undefined; 165 var low = heightRange.min; 166 var med = 0.5 * (heightRange.min + heightRange.max); 167 var high = heightRange.max; 168 /** 169 * Island 170 */ 171 // initialReliefmap = [[low, low, low], [low, high, low], [low, low, low]]; 172 initialReliefmap = [[low, low, low, low], [low, high, high, low], [low, high, high, low], [low, low, low, low]]; 173 /** 174 * Mountain 175 */ 176 // initialReliefmap = [[low, med, low], [med, high, med], [low, med, low]]; 177 /** 178 * Lake 179 */ 180 // initialReliefmap = [[high, high, high], [high, low, high], [high, high, high]]; 181 // initialReliefmap = [[high, high, high, high], [high, low, low, high], [high, low, low, high], [high, high, high, high]]; 182 /** 183 * Extreme Edges 184 */ 185 // initialReliefmap = [[high, med, low], [med, med, med], [low, med, high]]; 186 // initialReliefmap = [[high, med, med, low], [med, med, med, med], [med, med, med, med], [med, med, med, med], [low, med, med, high]]; 187 /** 188 * Extreme Center 189 */ 190 // initialReliefmap = [[med, med, med, med], [med, high, low, med], [med, low, high, med], [med, med, med, med]]; 191 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, /*0.5*/ 0.8, initialReliefmap, g_Map.height); 192 /** 193 * Apply simple erosion 194 */ 195 for (var i = 0; i < 3; ++i) 196 globalSmoothHeightmap(0.5); 197 rescaleHeightmap(heightRange.min, heightRange.max); 198 199 200 /** 201 * Setup biome and texture height limit 202 */ 203 204 /** 205 * myBiome presets 206 */ 207 208 /** 209 * Height presets 210 */ 211 var heighLimits = [ 212 heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water 213 heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), // 1 Medium Water 214 heightRange.min + (waterHeightAdjusted - heightRange.min), // 2 Shallow water 215 waterHeightAdjusted + 1/8 * (heightRange.max - waterHeightAdjusted), // 3 Shore 216 waterHeightAdjusted + 2/8 * (heightRange.max - waterHeightAdjusted), // 4 Low ground 217 waterHeightAdjusted + 3/8 * (heightRange.max - waterHeightAdjusted), // 5 Player and path height 218 waterHeightAdjusted + 4/8 * (heightRange.max - waterHeightAdjusted), // 6 High ground 219 waterHeightAdjusted + 5/8 * (heightRange.max - waterHeightAdjusted), // 7 Lower forest border 220 waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 8 Forest 221 waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border 222 waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop 223 224 /** 225 * Add tile painting presets for each biome 226 */ 227 var dummyActor = "actor|props/special/common/waypoint_flag.xml"; 228 var myBiome = []; 229 /** 230 * temperate 1 231 */ 232 myBiome.push([]); 233 /** 234 * snowy 2 235 */ 236 myBiome.push([]); 237 /** 238 * desert 3 239 */ 240 myBiome.push([]); 241 /** 242 * Alpine 4 243 */ 244 myBiome.push([]); 245 myBiome[myBiome.length - 1].push({ // 0 Deep water 246 "texture": ["shoreline_stoney_a"], "actor": [["gaia/fauna_fish", "actor|geology/stone_granite_boulder.xml"], 0.02], 247 "textureHS": ["alpine_mountainside"], "actorHS": [["gaia/fauna_fish"], 0.1] }); 248 myBiome[myBiome.length - 1].push({ // 1 Medium Water 249 "texture": ["shoreline_stoney_a", "alpine_shore_rocks"], "actor": [["actor|geology/stone_granite_boulder.xml"], 0.03], 250 "textureHS": ["alpine_mountainside"], "actorHS": [["actor|geology/stone_granite_boulder.xml"], 0.0] }); 251 myBiome[myBiome.length - 1].push({ // 2 Shallow water 252 "texture": ["alpine_shore_rocks"], "actor": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_large.xml", "actor|geology/stone_granite_med.xml", "actor|props/flora/reeds_pond_lush_b.xml"], 0.2], 253 "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_med.xml"], 0.1] }); 254 myBiome[myBiome.length - 1].push({ // 3 Shore 255 "texture": ["alpine_shore_rocks_grass_50", "alpine_grass_rocky"], "actor": [["gaia/flora_tree_pine", "gaia/flora_bush_badlands", "actor|geology/highland1_moss.xml", "actor|props/flora/grass_soft_tuft_a.xml", "actor|props/flora/bush.xml"], 0.3], 256 "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/grass_soft_tuft_a.xml"], 0.1] }); 257 myBiome[myBiome.length - 1].push({ // 4 Low ground 258 "texture": ["alpine_dirt_grass_50", "alpine_grass_rocky"], "actor": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml", "actor|props/flora/bush.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2], 259 "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml"], 0.1] }); 260 myBiome[myBiome.length - 1].push({ // 5 Player and path height 261 "texture": ["new_alpine_grass_c", "new_alpine_grass_b", "new_alpine_grass_d"], "actor": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2], 262 "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml"], 0.1] }); 263 myBiome[myBiome.length - 1].push({ // 6 High ground 264 "texture": ["new_alpine_grass_a", "alpine_grass_rocky"], "actor": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml", "actor|props/flora/bush_highlands.xml", "actor|props/flora/grass_medit_flowering_tall.xml"], 0.2], 265 "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml"], 0.1] }); 266 myBiome[myBiome.length - 1].push({ // 7 Lower forest border 267 "texture": ["new_alpine_grass_mossy", "alpine_grass_rocky"], "actor": [["gaia/flora_tree_pine", "actor|props/flora/grass_tufts_a.xml", "gaia/flora_bush_berry", "gaia/flora_bush_berry", "gaia/flora_bush_berry", "actor|geology/highland2_moss.xml", "gaia/fauna_goat", "actor|props/flora/bush_tempe_underbrush.xml"], 0.3], 268 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|props/flora/grass_tufts_a.xml", "actor|geology/highland2_moss.xml"], 0.1] }); 269 myBiome[myBiome.length - 1].push({ // 8 Forest 270 "texture": ["alpine_forrestfloor"], "actor": [["gaia/flora_tree_pine", "gaia/flora_tree_pine", "gaia/flora_tree_pine", "gaia/flora_tree_pine", "actor|geology/highland2_moss.xml", "actor|props/flora/bush_highlands.xml"], 0.5], 271 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland2_moss.xml", "actor|geology/stone_granite_med.xml"], 0.1] }); 272 myBiome[myBiome.length - 1].push({ // 9 Upper forest border 273 "texture": ["alpine_forrestfloor_snow", "new_alpine_grass_dirt_a"], "actor": [["gaia/flora_tree_pine", "actor|geology/snow1.xml"], 0.3], 274 "textureHS": ["alpine_cliff_b"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|geology/snow1.xml"], 0.1] }); 275 myBiome[myBiome.length - 1].push({ // 10 Hilltop 276 "texture": ["alpine_cliff_a"], "actor": [["actor|geology/highland1.xml"], 0.05], 277 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0] }); 278 /** 279 * medit 5 280 */ 281 myBiome.push([]); 282 /** 283 * savanah 6 284 */ 285 myBiome.push([]); 286 /** 287 * tropic 7 288 */ 289 myBiome.push([]); 290 /** 291 * autumn 8 292 */ 293 myBiome.push([]); 294 295 /** 296 * Set random biome 297 */ 298 while (biomeID != 4) 299 randomizeBiome(); 300 log("biomeID = " + biomeID); // DEBUG 301 302 if (biomeID == 4) 303 { 304 g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0 }; 305 g_Environment.Water.WaterBody.Colour = { "r" : 0.0, "g" : 0.05, "b" : 0.1, "a" : 0 }; 306 g_Environment.Water.WaterBody.Murkiness = 0.5; 307 rbt7 = rbt8; 308 rbt8 = rbt1 309 rbt1 = [rbt9[1]]; 310 rbt9 = [rbt9[0]]; 311 } 312 if (biomeID == 6) 313 rbt9 = [rbt9[1]]; 314 if (biomeID == 8) 315 rbt9 = rbt6; 316 317 318 /** 319 * Place start locations and apply terrain texture and decorative props by height 320 */ 321 322 /** 323 * Get start locations 324 */ 325 var startLocations = getStartLocationsByHeightmap({ "min": heighLimits[4], "max": heighLimits[5] }); 326 var playerHeight = (heighLimits[4] + heighLimits[5]) / 2; 327 328 /** 329 * Place start locations 330 */ 331 for (var p = 0; p < g_MapSettings.PlayerData.length - 1; ++p) 332 { 333 rectangularSmoothToHeight(startLocations[p], 35, 35, playerHeight, 0.8); 334 placeCivDefaultEntities(startLocations[p].x, startLocations[p].y, p + 1, BUILDING_ANGlE, { "iberWall": true }); 335 // Place resources at start locations 336 var uDist = 8; 337 var uSpace = 1.2; 338 for (var j = 1; j <= 4; ++j) 339 { 340 var uAngle = BUILDING_ANGlE - PI * (2-j) / 2; 341 var count = 4; 342 for (var numberofentities = 0; numberofentities < count; ++numberofentities) 343 { 344 var ux = startLocations[p].x + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); 345 var uz = startLocations[p].y + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); 346 if (j % 2 == 0) 347 placeObject(ux, uz, rbe6, 0, randFloat(0, 2*PI)); 348 else 349 placeObject(ux, uz, rbe4, 0, randFloat(0, 2*PI)); 350 } 351 } 352 ux = startLocations[p].x + 10 * cos(BUILDING_ANGlE + PI / 4); 353 uz = startLocations[p].y + 10 * sin(BUILDING_ANGlE + PI / 4); 354 placeObject(ux, uz, rbe11, 0, randFloat(0, 2*PI)); 355 ux = startLocations[p].x + 10 * cos(BUILDING_ANGlE - 3 * PI / 4); 356 uz = startLocations[p].y + 10 * sin(BUILDING_ANGlE - 3 * PI / 4); 357 placeObject(ux, uz, rbe13, 0, randFloat(0, 2*PI)); 358 } 359 360 /** 361 * Add further stone and metal mines 362 */ 363 distributeEntitiesByHeight({ "min": heighLimits[4], "max": heighLimits[5] }, startLocations); 364 365 /** 366 * Add paths 367 */ 368 var pathTerrainClasseIDs = []; 369 var pathPointDist = 3; 370 var startLocationOrder = getOrderOfPointsForShortestClosePath(startLocations); 371 // for (var i = 0; i < startLocationOrder.length; ++i) 372 // { 373 // var start = startLocations[startLocationOrder[i]]; 374 // var target = startLocations[startLocationOrder[(i + 1) % startLocationOrder.length]]; 375 // pathTerrainClasseIDs.push(placeRandomPathToHeight(start, [true, "road_rome_a"], target, playerHeight, 10, pathPointDist, 0.2)); 376 // } 377 378 /** 379 * Calculate slope map (Not entirely sure if correct or slopeMap is the correct term) 380 */ 381 var slopeMap = getSlopeMap(); 382 383 /** 384 * Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false (default) for this map to work properly) 385 */ 386 var tchm = getTileCenteredHeightmap(); 387 388 /** 389 * Divide tiles in areas by height and avoid paths 390 */ 391 var areas = []; 392 var pathAreas = []; 393 for (var i = 0; i < pathTerrainClasseIDs.length; ++i) 394 pathAreas.push([]); 395 for (var h = 0; h < heighLimits.length; ++h) 396 areas.push([]); 397 for (var x = 0; x < tchm.length; ++x) 398 { 399 for (var y = 0; y < tchm[0].length; ++y) 400 { 401 var isPath = false; 402 for (var i = 0; i < pathTerrainClasseIDs.length; ++i) 403 { 404 if (getTileClass(pathTerrainClasseIDs[i]).countMembersInRadius(x, y, 0.5)) 405 { 406 pathAreas[i].push({ "x": x, "y": y }); 407 isPath = true; 408 break; 409 } 410 } 411 if (isPath) 412 continue; 413 414 var minHeight = heightRange.min; 415 for (var h = 0; h < heighLimits.length; ++h) 416 { 417 if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h]) 418 { 419 areas[h].push({ "x": x, "y": y }); 420 break; 421 } 422 else 423 minHeight = heighLimits[h]; 424 } 425 } 426 } 427 428 /** 429 * Get max slope of each area 430 */ 431 var minSlope = new Array(areas.length); 432 var maxSlope = new Array(areas.length); 433 for (var h = 0; h < heighLimits.length; ++h) 434 { 435 minSlope[h] = Infinity; 436 maxSlope[h] = 0; 437 for (var t = 0; t < areas[h].length; ++t) 438 { 439 var x = areas[h][t].x; 440 var y = areas[h][t].y; 441 var slope = slopeMap[x][y]; 442 if (slope > maxSlope[h]) 443 maxSlope[h] = slope; 444 if (slope < minSlope[h]) 445 minSlope[h] = slope; 446 } 447 } 448 449 /** 450 * Paint areas by height and slope 451 */ 452 for (var h = 0; h < heighLimits.length; ++h) 453 { 454 for (var t = 0; t < areas[h].length; ++t) 455 { 456 var x = areas[h][t].x; 457 var y = areas[h][t].y; 458 var actor = undefined; 459 if (slopeMap[x][y] < 0.5 * (minSlope[h] + maxSlope[h])) 460 { 461 var texture = myBiome[biomeID - 1][h].texture[randInt(myBiome[biomeID - 1][h].texture.length)]; 462 if (randFloat() < myBiome[biomeID - 1][h].actor[1]) 463 actor = myBiome[biomeID - 1][h].actor[0][randInt(myBiome[biomeID - 1][h].actor[0].length)]; 464 } 465 else 466 { 467 var texture = myBiome[biomeID - 1][h].textureHS[randInt(myBiome[biomeID - 1][h].textureHS.length)]; 468 if (randFloat() < myBiome[biomeID - 1][h].actorHS[1]) 469 actor = myBiome[biomeID - 1][h].actorHS[0][randInt(myBiome[biomeID - 1][h].actorHS[0].length)]; 470 } 471 g_Map.setTexture(x, y, texture); 472 if (actor) 473 placeObject(x + 0.5, y + 0.5, actor, 0, randFloat() * TWO_PI); 474 } 475 } 476 477 /** 478 * Stop Timer 479 */ 480 log("Map generation time: " + (new Date().getTime() - genStartTime)) 481 482 /** 483 * Export map data 484 */ 485 ExportMap(); -
binaries/data/mods/public/maps/random/realisticTerrainDemo.json
1 { 2 "settings" : { 3 "Name" : "Realistic Terrain Demo", 4 "Script" : "realisticTerrainDemo.js", 5 "Description" : "A map to demonstrate playability of more realistic random terrain.", 6 "CircularMap" : false, 7 "BaseTerrain" : "whiteness", 8 "BaseHeight" : 0, 9 "Keywords": ["demo"], 10 "XXXXXX" : "Optionally define other things here, like we would for a scenario" 11 } 12 } 13 No newline at end of file -
binaries/data/mods/public/maps/random/rmgen/heightmap_manipulation.js
1 /** 2 * Heightmap manipulation functionality 3 * 4 * A heightmapt is an array of width arrays of height floats 5 * Width and height is normally mapSize+1 (Number of vertices is one bigger than number of tiles in each direction) 6 * The default heightmap is g_Map.height (See the Map object) 7 * 8 * NOTE (ambiguous naming/potential confusion): 9 * For using this heightmap functionalities it is VITAL that TILE_CENTERED_HEIGHT_MAP = false (default)! 10 * Otherwise TILE_CENTERED_HEIGHT_MAP has nothing to do with any tile centered heightmap in this library! 11 * All tile centered heightmaps in this module are temporary objects only and should never replace g_Map.height! 12 * (Tile centered heightmaps are one less in width and hight than heightmaps used by the engine) 13 * (Multiple conversions will lead to strange smoothing effects at best and potentially break the map entirely!) 14 * In the long run TILE_CENTERED_HEIGHT_MAP should be removed and g_Map.height should never be tile centered... 15 */ 16 17 /** 18 * getMinAndMaxHeight 19 * Optionally takes a heightmap, default is g_Map.height 20 * Returns the minimum and maximum heights present in this heightmap (an associative array of the form {"min": minHeight, "max": maxHeight}) 21 */ 22 function getMinAndMaxHeight(heightmap) 23 { 24 heightmap = (heightmap || g_Map.height) 25 var height = {}; 26 height.min = Infinity; 27 height.max = - Infinity; 28 for (var x = 0; x < heightmap.length; ++x) 29 { 30 for (var y = 0; y < heightmap[x].length; ++y) 31 { 32 if (heightmap[x][y] < height.min) 33 height.min = heightmap[x][y]; 34 else if (heightmap[x][y] > height.max) 35 height.max = heightmap[x][y]; 36 } 37 } 38 return height; 39 } 40 41 /** 42 * rescaleHeightmap 43 * Takes: 44 * Optional minHeight(float), minimum height that should be used for the resulting heightmap, default is MIN_HEIGHT 45 * Optional maxHeight(float), maximum height that should be used for the resulting heightmap, default is MAX_HEIGHT 46 * Optional heightmap, default is g_Map.height 47 * Rescales the heightmap so its minimum and maximum height is as the arguments told (preserving it's global shape) 48 */ 49 function rescaleHeightmap(minHeight, maxHeight, heightmap) 50 { 51 minHeight = (minHeight || MIN_HEIGHT); 52 maxHeight = (maxHeight || MAX_HEIGHT); 53 heightmap = (heightmap || g_Map.height); 54 55 var oldHeightRange = getMinAndMaxHeight(heightmap); 56 var max_x = heightmap.length; 57 var max_y = heightmap[0].length; 58 for (var x = 0; x < max_x; ++x) 59 for (var y = 0; y < max_y; ++y) 60 heightmap[x][y] = minHeight + (heightmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight); 61 } 62 63 /** 64 * getInclineMap 65 * Optionally takes a heightmap, default is g_Map.height 66 * Returns an inclination map corresponding to the tiles between the heightmaps vertices: 67 * array of heightmap width-1 arrays of height-1 vectors (associative arrays) of the from: 68 * {"x": x_slope, "y": y_slope] so a 2D Vector pointing to the hightest incline (with the length the incline in the vectors direction) 69 * NOTE: The x and y coordinates of a tile in the terrain texture map correspond to those of the inclination map 70 */ 71 function getInclineMap(heightmap) 72 { 73 heightmap = (heightmap || g_Map.height); 74 var max_x = heightmap.length - 1; 75 var max_y = heightmap[0].length - 1; 76 var inclineMap = []; 77 for (var x = 0; x < max_x; ++x) 78 { 79 inclineMap[x] = []; 80 for (var y = 0; y < max_y; ++y) 81 { 82 var dx = heightmap[x + 1][y] - heightmap[x][y]; 83 var dy = heightmap[x][y + 1] - heightmap[x][y]; 84 var next_dx = heightmap[x + 1][y + 1] - heightmap[x][y + 1]; 85 var next_dy = heightmap[x + 1][y + 1] - heightmap[x + 1][y]; 86 inclineMap[x][y] = {"x": 0.5 * (dx + next_dx), "y": 0.5 * (dy + next_dy)}; 87 } 88 } 89 return inclineMap; 90 } 91 92 /** 93 * getSlopeMap 94 * Optionally takes an inclineMap, default is getInclineMap(g_Map.height) 95 * Returns a slope map (same form as the a heightmap with one less width and height) 96 * NOTES: 97 * Not normalized. Only returns the steepness (float), not the direction of incline. 98 * The x and y coordinates of a tile in the terrain texture map correspond to those of the slope map 99 */ 100 function getSlopeMap(inclineMap) 101 { 102 inclineMap = (inclineMap || getInclineMap(g_Map.height)); 103 var max_x = inclineMap.length; 104 var slopeMap = new Array(max_x); 105 for (var x = 0; x < max_x; ++x) 106 { 107 var max_y = inclineMap[x].length; 108 slopeMap[x] = new Float32Array(max_y); 109 for (var y = 0; y < max_y; ++y) 110 slopeMap[x][y] = Math.pow(inclineMap[x][y].x * inclineMap[x][y].x + inclineMap[x][y].y * inclineMap[x][y].y, 0.5); 111 } 112 return slopeMap; 113 } 114 115 /** 116 * getTileCenteredHeightmap 117 * Optionally takes a heightmap, default is g_Map.height 118 * Returns an approximation of the heights of the tiles between the vertices, a tile centered heightmap 119 * NOTE: 120 * A tile centered heightmap is one smaller in width and height than an ordinary heightmap 121 * It is meant to e.g. texture a map by height (x/y coordinates correspond to those of the terrain texture map) 122 * Don't use this to override g_Map height (Potentially breaks the map)! 123 */ 124 function getTileCenteredHeightmap(heightmap) 125 { 126 heightmap = (heightmap || g_Map.height); 127 var max_x = heightmap.length - 1; 128 var max_y = heightmap[0].length - 1; 129 var tchm = new Array(max_x); 130 for (var x = 0; x < max_x; ++x) 131 { 132 tchm[x] = new Float32Array(max_y); 133 for (var y = 0; y < max_y; ++y) 134 { 135 tchm[x][y] = 0.25 * (heightmap[x][y] + heightmap[x + 1][y] + heightmap[x][y + 1] + heightmap[x + 1][y + 1]); 136 } 137 } 138 return tchm; 139 } 140 141 /** 142 * getStartLocationsByHeightmap 143 * 144 * Description: 145 * Get start location with the largest minimum distance between players 146 * Takes 147 * hightRange An associative array with keys "min" and "max" each containing a float (the height range start locations are allowed) 148 * maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared 149 * minDistToBorder Optional, default is 20, an integer, how far start locations have to be away from the map border 150 * numberOfPlayers Optional, default is g_MapSettings.PlayerData.length, an integer, how many start locations should be placed 151 * Heightmap Optional, default is g_Map.height, the heightmap for the start locations to be placed on 152 * Returns 153 * An array of 2D points (arrays of length 2) 154 */ 155 function getStartLocationsByHeightmap(hightRange, maxTries, minDistToBorder, numberOfPlayers, heightmap) 156 { 157 maxTries = (maxTries || 1000); 158 minDistToBorder = (minDistToBorder || 20); 159 numberOfPlayers = (numberOfPlayers || g_MapSettings.PlayerData.length); 160 heightmap = (heightmap || g_Map.height); 161 162 var validStartLoc = []; 163 for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; ++x) 164 for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; ++y) 165 if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight 166 validStartLoc.push({"x": x, "y": y}); 167 168 var maxMinDist = 0; 169 for (var tries = 0; tries < maxTries; ++tries) 170 { 171 var startLoc = []; 172 var minDist = heightmap.length; 173 for (var p = 0; p < numberOfPlayers; ++p) 174 startLoc.push(validStartLoc[randInt(validStartLoc.length)]); 175 for (var p1 = 0; p1 < numberOfPlayers - 1; ++p1) 176 { 177 for (var p2 = p1 + 1; p2 < numberOfPlayers; ++p2) 178 { 179 var dist = getDistance(startLoc[p1].x, startLoc[p1].y, startLoc[p2].x, startLoc[p2].y); 180 if (dist < minDist) 181 minDist = dist; 182 } 183 } 184 if (minDist > maxMinDist) 185 { 186 maxMinDist = minDist; 187 var finalStartLoc = startLoc; 188 } 189 } 190 191 return finalStartLoc; 192 } 193 194 /** 195 * distributeEntitiesByHeight 196 * Takes 197 * hightRange An associative array with keys "min" and "max" each containing a float (the height range start locations are allowed) 198 * startLoc An array of 2D points (arrays of length 2) 199 * heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats 200 * entityList Array of entities/actors (strings to be placed with placeObject()) 201 * maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared 202 * minDistance Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border 203 * Returns 204 * An array of 2D points (arrays of length 2) 205 */ 206 function distributeEntitiesByHeight(hightRange, startLoc, entityList, maxTries, minDistance, heightmap) 207 { 208 entityList = (entityList || [rbe11, rbe13]); 209 maxTries = (maxTries || 1000); 210 minDistance = (minDistance || 30); 211 heightmap = (heightmap || g_Map.height); 212 213 var placements = deepcopy(startLoc); 214 var validTiles = []; 215 for (var x = minDistance; x < heightmap.length - minDistance; ++x) 216 for (var y = minDistance; y < heightmap[0].length - minDistance; ++y) 217 if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight 218 validTiles.push({"x": x, "y": y}); 219 220 for (var tries = 0; tries < maxTries; ++tries) 221 { 222 var tile = validTiles[randInt(validTiles.length)]; 223 var isValid = true; 224 for (var p = 0; p < placements.length; ++p) 225 { 226 if (getDistance(placements[p].x, placements[p].y, tile.x, tile.y) < minDistance) 227 { 228 isValid = false; 229 break; 230 } 231 } 232 if (isValid) 233 { 234 placeObject(tile.x, tile.y, entityList[randInt(entityList.length)], 0, randFloat(0, 2*PI)); 235 placements.push(tile); 236 } 237 } 238 } 239 240 /** 241 * setBaseTerrainDiamondSquare 242 * 243 * Description: 244 * Sets the heightmap to a relatively realistic shape. 245 * Takes: 246 * minHeight Optional, Float, Default is the absolute possible minimum height 247 * Minimum height that can occur in the resulting heightmap 248 * maxHeight Optional, Float, Default is the absolute possible maximum height 249 * Maximum height that can occur in the resulting heightmap 250 * smoothness Optional, Float between 0 (rough) to 1 (smooth), Default is 0.5 251 * Lower values (rough) will generate more local structures (but should be smoothed e.g. with erosion later) 252 * initialHeightmap Optional, Array of width Arrays of depth Floats (usually a small square heightmap e.g. 2x2) 253 * Sets some initial heights at the edges (square heightmap of size 2) or more more specifically with bigger initial heightmaps 254 * heightmap Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height 255 * The heightmap that will be set by this function 256 * Returns nothing 257 * NOTES: 258 * min/maxHeight will not necessarily be present in the heightmap 259 * This function only supports rectangular heightmaps. 260 * This means that e.g. the edges (given by initialHeightmap) will not be in the playable map area in circular maps. 261 * The function doubles the size of the initial heightmap (if given, else a random 2x2 one) until it's big enough. 262 * Then the extend is cut. So the impact and scale of the initial heightmap depends on its size and the target map size. 263 */ 264 function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap) 265 { 266 // Make some arguments optional 267 minHeight = (minHeight || MIN_HEIGHT); 268 maxHeight = (maxHeight || MAX_HEIGHT); 269 var heightRange = maxHeight - minHeight; 270 if (heightRange <= 0) 271 warn("setBaseTerrainDiamondSquare: heightRange < 0"); 272 smoothness = (smoothness || 0.5); 273 initialHeightmap = (initialHeightmap || [[randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)], [randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)]]); 274 heightmap = (heightmap || g_Map.height); 275 276 var offset = heightRange / 2; 277 278 // Double initialHeightmap width until target width is reached (diamond square method) 279 while (initialHeightmap.length < heightmap.length) 280 { 281 var newHeightmap = []; 282 var oldWidth = initialHeightmap.length; 283 // Square 284 for (var x = 0; x < 2 * oldWidth - 1; ++x) 285 { 286 newHeightmap.push([]); 287 for (var y = 0; y < 2 * oldWidth - 1; ++y) 288 { 289 if (x % 2 == 0 && y % 2 == 0) // Old tile 290 newHeightmap[x].push(initialHeightmap[x/2][y/2]); 291 else if (x % 2 == 1 && y % 2 == 1) // New tile with diagonal old tile neighbors 292 { 293 newHeightmap[x].push((initialHeightmap[(x-1)/2][(y-1)/2] + initialHeightmap[(x+1)/2][(y-1)/2] + initialHeightmap[(x-1)/2][(y+1)/2] + initialHeightmap[(x+1)/2][(y+1)/2]) / 4); 294 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 295 } 296 else // New tile with straight old tile neighbors 297 newHeightmap[x].push(undefined); // Define later 298 } 299 } 300 // Diamond 301 for (var x = 0; x < 2 * oldWidth - 1; ++x) 302 { 303 for (var y = 0; y < 2 * oldWidth - 1; ++y) 304 { 305 if (newHeightmap[x][y] === undefined) 306 { 307 if (x > 0 && x + 1 < newHeightmap.length - 1 && y > 0 && y + 1 < newHeightmap.length - 1) // Not a border tile 308 { 309 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 4; 310 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 311 } 312 else if (x < newHeightmap.length - 1 && y > 0 && y < newHeightmap.length - 1) // Left border 313 { 314 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x][y-1]) / 3; 315 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 316 } 317 else if (x > 0 && y > 0 && y < newHeightmap.length - 1) // Right border 318 { 319 newHeightmap[x][y] = (newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3; 320 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 321 } 322 else if (x > 0 && x < newHeightmap.length - 1 && y < newHeightmap.length - 1) // Bottom border 323 { 324 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y]) / 3; 325 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 326 } 327 else if (x > 0 && x < newHeightmap.length - 1 && y > 0) // Top border 328 { 329 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3; 330 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 331 } 332 } 333 } 334 } 335 initialHeightmap = deepcopy(newHeightmap); 336 offset /= Math.pow(2, smoothness); 337 } 338 339 // Cut initialHeightmap to fit target width 340 var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)]; 341 for (var x = 0; x < heightmap.length; ++x) 342 for (var y = 0; y < heightmap[0].length; ++y) 343 heightmap[x][y] = newHeightmap[x][y]; 344 } 345 346 /** 347 * globalSmoothHeightmap 348 * 349 * Takes: 350 * strength Optional, Float between 0 (no effect) and 1 (2 appending tiles will be set to roughly the same height), default is 0.8 351 * How strong the smooth effect should be 352 * heightmap Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height 353 * The heightmap to be smoothed 354 * Returns nothing 355 * NOTES: 356 * Strength values >0.9 might result in interference. 357 * To strengthen the smooth effect it's better to apply it multiple times than using high strength values. 358 */ 359 function globalSmoothHeightmap(strength, heightmap) 360 { 361 strength = (strength || 0.8); // 0 to 1 362 heightmap = (heightmap || g_Map.height); 363 364 var referenceHeightmap = deepcopy(heightmap); 365 // var map = [[1, 0], [0, 1], [-1, 0], [0, -1]]; // faster 366 var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother 367 var max_x = heightmap.length; 368 var max_y = heightmap[0].length; 369 for (var x = 0; x < max_x; ++x) 370 { 371 for (var y = 0; y < max_y; ++y) 372 { 373 for (var i = 0; i < map.length; ++i) 374 { 375 var mapX = x + map[i][0]; 376 var mapY = y + map[i][1]; 377 if (mapX >= 0 && mapX < max_x && mapY >= 0 && mapY < max_y) 378 heightmap[x][y] += strength / map.length * (referenceHeightmap[mapX][mapY] - referenceHeightmap[x][y]); 379 } 380 } 381 } 382 } 383 384 /** 385 * rectangularSmoothToHeight 386 * 387 * Description: 388 * Pushes a rectangular area towards a given height smoothing it into the original terrain 389 * Takes: 390 * center Associative Array with keys x and y containing Floats (the x/y coordinates of the center) 391 * dx/dy Float, Distance from the center in x/y direction the rectangle ends (half width/depth) 392 * targetHeight Float, Height the center of the rectangle will be pushed to 393 * strength Float between 0 (no change) to 1 (center set to target height), how strong the height is pushed 394 * heightmap Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height 395 * The heightmap to be manipulated 396 * Returns nothing 397 * NOTES: 398 * The window function to determine the smooth is not exactly a gaussian to ensure smooth edges. 399 * The optimal function is not clear so several working ones are left in the function (documented out) 400 */ 401 function rectangularSmoothToHeight(center, dx, dy, targetHeight, strength, heightmap) 402 { 403 var x = round(center.x); 404 var y = round(center.y); 405 dx = round(dx); 406 dy = round(dy); 407 strength = (strength || 1); 408 heightmap = (heightmap || g_Map.height); 409 410 var heightmapWin = []; 411 for (var wx = 0; wx < 2 * dx + 1; ++wx) 412 { 413 heightmapWin.push([]); 414 for (var wy = 0; wy < 2 * dy + 1; ++wy) 415 { 416 var actualX = x - dx + wx; 417 var actualY = y - dy + wy; 418 if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map 419 heightmapWin[wx].push(heightmap[actualX][actualY]); 420 else 421 heightmapWin[wx].push(targetHeight); 422 } 423 } 424 for (var wx = 0; wx < 2 * dx + 1; ++wx) 425 { 426 for (var wy = 0; wy < 2 * dy + 1; ++wy) 427 { 428 var actualX = x - dx + wx; 429 var actualY = y - dy + wy; 430 if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map 431 { 432 // Window function polynomial 2nd degree 433 var scaleX = 1 - (wx / dx - 1) * (wx / dx - 1); 434 var scaleY = 1 - (wy / dy - 1) * (wy / dy - 1); 435 436 heightmap[actualX][actualY] = heightmapWin[wx][wy] + strength * scaleX * scaleY * (targetHeight - heightmapWin[wx][wy]); 437 } 438 } 439 } 440 } -
binaries/data/mods/public/maps/random/rmgen/library.js
1 2 ///////////////////////////////////////////////////////////////////////////////////////////// 3 // Constant definitions 4 ///////////////////////////////////////////////////////////////////////////////////////////// 5 1 /** 2 * Constant definitions 3 */ 6 4 const PI = Math.PI; 7 const TWO_PI = 2 * Math.PI; 5 const TWO_PI = 2 * Math.PI; // mathematically known as 'tau' 8 6 const TERRAIN_SEPARATOR = "|"; 9 const SEA_LEVEL = 20.0;7 const SEA_LEVEL = 160.0; 10 8 const CELL_SIZE = 4; 11 9 const HEIGHT_UNITS_PER_METRE = 92; 12 10 const MIN_MAP_SIZE = 128; 13 11 const MAX_MAP_SIZE = 512; 14 12 const FALLBACK_CIV = "athen"; 15 13 16 ///////////////////////////////////////////////////////////////////////////////////////////// 17 // Utility functions 18 ///////////////////////////////////////////////////////////////////////////////////////////// 14 /** 15 * Constants needed for heightmap_manipulation.js 16 */ 17 const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE // Engine limit, Roughly 700 meters 18 const MIN_HEIGHT = - SEA_LEVEL; 19 const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL; 20 /** 21 * Entity template structure keys that might change, for easier mod support 22 */ 23 const CIV_PLACEHOLDER_STRING = "{civ}"; 24 const START_ENTITY_KEYS = ["StartEntities"]; 25 const START_ENTITY_TEMPLATE_KEYS = ["Template"]; 26 const BUILDER_TEMPLATEPATH_KEYS = ["Builder", "Entities", "_string"]; 27 const PRODUCTION_TEMPLATEPATH_KEYS = ["ProductionQueue", "Entities", "_string"]; 28 const WALLSET_KEYS = ["WallSet"]; 29 const BUILDING_KEYS = ["Obstruction", "Static"]; 19 30 31 32 /** 33 * Utility functions 34 */ 35 20 36 function fractionToTiles(f) 21 37 { 22 38 return getMapSize() * f; … … 394 412 return g_MapSettings.PlayerData.length - 1; 395 413 } 396 414 415 /** 416 * Takes nothing, returns an array of strings representing all available civilizations 417 */ 418 function getCivList() 419 { 420 var raw_civData = RMS.GetCivData(); 421 var civList = []; 422 for (var i = 0; i < raw_civData.length; ++i) 423 civList.push(JSON.parse(raw_civData[i]).Code); 424 425 return civList; 426 } 427 428 /** 429 * Takes nothing, returns an associative array with civ strings as keys containing all unpacked civ data (Templates need to be unpacked with RMS.GetTemplate() if needed) 430 */ 431 function getFullCivData() 432 { 433 var rawCivData = RMS.GetCivData(); 434 var unpackedCivData = {}; 435 for (var i = 0; i < rawCivData.length; ++i) 436 { 437 var singleCivData = JSON.parse(rawCivData[i]); 438 unpackedCivData[singleCivData.Code] = singleCivData; 439 } 440 441 return unpackedCivData; 442 } 443 444 /** 445 * Takes a player number (0-7, so Gaia excluded). Returns this players civ string 446 * ToDo: If the player number is to high an error will occur (and the fallback won't be reached)! 447 */ 397 448 function getCivCode(player) 398 449 { 399 450 if (g_MapSettings.PlayerData[player+1].Civ) 400 451 return g_MapSettings.PlayerData[player+1].Civ; 401 452 402 warn(" undefined civ specified for player " + (player + 1) + ", falling back to '" + FALLBACK_CIV + "'");453 warn("Undefined civ specified for player " + (player + 1) + ", falling back to '" + FALLBACK_CIV + "'"); 403 454 return FALLBACK_CIV; 404 455 } 405 456 457 /** 458 * Takes an entity path and a key list to get the templates value 459 */ 460 function getTemplateValue(entPath, key_list) 461 { 462 var subdata = RMS.GetTemplate(entPath); 463 for (var i = 0; i < key_list.length; ++i) 464 { 465 if (!subdata[key_list[i]]) 466 { 467 return false 468 } 469 subdata = subdata[key_list[i]]; 470 } 471 return subdata; 472 } 473 474 /** 475 * Returns a list of all templates paths available to the given civ 476 */ 477 function getTempatePathList(civ) 478 { 479 var templatePaths = getFullCivData(); 480 if (!templatePaths[civ]) 481 { 482 warn("getTempatePathList: Unknown civ '" + civ + "' not in " + Object.keys(templatePaths)); 483 return false; 484 } 485 templatePaths = templatePaths[civ]; 486 487 if (!templatePaths[START_ENTITY_KEYS]) 488 { 489 warn("getTempatePathList: Civ has no starting entities as defined in START_ENTITY_KEYS (" + START_ENTITY_KEYS + "): " + Object.keys(templatePaths)); 490 return false; 491 } 492 templatePaths = templatePaths[START_ENTITY_KEYS]; 493 494 for (var i = 0; i < templatePaths.length; ++i) 495 { 496 if (!templatePaths[i][START_ENTITY_TEMPLATE_KEYS]) 497 { 498 warn("getTempatePathList: Starting entity list item has no template as defined in START_ENTITY_TEMPLATE_KEYS (" + START_ENTITY_TEMPLATE_KEYS + "): " + Object.keys(templatePaths)); 499 return false; 500 } 501 templatePaths[i] = templatePaths[i][START_ENTITY_TEMPLATE_KEYS]; 502 } 503 var foundNew = 1; 504 while (foundNew > 0) 505 { 506 foundNew = 0; 507 var methods = [BUILDER_TEMPLATEPATH_KEYS, PRODUCTION_TEMPLATEPATH_KEYS]; 508 for (var m = 0; m < methods.length; ++m) 509 { 510 for (var t = 0; t < templatePaths.length; ++t) 511 { 512 var pathsToCheck = getTemplateValue(templatePaths[t], methods[m]); 513 if (typeof(pathsToCheck) === typeof("")) 514 { 515 pathsToCheck = pathsToCheck.split(/\s+/); 516 for (var c = 0; c < pathsToCheck.length; ++c) 517 { 518 var actualPath = pathsToCheck[c].replace(CIV_PLACEHOLDER_STRING, civ); 519 if (templatePaths.indexOf(actualPath) == -1 && RMS.TemplateExists(actualPath)) 520 { 521 templatePaths.push(actualPath); 522 ++foundNew; 523 } 524 } 525 } 526 } 527 } 528 } 529 return templatePaths; 530 } 531 532 /** 533 * Takes a list of entity path strings and returns a list with only those remaining that have the specified keys 534 */ 535 function filterByKeys(entPaths, keys) 536 { 537 let filteredEntPaths = []; 538 for (let i = 0; i < entPaths.length; ++i) 539 { 540 if (getTemplateValue(entPaths[i], keys)) 541 filteredEntPaths.push(entPaths[i]); 542 } 543 return filteredEntPaths; 544 } 545 406 546 function areAllies(player1, player2) 407 547 { 408 548 if ((g_MapSettings.PlayerData[player1+1].Team === undefined) || (g_MapSettings.PlayerData[player2+1].Team === undefined) || (g_MapSettings.PlayerData[player2+1].Team == -1) || (g_MapSettings.PlayerData[player1+1].Team == -1)) … … 642 795 { 643 796 return g_Map.getTexture(x, y); 644 797 } 645 -
binaries/data/mods/public/maps/random/schwarzwald.js
226 242 } 227 243 228 244 229 //////////////// 230 // 231 // Heightmap functionality 232 // 233 //////////////// 245 /** 246 * Heightmap functionality 247 */ 234 248 235 // Some heightmap constants 236 const MIN_HEIGHT = - SEA_LEVEL; // -20 237 const MAX_HEIGHT = 0xFFFF/HEIGHT_UNITS_PER_METRE - SEA_LEVEL; // A bit smaller than 90 238 239 // Get the diferrence between minimum and maxumum height 249 /** 250 * Get the diferrence between minimum and maxumum height 251 */ 240 252 function getMinAndMaxHeight(reliefmap) 241 253 { 242 254 var height = {}; 243 255 height.min = Infinity; 244 256 height.max = - Infinity; 245 for (var x = 0; x < reliefmap.length; x++)257 for (var x = 0; x < reliefmap.length; ++x) 246 258 { 247 for (var y = 0; y < reliefmap[x].length; y++)259 for (var y = 0; y < reliefmap[x].length; ++y) 248 260 { 249 261 if (reliefmap[x][y] < height.min) 250 262 height.min = reliefmap[x][y];