Ticket #3764: realistic_terrain_demo2016-1-31.diff
File realistic_terrain_demo2016-1-31.diff, 118.6 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/maps/random/belgian_uplands.js
1 // Prepare progress calculation 1 /** 2 * Prepare progress calculation 3 */ 2 4 var timeArray = []; 3 5 timeArray.push(new Date().getTime()); 4 6 5 // Importing rmgen libraries 7 /** 8 * Importing rmgen libraries 9 */ 6 10 RMS.LoadLibrary("rmgen"); 7 11 8 12 const BUILDING_ANGlE = -PI/4; 9 13 10 // initialize map 11 14 /** 15 * initialize map 16 */ 12 17 log("Initializing map..."); 13 18 14 19 InitMap(); … … 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); … … 35 38 if (maxHeight > MAX_HEIGHT) 36 39 warn("getRandomReliefmap: Argument maxHeight is smaler then the supported maximum height of " + MAX_HEIGHT + " (const MAX_HEIGHT): " + maxHeight) 37 40 var reliefmap = []; 38 for (var x = 0; x <= mapSize; x++)41 for (var x = 0; x <= mapSize; ++x) 39 42 { 40 43 reliefmap.push([]); 41 for (var y = 0; y <= mapSize; y++)44 for (var y = 0; y <= mapSize; ++y) 42 45 { 43 46 reliefmap[x].push(randFloat(minHeight, maxHeight)); 44 47 } … … 46 49 return reliefmap; 47 50 } 48 51 49 // Apply a heightmap 52 /** 53 * Apply a heightmap 54 */ 50 55 function setReliefmap(reliefmap) 51 56 { 52 57 // g_Map.height = reliefmap; 53 for (var x = 0; x <= mapSize; x++)58 for (var x = 0; x <= mapSize; ++x) 54 59 { 55 for (var y = 0; y <= mapSize; y++)60 for (var y = 0; y <= mapSize; ++y) 56 61 { 57 62 setHeight(x, y, reliefmap[x][y]); 58 63 } 59 64 } 60 65 } 61 66 62 // Get minimum and maxumum height used in a heightmap 67 /** 68 * Get minimum and maxumum height used in a heightmap 69 */ 63 70 function getMinAndMaxHeight(reliefmap) 64 71 { 65 72 var height = {}; 66 73 height.min = Infinity; 67 74 height.max = -Infinity; 68 for (var x = 0; x <= mapSize; x++)75 for (var x = 0; x <= mapSize; ++x) 69 76 { 70 for (var y = 0; y <= mapSize; y++)77 for (var y = 0; y <= mapSize; ++y) 71 78 { 72 79 if (reliefmap[x][y] < height.min) 73 80 height.min = reliefmap[x][y]; … … 78 85 return height; 79 86 } 80 87 81 // Rescale a heightmap (Waterlevel is not taken into consideration!) 88 /** 89 * Rescale a heightmap (Waterlevel is not taken into consideration!) 90 */ 82 91 function getRescaledReliefmap(reliefmap, minHeight, maxHeight) 83 92 { 84 93 var newReliefmap = deepcopy(reliefmap); … … 89 98 if (maxHeight > MAX_HEIGHT) 90 99 warn("getRescaledReliefmap: Argument maxHeight is smaler then the supported maximum height of " + MAX_HEIGHT + " (const MAX_HEIGHT): " + maxHeight) 91 100 var oldHeightRange = getMinAndMaxHeight(reliefmap); 92 for (var x = 0; x <= mapSize; x++)101 for (var x = 0; x <= mapSize; ++x) 93 102 { 94 for (var y = 0; y <= mapSize; y++)103 for (var y = 0; y <= mapSize; ++y) 95 104 { 96 105 newReliefmap[x][y] = minHeight + (reliefmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight); 97 106 } … … 99 108 return newReliefmap 100 109 } 101 110 102 // Applying decay errosion (terrain independent) 111 /** 112 * Applying decay errosion (terrain independent) 113 */ 103 114 function getHeightErrosionedReliefmap(reliefmap, strength) 104 115 { 105 116 var newReliefmap = deepcopy(reliefmap); 106 117 strength = (strength || 1.0); // Values much higher then 1 (1.32+ for an 8 tile map, 1.45+ for a 12 tile map, 1.62+ @ 20 tile map, 0.99 @ 4 tiles) will result in a resonance disaster/self interference 107 118 var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // Default 108 for (var x = 0; x <= mapSize; x++)119 for (var x = 0; x <= mapSize; ++x) 109 120 { 110 for (var y = 0; y <= mapSize; y++)121 for (var y = 0; y <= mapSize; ++y) 111 122 { 112 123 var div = 0; 113 for (var i = 0; i < map.length; i++)124 for (var i = 0; i < map.length; ++i) 114 125 newReliefmap[x][y] += strength / map.length * (reliefmap[(x + map[i][0] + mapSize + 1) % (mapSize + 1)][(y + map[i][1] + mapSize + 1) % (mapSize + 1)] - reliefmap[x][y]); // Not entirely sure if scaling with map.length is perfect but tested values seam to indicate it is 115 126 } 116 127 } … … 118 129 } 119 130 120 131 121 / /////////122 //Prepare for hightmap munipulation123 //////////132 /** 133 * Prepare for hightmap munipulation 134 */ 124 135 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}; 136 /** 137 * Set target min and max height depending on map size to make average stepness the same on all map sizes 138 */ 139 var heightRange = { "min": MIN_HEIGHT * (mapSize + 256) / 8192, "max": MAX_HEIGHT * (mapSize + 256) / 8192 }; 127 140 128 // Set average water coverage 141 /** 142 * Set average water coverage 143 */ 129 144 var averageWaterCoverage = 1/3; // NOTE: Since errosion is not predictable actual water coverage might differ much with the same value 130 145 if (mapSize < 200) // Sink the waterlevel on tiny maps to ensure enough space 131 146 averageWaterCoverage = 2/3 * averageWaterCoverage; … … 134 149 setWaterHeight(waterHeight); 135 150 136 151 137 ////////// 138 // Prepare terrain texture by height placement 139 ////////// 140 152 /** 153 * Prepare terrain texture by height placement 154 */ 141 155 var textueByHeight = []; 142 156 143 // Deep water 144 textueByHeight.push({"upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks"}); 145 // Medium deep water (with fish) 157 /** 158 * Deep water 159 */ 160 textueByHeight.push({ "upperHeightLimit": heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_sea_rocks" }); 161 162 /** 163 * Medium water (with fish) 164 */ 146 165 var terreins = ["temp_sea_weed"]; 147 166 terreins = terreins.concat(terreins, terreins, terreins, terreins); 148 167 terreins = terreins.concat(terreins, terreins, terreins, terreins); 149 168 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) 169 textueByHeight.push({ "upperHeightLimit": heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), "terrain": terreins }); 170 171 /** 172 * Flat Water 173 */ 174 textueByHeight.push({ "upperHeightLimit": heightRange.min + 3/3 * (waterHeightAdjusted - heightRange.min), "terrain": "temp_mud_a" }); 175 176 /** 177 * Water surroundings/bog (with stone/metal some rabits and bushes) 178 */ 154 179 var terreins = ["temp_plants_bog", "temp_plants_bog_aut", "temp_dirt_gravel_plants", "temp_grass_d"]; 155 180 terreins = terreins.concat(terreins, terreins, terreins, terreins, terreins); 156 181 terreins = ["temp_plants_bog|gaia/flora_bush_temperate"].concat(terreins, terreins); 157 182 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 183 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) 184 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 1/6 * (heightRange.max - waterHeightAdjusted), "terrain": terreins }); 185 186 /** 187 * Juicy grass near bog 188 */ 189 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 2/6 * (heightRange.max - waterHeightAdjusted), 190 "terrain": ["temp_grass", "temp_grass_d", "temp_grass_long_b", "temp_grass_plants"] }); 191 192 /** 193 * Medium level grass 194 */ 195 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 3/6 * (heightRange.max - waterHeightAdjusted), 196 "terrain": ["temp_grass", "temp_grass_b", "temp_grass_c", "temp_grass_mossy"] }); 197 198 /** 199 * Long grass near forest border 200 */ 201 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 4/6 * (heightRange.max - waterHeightAdjusted), 202 "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"] }); 203 204 /** 205 * Forest border (With wood/food plants/deer/rabits) 206 */ 171 207 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", 172 208 "temp_grass_long|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_temperate", "temp_mud_plants|gaia/flora_bush_badlands", 173 209 "temp_grass_long|gaia/flora_tree_apple", "temp_grass_clovers|gaia/flora_bush_berry", "temp_grass_clovers_2|gaia/flora_bush_grapes", 174 210 "temp_grass_plants|gaia/fauna_deer", "temp_grass_long_b|gaia/fauna_rabbit"]; 175 211 var numTerreins = terreins.length; 176 for (var i = 0; i < numTerreins; i++)212 for (var i = 0; i < numTerreins; ++i) 177 213 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), 214 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 5/6 * (heightRange.max - waterHeightAdjusted), "terrain": terreins }); 215 216 /** 217 * Unpassable woods 218 */ 219 textueByHeight.push({ "upperHeightLimit": waterHeightAdjusted + 6/6 * (heightRange.max - waterHeightAdjusted), 181 220 "terrain": ["temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine", 182 221 "temp_grass_mossy|gaia/flora_tree_oak", "temp_forestfloor_pine|gaia/flora_tree_pine", 183 222 "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"] });223 "temp_dirt_gravel_plants|gaia/flora_tree_aleppo_pine", "temp_forestfloor_autumn|gaia/flora_tree_carob"] }); 185 224 var minTerrainDistToBorder = 3; 186 225 187 // Time check 1 226 /** 227 * Time check 1 228 */ 188 229 timeArray.push(new Date().getTime()); 189 230 RMS.SetProgress(5); 190 231 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 232 /** 233 * START THE GIANT WHILE LOOP: 234 * - Generate Heightmap 235 * - Search valid start position tiles 236 * - Choose a good start position derivation (largest distance between closest players) 237 * - Restart the loop if start positions are invalid or two players are to close to each other 238 */ 197 239 var goodStartPositionsFound = false; 198 240 var minDistBetweenPlayers = 16 + mapSize / 16; // Don't set this higher than 25 for tiny maps! It will take forever with 8 players! 199 241 var enoughTiles = false; 200 242 var tries = 0; 201 243 while (!goodStartPositionsFound) 202 244 { 203 tries++;245 ++tries; 204 246 log("Starting giant while loop try " + tries); 205 247 // Generate reliefmap 206 248 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)249 for (var i = 0; i < 50 + mapSize/4; ++i) // Cycles depend on mapsize (more cycles -> bigger structures) 208 250 myReliefmap = getHeightErrosionedReliefmap(myReliefmap, 1); 209 251 myReliefmap = getRescaledReliefmap(myReliefmap, heightRange.min, heightRange.max); 210 252 setReliefmap(myReliefmap); … … 217 259 var lowerHeightLimit = textueByHeight[3].upperHeightLimit; 218 260 var upperHeightLimit = textueByHeight[6].upperHeightLimit; 219 261 // Check for valid points by height 220 for (var x = distToBorder + minTerrainDistToBorder; x < mapSize - distToBorder - minTerrainDistToBorder; x++)262 for (var x = distToBorder + minTerrainDistToBorder; x < mapSize - distToBorder - minTerrainDistToBorder; ++x) 221 263 { 222 for (var y = distToBorder + minTerrainDistToBorder; y < mapSize - distToBorder - minTerrainDistToBorder; y++)264 for (var y = distToBorder + minTerrainDistToBorder; y < mapSize - distToBorder - minTerrainDistToBorder; ++y) 223 265 { 224 266 var actualHeight = getHeight(x, y); 225 267 if (actualHeight > lowerHeightLimit && actualHeight < upperHeightLimit) 226 268 { 227 269 // Check for points within a valid area by height (rectangular since faster) 228 270 var isPossible = true; 229 for (var offX = - neededDistance; offX <= neededDistance; offX++)271 for (var offX = - neededDistance; offX <= neededDistance; ++offX) 230 272 { 231 for (var offY = - neededDistance; offY <= neededDistance; offY++)273 for (var offY = - neededDistance; offY <= neededDistance; ++offY) 232 274 { 233 275 var testHeight = getHeight(x + offX, y + offY); 234 276 if (testHeight <= lowerHeightLimit || testHeight >= upperHeightLimit) … … 252 294 // Reduce to tiles in a circle of mapSize / 2 distance to the center (to avoid players placed in corners) 253 295 var possibleStartPositionsTemp = []; 254 296 var maxDistToCenter = mapSize / 2; 255 for (var i = 0; i < possibleStartPositions.length; i++)297 for (var i = 0; i < possibleStartPositions.length; ++i) 256 298 { 257 299 var deltaX = possibleStartPositions[i][0] - mapSize / 2; 258 300 var deltaY = possibleStartPositions[i][1] - mapSize / 2; … … 270 312 var maxDistToResources = distToBorder; // Has to be <= distToBorder! 271 313 var minNumLowTiles = 10; 272 314 var minNumHighTiles = 10; 273 for (var i = 0; i < possibleStartPositions.length; i++)315 for (var i = 0; i < possibleStartPositions.length; ++i) 274 316 { 275 317 var numLowTiles = 0; 276 318 var numHighTiles = 0; 277 for (var dx = - maxDistToResources; dx < maxDistToResources; dx++)319 for (var dx = - maxDistToResources; dx < maxDistToResources; ++dx) 278 320 { 279 for (var dy = - maxDistToResources; dy < maxDistToResources; dy++)321 for (var dy = - maxDistToResources; dy < maxDistToResources; ++dy) 280 322 { 281 323 var testHeight = getHeight(possibleStartPositions[i][0] + dx, possibleStartPositions[i][1] + dy); 282 324 if (testHeight < lowerHeightLimit) 283 numLowTiles++;325 ++numLowTiles; 284 326 if (testHeight > upperHeightLimit) 285 numHighTiles++;327 ++numHighTiles; 286 328 if (numLowTiles > minNumLowTiles && numHighTiles > minNumHighTiles) 287 329 break; 288 330 } … … 311 353 // Get some random start location derivations. NOTE: Itterating over all possible derivations is just to much (valid points ** numPlayers) 312 354 var maxTries = 100000; // floor(800000 / (Math.pow(numPlayers, 2) / 2)); 313 355 var possibleDerivations = []; 314 for (var i = 0; i < maxTries; i++)356 for (var i = 0; i < maxTries; ++i) 315 357 { 316 358 var vector = []; 317 for (var p = 0; p < numPlayers; p++)359 for (var p = 0; p < numPlayers; ++p) 318 360 vector.push(randInt(possibleStartPositions.length)); 319 361 possibleDerivations.push(vector); 320 362 } 321 363 322 364 // Choose the start location derivation with the greatest minimum distance between players 323 365 var maxMinDist = 0; 324 for (var d = 0; d < possibleDerivations.length; d++)366 for (var d = 0; d < possibleDerivations.length; ++d) 325 367 { 326 368 var minDist = 2 * mapSize; 327 for (var p1 = 0; p1 < numPlayers - 1; p1++)369 for (var p1 = 0; p1 < numPlayers - 1; ++p1) 328 370 { 329 for (var p2 = p1 + 1; p2 < numPlayers; p2++)371 for (var p2 = p1 + 1; p2 < numPlayers; ++p2) 330 372 { 331 373 if (p1 != p2) 332 374 { … … 358 400 } // End of derivation check 359 401 } // END THE GIANT WHILE LOOP 360 402 361 // Time check 2 403 /** 404 * Time check 2 405 */ 362 406 timeArray.push(new Date().getTime()); 363 407 RMS.SetProgress(60); 364 408 365 409 366 / ///////367 //Paint terrain by height and add props368 ////////410 /** 411 * Paint terrain by height and add props 412 */ 369 413 370 414 var propDensity = 1; // 1 means as determined in the loop, less for large maps as set below 371 415 if (mapSize > 500) 372 416 propDensity = 1/4; 373 417 else if (mapSize > 400) 374 418 propDensity = 3/4; 375 for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; x++)419 for(var x = minTerrainDistToBorder; x < mapSize - minTerrainDistToBorder; ++x) 376 420 { 377 for (var y = minTerrainDistToBorder; y < mapSize - minTerrainDistToBorder; y++)421 for (var y = minTerrainDistToBorder; y < mapSize - minTerrainDistToBorder; ++y) 378 422 { 379 423 var textureMinHeight = heightRange.min; 380 for (var i = 0; i < textueByHeight.length; i++)424 for (var i = 0; i < textueByHeight.length; ++i) 381 425 { 382 426 if (getHeight(x, y) >= textureMinHeight && getHeight(x, y) <= textueByHeight[i].upperHeightLimit) 383 427 { … … 488 532 } 489 533 } 490 534 491 // Time check 3 535 /** 536 * Time check 3 537 */ 492 538 timeArray.push(new Date().getTime()); 493 539 RMS.SetProgress(90); 494 540 495 541 496 / ///////497 //Place players and start resources498 ////////542 /** 543 * Place players and start resources 544 */ 499 545 500 for (var p = 0; p < numPlayers; p++)546 for (var p = 0; p < numPlayers; ++p) 501 547 { 502 548 var actualX = possibleStartPositions[bestDerivation[p]][0]; 503 549 var actualY = possibleStartPositions[bestDerivation[p]][1]; 504 placeCivDefaultEntities(actualX, actualY, p + 1, BUILDING_ANGlE, { "iberWall" : false});550 placeCivDefaultEntities(actualX, actualY, p + 1, BUILDING_ANGlE, { "iberWall" : false }); 505 551 // Place some start resources 506 552 var uDist = 8; 507 553 var uSpace = 1; … … 509 555 { 510 556 var uAngle = BUILDING_ANGlE - PI * (2-j) / 2; 511 557 var count = 4; 512 for (var numberofentities = 0; numberofentities < count; numberofentities++)558 for (var numberofentities = 0; numberofentities < count; ++numberofentities) 513 559 { 514 560 var ux = actualX + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); 515 561 var uz = actualY + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); … … 521 567 } 522 568 } 523 569 524 525 // Export map data 570 /** 571 * Export map data 572 */ 526 573 ExportMap(); 527 574 528 // Time check 7 575 /** 576 * Time check 7 577 */ 529 578 timeArray.push(new Date().getTime()); 530 579 531 // Calculate progress percentage with the time checks 580 /** 581 * Calculate progress percentage with the time checks 582 */ 532 583 var generationTime = timeArray[timeArray.length - 1] - timeArray[0]; 533 584 log("Total generation time (ms): " + generationTime); 534 for (var i = 0; i < timeArray.length; i++)585 for (var i = 0; i < timeArray.length; ++i) 535 586 { 536 587 var timeSinceStart = timeArray[i] - timeArray[0]; 537 588 var progressPercentage = 100 * timeSinceStart / generationTime; -
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 * DEBUG 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 * ??? 1 231 */ 232 myBiome.push([]); 233 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 234 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 235 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 236 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 237 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 238 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 239 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 240 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 241 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 242 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 243 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 244 /** 245 * ??? 2 246 */ 247 myBiome.push([]); 248 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 249 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 250 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 251 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 252 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 253 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 254 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 255 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 256 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 257 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 258 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 259 /** 260 * ??? 3 261 */ 262 myBiome.push([]); 263 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 264 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 265 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 266 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 267 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 268 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 269 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 270 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 271 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 272 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 273 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 274 /** 275 * Alpine 4 276 */ 277 myBiome.push([]); 278 myBiome[myBiome.length - 1].push({ // 0 Deep water 279 "texture": ["shoreline_stoney_a"], "actor": [["gaia/fauna_fish", "actor|geology/stone_granite_boulder.xml"], 0.02], 280 "textureHS": ["alpine_mountainside"], "actorHS": [["gaia/fauna_fish"], 0.1] }); 281 myBiome[myBiome.length - 1].push({ // 1 Medium Water 282 "texture": ["shoreline_stoney_a", "alpine_shore_rocks"], "actor": [["actor|geology/stone_granite_boulder.xml"], 0.03], 283 "textureHS": ["alpine_mountainside"], "actorHS": [["actor|geology/stone_granite_boulder.xml"], 0.0] }); 284 myBiome[myBiome.length - 1].push({ // 2 Shallow water 285 "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], 286 "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/reeds_pond_dry.xml", "actor|geology/stone_granite_med.xml"], 0.1] }); 287 myBiome[myBiome.length - 1].push({ // 3 Shore 288 "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], 289 "textureHS": ["alpine_mountainside"], "actorHS": [["actor|props/flora/grass_soft_tuft_a.xml"], 0.1] }); 290 myBiome[myBiome.length - 1].push({ // 4 Low ground 291 "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], 292 "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_soft_tuft_a.xml"], 0.1] }); 293 myBiome[myBiome.length - 1].push({ // 5 Player and path height 294 "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], 295 "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_small.xml", "actor|props/flora/grass_soft_small.xml"], 0.1] }); 296 myBiome[myBiome.length - 1].push({ // 6 High ground 297 "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], 298 "textureHS": ["alpine_grass_rocky"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|props/flora/grass_tufts_a.xml"], 0.1] }); 299 myBiome[myBiome.length - 1].push({ // 7 Lower forest border 300 "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], 301 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|props/flora/grass_tufts_a.xml", "actor|geology/highland2_moss.xml"], 0.1] }); 302 myBiome[myBiome.length - 1].push({ // 8 Forest 303 "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], 304 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland2_moss.xml", "actor|geology/stone_granite_med.xml"], 0.1] }); 305 myBiome[myBiome.length - 1].push({ // 9 Upper forest border 306 "texture": ["alpine_forrestfloor_snow", "new_alpine_grass_dirt_a"], "actor": [["gaia/flora_tree_pine", "actor|geology/snow1.xml"], 0.3], 307 "textureHS": ["alpine_cliff_b"], "actorHS": [["actor|geology/stone_granite_med.xml", "actor|geology/snow1.xml"], 0.1] }); 308 myBiome[myBiome.length - 1].push({ // 10 Hilltop 309 "texture": ["alpine_cliff_a"], "actor": [["actor|geology/highland1.xml"], 0.05], 310 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0] }); 311 /** 312 * ??? 5 313 */ 314 myBiome.push([]); 315 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 316 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 317 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 318 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 319 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 320 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 321 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 322 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 323 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 324 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 325 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 326 /** 327 * ??? 6 328 */ 329 myBiome.push([]); 330 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 331 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 332 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 333 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 334 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 335 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 336 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 337 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 338 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 339 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 340 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 341 /** 342 * ??? 7 343 */ 344 myBiome.push([]); 345 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 346 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 347 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 348 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 349 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 350 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 351 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 352 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 353 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 354 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 355 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 356 /** 357 * ??? 8 358 */ 359 myBiome.push([]); 360 myBiome[myBiome.length - 1].push({ "texture": ["blackness"], "actor": [rbe9, 0.1] }); // 1 Deep water 361 myBiome[myBiome.length - 1].push({ "texture": ["blue"], "actor": [[dummyActor], 0.1] }); // 1 Medium Water 362 myBiome[myBiome.length - 1].push({ "texture": ["light blue"], "actor": [[dummyActor], 0.2] }); // 2 Shallow water 363 myBiome[myBiome.length - 1].push({ "texture": ["purple"], "actor": [[dummyActor], 0.1] }); // 3 Shore 364 myBiome[myBiome.length - 1].push({ "texture": ["whiteness"], "actor": [[dummyActor], 0.1] }); // 4 Low ground 365 myBiome[myBiome.length - 1].push({ "texture": ["red"], "actor": [[dummyActor], 0.1] }); // 5 Player and path height 366 myBiome[myBiome.length - 1].push({ "texture": ["yellow"], "actor": [[dummyActor], 0.1] }); // 6 High ground 367 myBiome[myBiome.length - 1].push({ "texture": ["neon green"], "actor": [[dummyActor], 0.2] }); // 7 Lower forest border 368 myBiome[myBiome.length - 1].push({ "texture": ["green"], "actor": [[dummyActor], 0.3] }); // 8 Forest 369 myBiome[myBiome.length - 1].push({ "texture": ["brown"], "actor": [[dummyActor], 0.2] }); // 9 Upper forest border 370 myBiome[myBiome.length - 1].push({ "texture": ["grid_subdiv"], "actor": [[dummyActor], 0.1] }); // 10 Hilltop 371 372 /** 373 * Set random biome 374 */ 375 while (biomeID != 4) 376 randomizeBiome(); 377 log("biomeID = " + biomeID); // DEBUG 378 379 if (biomeID == 4) 380 { 381 g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0 }; 382 g_Environment.Water.WaterBody.Colour = { "r" : 0.0, "g" : 0.05, "b" : 0.1, "a" : 0 }; 383 g_Environment.Water.WaterBody.Murkiness = 0.5; 384 rbt7 = rbt8; 385 rbt8 = rbt1 386 rbt1 = [rbt9[1]]; 387 rbt9 = [rbt9[0]]; 388 } 389 if (biomeID == 6) 390 rbt9 = [rbt9[1]]; 391 if (biomeID == 8) 392 rbt9 = rbt6; 393 394 395 /** 396 * Place start locations and apply terrain texture and decorative props by height 397 */ 398 399 /** 400 * Get start locations 401 */ 402 var startLocations = getStartLocationsByHeightmap({ "min": heighLimits[4], "max": heighLimits[5] }); 403 var playerHeight = (heighLimits[4] + heighLimits[5]) / 2; 404 405 /** 406 * Place start locations 407 */ 408 for (var p = 0; p < g_MapSettings.PlayerData.length - 1; ++p) 409 { 410 rectangularSmoothToHeight(startLocations[p], 35, 35, playerHeight, 0.8); 411 placeCivDefaultEntities(startLocations[p].x, startLocations[p].y, p + 1, BUILDING_ANGlE, { "iberWall": true }); 412 // Place resources at start locations 413 var uDist = 8; 414 var uSpace = 1.2; 415 for (var j = 1; j <= 4; ++j) 416 { 417 var uAngle = BUILDING_ANGlE - PI * (2-j) / 2; 418 var count = 4; 419 for (var numberofentities = 0; numberofentities < count; ++numberofentities) 420 { 421 var ux = startLocations[p].x + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); 422 var uz = startLocations[p].y + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); 423 if (j % 2 == 0) 424 placeObject(ux, uz, rbe6, 0, randFloat(0, 2*PI)); 425 else 426 placeObject(ux, uz, rbe4, 0, randFloat(0, 2*PI)); 427 } 428 } 429 ux = startLocations[p].x + 10 * cos(BUILDING_ANGlE + PI / 4); 430 uz = startLocations[p].y + 10 * sin(BUILDING_ANGlE + PI / 4); 431 // placeObject(ux, uz, rbe11, 0, randFloat(0, 2*PI)); 432 ux = startLocations[p].x + 10 * cos(BUILDING_ANGlE - 3 * PI / 4); 433 uz = startLocations[p].y + 10 * sin(BUILDING_ANGlE - 3 * PI / 4); 434 // placeObject(ux, uz, rbe13, 0, randFloat(0, 2*PI)); 435 } 436 437 /** 438 * Add further stone and metal mines 439 */ 440 distributeEntitiesByHeight({ "min": heighLimits[4], "max": heighLimits[5] }, startLocations); 441 442 /** 443 * Add paths 444 */ 445 var pathTerrainClasseIDs = []; 446 var pathPointDist = 3; 447 var startLocationOrder = getOrderOfPointsForShortestClosePath(startLocations); 448 // for (var i = 0; i < startLocationOrder.length; ++i) 449 // { 450 // var start = startLocations[startLocationOrder[i]]; 451 // var target = startLocations[startLocationOrder[(i + 1) % startLocationOrder.length]]; 452 // pathTerrainClasseIDs.push(placeRandomPathToHeight(start, [true, "road_rome_a"], target, playerHeight, 10, pathPointDist, 0.2)); 453 // } 454 455 /** 456 * Calculate slope map (Not entirely sure if correct or slopeMap is the correct term) 457 */ 458 var slopeMap = getSlopeMap(); 459 460 /** 461 * Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false (default) for this map to work properly) 462 */ 463 var tchm = getTileCenteredHeightmap(); 464 465 /** 466 * Divide tiles in areas by height and avoid paths 467 */ 468 var areas = []; 469 var pathAreas = []; 470 for (var i = 0; i < pathTerrainClasseIDs.length; ++i) 471 pathAreas.push([]); 472 for (var h = 0; h < heighLimits.length; ++h) 473 areas.push([]); 474 for (var x = 0; x < tchm.length; ++x) 475 { 476 for (var y = 0; y < tchm[0].length; ++y) 477 { 478 var isPath = false; 479 for (var i = 0; i < pathTerrainClasseIDs.length; ++i) 480 { 481 if (getTileClass(pathTerrainClasseIDs[i]).countMembersInRadius(x, y, 0.5)) 482 { 483 pathAreas[i].push({ "x": x, "y": y }); 484 isPath = true; 485 break; 486 } 487 } 488 if (!isPath) 489 { 490 var minHeight = heightRange.min; 491 for (var h = 0; h < heighLimits.length; ++h) 492 { 493 if (tchm[x][y] >= minHeight && tchm[x][y] <= heighLimits[h]) 494 { 495 areas[h].push({ "x": x, "y": y }); 496 break; 497 } 498 else 499 minHeight = heighLimits[h]; 500 } 501 } 502 } 503 } 504 505 /** 506 * Get max slope of each area 507 */ 508 var minSlope = new Array(areas.length); 509 var maxSlope = new Array(areas.length); 510 for (var h = 0; h < heighLimits.length; ++h) 511 { 512 minSlope[h] = Infinity; 513 maxSlope[h] = 0; 514 for (var t = 0; t < areas[h].length; ++t) 515 { 516 var x = areas[h][t].x; 517 var y = areas[h][t].y; 518 var slope = slopeMap[x][y]; 519 if (slope > maxSlope[h]) 520 maxSlope[h] = slope; 521 if (slope < minSlope[h]) 522 minSlope[h] = slope; 523 } 524 } 525 526 /** 527 * Paint areas by height and slope 528 */ 529 for (var h = 0; h < heighLimits.length; ++h) 530 { 531 for (var t = 0; t < areas[h].length; ++t) 532 { 533 var x = areas[h][t].x; 534 var y = areas[h][t].y; 535 var actor = undefined; 536 if (slopeMap[x][y] < 0.5 * (minSlope[h] + maxSlope[h])) 537 { 538 var texture = myBiome[biomeID - 1][h].texture[randInt(myBiome[biomeID - 1][h].texture.length)]; 539 if (randFloat() < myBiome[biomeID - 1][h].actor[1]) 540 actor = myBiome[biomeID - 1][h].actor[0][randInt(myBiome[biomeID - 1][h].actor[0].length)]; 541 } 542 else 543 { 544 var texture = myBiome[biomeID - 1][h].textureHS[randInt(myBiome[biomeID - 1][h].textureHS.length)]; 545 if (randFloat() < myBiome[biomeID - 1][h].actorHS[1]) 546 actor = myBiome[biomeID - 1][h].actorHS[0][randInt(myBiome[biomeID - 1][h].actorHS[0].length)]; 547 } 548 g_Map.setTexture(x, y, texture); 549 if (actor) 550 placeObject(x + 0.5, y + 0.5, actor, 0, randFloat() * TWO_PI); 551 } 552 } 553 554 555 /** 556 * DEBUG Collision detection START 557 */ 558 // for (var ox = 0; ox < collision.obstructionMap.length; ++ox) 559 // { 560 // for (var oy = 0; oy < collision.obstructionMap[ox].length; ++oy) 561 // { 562 // if (collision.obstructionMap[ox][oy]) 563 // { 564 // var x = (ox + 0.5) / CELL_SIZE * collision.obstructionGridSizeInMeters; 565 // var y = (oy + 0.5) / CELL_SIZE * collision.obstructionGridSizeInMeters; 566 // // log("ox = " + ox + ", oy = " + oy + ", x = " + x + ", y = " + y); 567 // if (g_Map.validT(x, y)) 568 // placeObject(x, y, "actor|geology/stone_granite_small.xml", 0, randFloat() * TWO_PI); 569 // else 570 // warn("Obstruction cell {x: " + uneval(ox) + ", y: " + uneval(oy) + "} corresponds to a tile outside the map (size = " + uneval(g_Map.height.length) + "): {x: " + uneval(x) + ", y: " + uneval(y) + "}"); 571 // } 572 // } 573 // } 574 /** 575 * DEBUG Collision detection END 576 */ 577 578 /** 579 * DEBUG Stop Timer 580 */ 581 log("Map generation time: " + (new Date().getTime() - genStartTime)) 582 583 /** 584 * Export map data 585 */ 586 ExportMap(); 587 588 /** 589 * DEBUG Entity IDs START 590 */ 591 // for (var i = 0; i < g_Map.objects.length; ++i) 592 // { 593 // var id = g_Map.objects[i].id; 594 // if (i + 150 != id) 595 // log("The " + i + "-th object (should be ID " + uneval(i + 150) + ") has ID " + id); 596 // } 597 /** 598 * DEBUG Entity IDs END 599 */ -
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 // placeObject(tile[0], tile[1], "actor|geology/decal_stone_medit_b.xml", 0, randFloat(0, 2*PI)); 236 placements.push(tile); 237 } 238 } 239 } 240 241 /** 242 * setBaseTerrainDiamondSquare 243 * 244 * Description: 245 * Sets the heightmap to a relatively realistic shape. 246 * Takes: 247 * minHeight Optional, Float, Default is the absolute possible minimum height 248 * Minimum height that can occur in the resulting heightmap 249 * maxHeight Optional, Float, Default is the absolute possible maximum height 250 * Maximum height that can occur in the resulting heightmap 251 * smoothness Optional, Float between 0 (rough) to 1 (smooth), Default is 0.5 252 * Lower values (rough) will generate more local structures (but should be smoothed e.g. with erosion later) 253 * initialHeightmap Optional, Array of width Arrays of depth Floats (usually a small square heightmap e.g. 2x2) 254 * Sets some initial heights at the edges (square heightmap of size 2) or more more specifically with bigger initial heightmaps 255 * heightmap Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height 256 * The heightmap that will be set by this function 257 * Returns nothing 258 * NOTES: 259 * min/maxHeight will not necessarily be present in the heightmap 260 * This function only supports rectangular heightmaps. 261 * This means that e.g. the edges (given by initialHeightmap) will not be in the playable map area in circular maps. 262 * The function doubles the size of the initial heightmap (if given, else a random 2x2 one) until it's big enough. 263 * Then the extend is cut. So the impact and scale of the initial heightmap depends on its size and the target map size. 264 */ 265 function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap) 266 { 267 // Make some arguments optional 268 minHeight = (minHeight || MIN_HEIGHT); 269 maxHeight = (maxHeight || MAX_HEIGHT); 270 var heightRange = maxHeight - minHeight; 271 if (heightRange <= 0) 272 warn("setBaseTerrainDiamondSquare: heightRange < 0"); 273 smoothness = (smoothness || 0.5); 274 initialHeightmap = (initialHeightmap || [[randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)], [randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)]]); 275 heightmap = (heightmap || g_Map.height); 276 277 var offset = heightRange / 2; 278 279 // Double initialHeightmap width until target width is reached (diamond square method) 280 while (initialHeightmap.length < heightmap.length) 281 { 282 var newHeightmap = []; 283 var oldWidth = initialHeightmap.length; 284 // Square 285 for (var x = 0; x < 2 * oldWidth - 1; ++x) 286 { 287 newHeightmap.push([]); 288 for (var y = 0; y < 2 * oldWidth - 1; ++y) 289 { 290 if (x % 2 == 0 && y % 2 == 0) // Old tile 291 newHeightmap[x].push(initialHeightmap[x/2][y/2]); 292 else if (x % 2 == 1 && y % 2 == 1) // New tile with diagonal old tile neighbors 293 { 294 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); 295 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 296 } 297 else // New tile with straight old tile neighbors 298 newHeightmap[x].push(undefined); // Define later 299 } 300 } 301 // Diamond 302 for (var x = 0; x < 2 * oldWidth - 1; ++x) 303 { 304 for (var y = 0; y < 2 * oldWidth - 1; ++y) 305 { 306 if (newHeightmap[x][y] === undefined) 307 { 308 if (x > 0 && x + 1 < newHeightmap.length - 1 && y > 0 && y + 1 < newHeightmap.length - 1) // Not a border tile 309 { 310 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 4; 311 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 312 } 313 else if (x < newHeightmap.length - 1 && y > 0 && y < newHeightmap.length - 1) // Left border 314 { 315 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x][y-1]) / 3; 316 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 317 } 318 else if (x > 0 && y > 0 && y < newHeightmap.length - 1) // Right border 319 { 320 newHeightmap[x][y] = (newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3; 321 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 322 } 323 else if (x > 0 && x < newHeightmap.length - 1 && y < newHeightmap.length - 1) // Bottom border 324 { 325 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y]) / 3; 326 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 327 } 328 else if (x > 0 && x < newHeightmap.length - 1 && y > 0) // Top border 329 { 330 newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3; 331 newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset) 332 } 333 } 334 } 335 } 336 initialHeightmap = deepcopy(newHeightmap); 337 offset /= Math.pow(2, smoothness); 338 } 339 340 // Cut initialHeightmap to fit target width 341 var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)]; 342 for (var x = 0; x < heightmap.length; ++x) 343 for (var y = 0; y < heightmap[0].length; ++y) 344 heightmap[x][y] = newHeightmap[x][y]; 345 } 346 347 /** 348 * globalSmoothHeightmap 349 * 350 * Takes: 351 * strength Optional, Float between 0 (no effect) and 1 (2 appending tiles will be set to roughly the same height), default is 0.8 352 * How strong the smooth effect should be 353 * heightmap Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height 354 * The heightmap to be smoothed 355 * Returns nothing 356 * NOTES: 357 * Strength values >0.9 might result in interference. 358 * To strengthen the smooth effect it's better to apply it multiple times than using high strength values. 359 */ 360 function globalSmoothHeightmap(strength, heightmap) 361 { 362 strength = (strength || 0.8); // 0 to 1 363 heightmap = (heightmap || g_Map.height); 364 365 var referenceHeightmap = deepcopy(heightmap); 366 // var map = [[1, 0], [0, 1], [-1, 0], [0, -1]]; // faster 367 var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother 368 var max_x = heightmap.length; 369 var max_y = heightmap[0].length; 370 for (var x = 0; x < max_x; ++x) 371 { 372 for (var y = 0; y < max_y; ++y) 373 { 374 for (var i = 0; i < map.length; ++i) 375 { 376 var mapX = x + map[i][0]; 377 var mapY = y + map[i][1]; 378 if (mapX >= 0 && mapX < max_x && mapY >= 0 && mapY < max_y) 379 heightmap[x][y] += strength / map.length * (referenceHeightmap[mapX][mapY] - referenceHeightmap[x][y]); 380 } 381 } 382 } 383 } 384 385 /** 386 * rectangularSmoothToHeight 387 * 388 * Description: 389 * Pushes a rectangular area towards a given height smoothing it into the original terrain 390 * Takes: 391 * center Associative Array with keys x and y containing Floats (the x/y coordinates of the center) 392 * dx/dy Float, Distance from the center in x/y direction the rectangle ends (half width/depth) 393 * targetHeight Float, Height the center of the rectangle will be pushed to 394 * strength Float between 0 (no change) to 1 (center set to target height), how strong the height is pushed 395 * heightmap Optional, Array of width Arrays of depth Floats (a heightmap), Default is g_Map.height 396 * The heightmap to be manipulated 397 * Returns nothing 398 * NOTES: 399 * The window function to determine the smooth is not exactly a gaussian to ensure smooth edges. 400 * The optimal function is not clear so several working ones are left in the function (documented out) 401 */ 402 function rectangularSmoothToHeight(center, dx, dy, targetHeight, strength, heightmap) 403 { 404 var x = round(center.x); 405 var y = round(center.y); 406 dx = round(dx); 407 dy = round(dy); 408 strength = (strength || 1); 409 heightmap = (heightmap || g_Map.height); 410 411 var heightmapWin = []; 412 for (var wx = 0; wx < 2 * dx + 1; ++wx) 413 { 414 heightmapWin.push([]); 415 for (var wy = 0; wy < 2 * dy + 1; ++wy) 416 { 417 var actualX = x - dx + wx; 418 var actualY = y - dy + wy; 419 if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map 420 heightmapWin[wx].push(heightmap[actualX][actualY]); 421 else 422 heightmapWin[wx].push(targetHeight); 423 } 424 } 425 for (var wx = 0; wx < 2 * dx + 1; ++wx) 426 { 427 for (var wy = 0; wy < 2 * dy + 1; ++wy) 428 { 429 var actualX = x - dx + wx; 430 var actualY = y - dy + wy; 431 if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map 432 { 433 // // Window function rectangle 434 // var scaleX = 1; 435 // var scaleY = 1; 436 // // Window function triangle 437 // var scaleX = min(min(wx / dx, (2 * dx + 1 - wx) / dx), 1); 438 // var scaleY = min(min(wy / dy, (2 * dy + 1 - wy) / dy), 1); 439 // // Window function trapeze 440 // var scaleX = min(min(1.3 * wx / dx, 1.3 * (2 * dx + 1 - wx) / dx), 1); 441 // var scaleY = min(min(1.3 * wy / dy, 1.3 * (2 * dy + 1 - wy) / dy), 1); 442 // // Window function simple sine (range PI) 443 // var scaleX = sin(wx * PI / (2 * dx + 1)); 444 // var scaleY = sin(wy * PI / (2 * dy + 1)); 445 // // Window function cosine (range 2*PI) 446 // var scaleX = (cos((wx - (2 * dx + 1) / 2) * 2 * PI / (2 * dx + 1)) + 1) / 2; 447 // var scaleY = (cos((wy - (2 * dy + 1) / 2) * 2 * PI / (2 * dy + 1)) + 1) / 2; 448 // // Window function polynomial 2nd degree (BEST SINGLE SO FAR) 449 var scaleX = 1 - (wx / dx - 1) * (wx / dx - 1); 450 var scaleY = 1 - (wy / dy - 1) * (wy / dy - 1); 451 // // Window function polynomial 4th degree 452 // var scaleX = (wx / dx - 1) * (wx / dx - 1) * (wx / dx - 1) * (wx / dx - 1) - 2 * (wx / dx - 1) * (wx / dx - 1) + 1; 453 // var scaleY = (wy / dy - 1) * (wy / dy - 1) * (wy / dy - 1) * (wy / dy - 1) - 2 * (wy / dy - 1) * (wy / dy - 1) + 1; 454 // COMBINATIONS 455 // // Window function arithmetic mean of simple sine (range PI) and trapeze 456 // var scaleX = (sin(wx * PI / (2 * dx + 1)) + min(min(2.1 * wx / dx, 2.1 * (2 * dx + 1 - wx) / dx), 1)) / 2; 457 // var scaleY = (sin(wy * PI / (2 * dy + 1)) + min(min(2.1 * wy / dy, 2.1 * (2 * dy + 1 - wy) / dy), 1)) / 2; 458 // // Window function arithmetic mean of cosine (range 2*PI) and trapeze 459 // var scaleX = ((cos((wx - (2 * dx + 1) / 2) * 2 * PI / (2 * dx + 1)) + 1) / 2 + min(min(5 * wx / dx, 5 * (2 * dx + 1 - wx) / dx), 1)) / 2; 460 // var scaleY = ((cos((wy - (2 * dy + 1) / 2) * 2 * PI / (2 * dy + 1)) + 1) / 2 + min(min(5 * wy / dy, 5 * (2 * dy + 1 - wy) / dy), 1)) / 2; 461 // // Window function arithmetic mean of polynomial 2nd degree and trapeze (BEST COMBINATION SO FAR) 462 // var scaleX = (1 - (wx / dx - 1) * (wx / dx - 1) + min(min(3 * wx / dx, 3 * (2 * dx + 1 - wx) / dx), 1)) / 2; 463 // var scaleY = (1 - (wy / dy - 1) * (wy / dy - 1) + min(min(3 * wy / dy, 3 * (2 * dy + 1 - wy) / dy), 1)) / 2; 464 465 if (scaleX < 0 || scaleY < 0 || scaleX > 1 || scaleY > 1) // DEBUG 466 warn("rectangularSmoothToHeight: Scale < 0 or > 1: scaleX = " + scaleX + ", scaleY = " + scaleY); // DEBUG 467 heightmap[actualX][actualY] = heightmapWin[wx][wy] + strength * scaleX * scaleY * (targetHeight - heightmapWin[wx][wy]); 468 // g_Map.setTexture(actualX, actualY, "red"); // DEBUG 469 } 470 } 471 } 472 } -
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; … … 103 119 if (numArgs != 1) 104 120 { 105 121 var ret = new Array(numArgs); 106 for (var i=0; i < numArgs; i++)122 for (var i=0; i < numArgs; ++i) 107 123 { 108 124 ret[i] = x[i]; 109 125 } … … 125 141 return ar[randInt(ar.length)]; 126 142 } 127 143 128 // "Inside-out" implementation of Fisher-Yates shuffle 144 /** 145 * "Inside-out" implementation of Fisher-Yates shuffle 146 */ 129 147 function shuffleArray(source) 130 148 { 131 149 if (!source.length) 132 150 return []; 133 151 134 152 var result = [source[0]]; 135 for (var i = 1; i < source.length; i++)153 for (var i = 1; i < source.length; ++i) 136 154 { 137 155 var j = randInt(0, i); 138 156 result[i] = result[j]; … … 172 190 var area = g_Map.createArea(centeredPlacer, painter, constraint); 173 191 if (area !== undefined) 174 192 { 175 good++;193 ++good; 176 194 result.push(area); 177 195 } 178 196 else 179 197 { 180 bad++;198 ++bad; 181 199 } 182 200 } 183 201 return result; … … 208 226 var area = g_Map.createArea(centeredPlacer, painter, constraint); 209 227 if (area !== undefined) 210 228 { 211 good++;229 ++good; 212 230 result.push(area); 213 231 } 214 232 else 215 233 { 216 bad++;234 ++bad; 217 235 } 218 236 } 219 237 return result; … … 248 266 var result = createObjectGroup(placer, player, constraint); 249 267 if (result !== undefined) 250 268 { 251 good++;269 ++good; 252 270 } 253 271 else 254 272 { 255 bad++;273 ++bad; 256 274 } 257 275 } 258 276 return good; … … 282 300 var result = createObjectGroup(placer, player, constraint); 283 301 if (result !== undefined) 284 302 { 285 good++;303 ++good; 286 304 } 287 305 else 288 306 { 289 bad++;307 ++bad; 290 308 } 291 309 } 292 310 return good; … … 349 367 return (g_MapSettings.CircularMap ? true : false); 350 368 } 351 369 352 / ////////////////////////////////////////////////////////////////////////////////////////////353 //Access global map variable354 /////////////////////////////////////////////////////////////////////////////////////////////370 /** 371 * Access global map variable 372 */ 355 373 356 374 function createTileClass() 357 375 { … … 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)) … … 434 574 435 575 var result = new Array(0); 436 576 var team = new Array(5); 437 for (var q = 0; q < 5; q++)577 for (var q = 0; q < 5; ++q) 438 578 { 439 579 team[q] = new Array(1); 440 580 } 441 581 442 for (var i = -1; i < 4; i++)582 for (var i = -1; i < 4; ++i) 443 583 { 444 for (var j = 0; j < source.length; j++)584 for (var j = 0; j < source.length; ++j) 445 585 { 446 586 if (getPlayerTeam(j) == i) 447 {448 587 team[i+1].unshift(j+1); 449 }450 588 } 451 589 team[i+1].pop(); 452 590 result=result.concat(shuffleArray(team[i+1])) … … 461 599 462 600 var prime = new Array(source.length); 463 601 464 for (var i = 0; i < round(source.length/2); i++)602 for (var i = 0; i < round(source.length/2); ++i) 465 603 { 466 604 prime[2*i]=source[i]; 467 605 prime[2*i+1]=source[source.length-1-i]; … … 492 630 g_Map.setHeight(x, z, height); 493 631 } 494 632 495 /////////////////////////////////////////////////////////////////////////////////////////////496 // Utility functions for classes497 /////////////////////////////////////////////////////////////////////////////////////////////498 633 634 /** 635 * Utility functions for classes 636 */ 499 637 500 // Add point to given class by id 638 /** 639 * Add point to given class by id 640 */ 501 641 function addToClass(x, z, id) 502 642 { 503 643 var tileClass = getTileClass(id); 504 644 505 645 if (tileClass !== null) 506 {507 646 tileClass.add(x, z); 508 }509 647 } 510 648 511 // Remove point from the given class by id 649 /** 650 * Remove point from the given class by id 651 */ 512 652 function removeFromClass(x, z, id) 513 653 { 514 654 var tileClass = getTileClass(id); 515 655 516 656 if (tileClass !== null) 517 {518 657 tileClass.remove(x, z); 519 }520 658 } 521 659 522 // Create a painter for the given class 660 /** 661 * Create a painter for the given class 662 */ 523 663 function paintClass(id) 524 664 { 525 665 return new TileClassPainter(getTileClass(id)); 526 666 } 527 667 528 // Create a painter for the given class 668 /** 669 * Create a painter for the given class 670 */ 529 671 function unPaintClass(id) 530 672 { 531 673 return new TileClassUnPainter(getTileClass(id)); 532 674 } 533 675 534 // Create an avoid constraint for the given classes by the given distances 676 /** 677 * Create an avoid constraint for the given classes by the given distances 678 */ 535 679 function avoidClasses(/*class1, dist1, class2, dist2, etc*/) 536 680 { 537 681 var ar = new Array(arguments.length/2); 538 for (var i = 0; i < arguments.length/2; i++)682 for (var i = 0; i < arguments.length/2; ++i) 539 683 { 540 684 ar[i] = new AvoidTileClassConstraint(arguments[2*i], arguments[2*i+1]); 541 685 } 542 686 543 687 // Return single constraint 544 688 if (ar.length == 1) 545 {546 689 return ar[0]; 547 }548 690 else 549 691 { 550 692 return new AndConstraint(ar); 551 693 } 552 694 } 553 695 554 // Create a stay constraint for the given classes by the given distances 696 /** 697 * Create a stay constraint for the given classes by the given distances 698 */ 555 699 function stayClasses(/*class1, dist1, class2, dist2, etc*/) 556 700 { 557 701 var ar = new Array(arguments.length/2); 558 for (var i = 0; i < arguments.length/2; i++)702 for (var i = 0; i < arguments.length/2; ++i) 559 703 { 560 704 ar[i] = new StayInTileClassConstraint(arguments[2*i], arguments[2*i+1]); 561 705 } … … 571 715 } 572 716 } 573 717 574 // Create a border constraint for the given classes by the given distances 718 /** 719 * Create a border constraint for the given classes by the given distances 720 */ 575 721 function borderClasses(/*class1, idist1, odist1, class2, idist2, odist2, etc*/) 576 722 { 577 723 var ar = new Array(arguments.length/3); 578 for (var i = 0; i < arguments.length/3; i++)724 for (var i = 0; i < arguments.length/3; ++i) 579 725 { 580 726 ar[i] = new BorderTileClassConstraint(arguments[3*i], arguments[3*i+1], arguments[3*i+2]); 581 727 } … … 591 737 } 592 738 } 593 739 594 // Checks if the given tile is in class "id" 740 /** 741 * Checks if the given tile is in class "id" 742 */ 595 743 function checkIfInClass(x, z, id) 596 744 { 597 745 var tileClass = getTileClass(id); … … 612 760 } 613 761 } 614 762 615 616 // Returns the distance between 2 points 763 /** 764 * Returns the distance between 2 points 765 */ 617 766 function getDistance(x1, z1, x2, z2) 618 767 { 619 768 return Math.pow(Math.pow(x1 - x2, 2) + Math.pow(z1 - z2, 2), 1/2); 620 769 } 621 770 622 // Returns the angle of the vector between point 1 and point 2. The angle is anticlockwise from the positive x axis. 771 /** 772 * Returns the angle of the vector between point 1 and point 2. The angle is anticlockwise from the positive x axis. 773 */ 623 774 function getAngle(x1, z1, x2, z2) 624 775 { 625 776 return Math.atan2(z2 - z1, x2 - x1); 626 777 } 627 778 628 // Returns the gradient of the line between point 1 and 2 in the form dz/dx 779 /** 780 * Returns the gradient of the line between point 1 and 2 in the form dz/dx 781 */ 629 782 function getGradient(x1, z1, x2, z2) 630 783 { 631 784 if (x1 == x2 && z1 == z2) … … 642 795 { 643 796 return g_Map.getTexture(x, y); 644 797 } 645 -
binaries/data/mods/public/maps/random/rmgen/misc.js
1 ///////////////////////////////////////////////////////////////////////////////////////// 2 // passageMaker 3 // 4 // Function for creating shallow water between two given points by changing the heiight of all tiles in 5 // the path with height less than or equal to "maxheight" to "height" 6 // 7 // x1,z1: Starting point of path 8 // x2,z2: Ending point of path 9 // width: Width of the shallow 10 // maxheight: Maximum height that it changes 11 // height: Height of the shallow 12 // smooth: smooth elevation in borders 13 // tileclass: (Optianal) - Adds those tiles to the class given 14 // terrain: (Optional) - Changes the texture of the elevated land 15 // 16 ///////////////////////////////////////////////////////////////////////////////////////// 17 1 /** 2 * passageMaker 3 * 4 * Function for creating shallow water between two given points by changing the heiight of all tiles in 5 * the path with height less than or equal to "maxheight" to "height" 6 * 7 * x1,z1: Starting point of path 8 * x2,z2: Ending point of path 9 * width: Width of the shallow 10 * maxheight: Maximum height that it changes 11 * height: Height of the shallow 12 * smooth: smooth elevation in borders 13 * tileclass: (Optianal) - Adds those tiles to the class given 14 * terrain: (Optional) - Changes the texture of the elevated land 15 */ 18 16 function passageMaker(x1, z1, x2, z2, width, maxheight, height, smooth, tileclass, terrain, riverheight) 19 17 { 20 18 var tchm = TILE_CENTERED_HEIGHT_MAP; 21 19 TILE_CENTERED_HEIGHT_MAP = true; 22 20 var mapSize = g_Map.size; 23 for (var ix = 0; ix < mapSize; ix++)21 for (var ix = 0; ix < mapSize; ++ix) 24 22 { 25 for (var iz = 0; iz < mapSize; iz++)23 for (var iz = 0; iz < mapSize; ++iz) 26 24 { 27 25 var a = z1-z2; 28 26 var b = x2-x1; … … 73 71 TILE_CENTERED_HEIGHT_MAP = tchm; 74 72 } 75 73 76 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 77 //rndRiver is a fuction that creates random values useful for making a jagged river. 78 // 79 //it works the same as sin or cos function. the only difference is that it's period is 1 instead of 2*pi 80 //it needs the "seed" parameter to use it to make random curves that don't get broken. 81 //seed must be created using randFloat(). or else it won't work 82 // 83 // f: Input: Same as angle in a sine function 84 // seed: Random Seed: Best to implement is to use randFloat() 85 // 86 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 87 74 /** 75 *rndRiver is a fuction that creates random values useful for making a jagged river. 76 * 77 *it works the same as sin or cos function. the only difference is that it's period is 1 instead of 2*pi 78 *it needs the "seed" parameter to use it to make random curves that don't get broken. 79 *seed must be created using randFloat(). or else it won't work 80 * 81 * f: Input: Same as angle in a sine function 82 * seed: Random Seed: Best to implement is to use randFloat() 83 */ 88 84 function rndRiver(f, seed) 89 85 { 90 86 var rndRq = seed; … … 92 88 var rndRe = 0; 93 89 var rndRr = f-floor(f); 94 90 var rndRa = 0; 95 for (var rndRx=0; rndRx<=floor(f); rndRx++)91 for (var rndRx=0; rndRx<=floor(f); ++rndRx) 96 92 { 97 93 rndRw = 10*(rndRw-floor(rndRw)); 98 94 } … … 129 125 } 130 126 131 127 132 ///////////////////////////////////////////////////////////////////////////////////////// 133 // createStartingPlayerEntities 134 // 135 // Creates the starting player entities 136 // fx&fz: position of player base 137 // playerid: id of player 138 // civEntities: use getStartingEntities(id-1) fo this one 139 // BUILDING_ANGlE: angle of main base building 140 // 141 /////////////////////////////////////////////////////////////////////////////////////////// 128 /** 129 * createStartingPlayerEntities 130 * 131 * Creates the starting player entities 132 * fx&fz: position of player base 133 * playerid: id of player 134 * civEntities: use getStartingEntities(id-1) fo this one 135 * BUILDING_ANGlE: angle of main base building 136 */ 142 137 function createStartingPlayerEntities(fx, fz, playerid, civEntities, BUILDING_ANGlE) 143 138 { 144 139 var uDist = 6; … … 148 143 { 149 144 var uAngle = BUILDING_ANGlE - PI * (2-j) / 2; 150 145 var count = (civEntities[j].Count !== undefined ? civEntities[j].Count : 1); 151 for (var numberofentities = 0; numberofentities < count; numberofentities++)146 for (var numberofentities = 0; numberofentities < count; ++numberofentities) 152 147 { 153 148 var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); 154 149 var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); … … 157 152 } 158 153 } 159 154 160 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 161 // placeCivDefaultEntities 162 // 163 // Creates the default starting player entities depending on the players civ 164 // fx&fy: position of player base 165 // playerid: id of player 166 // angle: angle of main base building, optional, default is BUILDING_ANGlE 167 // kwargs: Takes some optional keyword arguments to tweek things 168 // 'iberWall': may be false, 'walls' (default) or 'towers'. Determines the defensive structures Iberians get as civ bonus 169 // 170 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 155 /** 156 * placeCivDefaultEntities 157 * 158 * Creates the default starting player entities depending on the players civ 159 * fx&fy: position of player base 160 * playerid: id of player 161 * angle: angle of main base building, optional, default is BUILDING_ANGlE 162 * kwargs: Optional. Takes an associative array with keyword arguments to tweak things: 163 * Known keys: 'iberWall': may be false, 'walls' (default) or 'towers'. Determines the defensive structures Iberians get as civ bonus 164 */ 171 165 function placeCivDefaultEntities(fx, fz, playerid, angle, kwargs) 172 166 { 173 167 // Unpack kwargs … … 187 181 { 188 182 var uAngle = angle - PI * (2-j) / 2; 189 183 var count = (civEntities[j].Count !== undefined ? civEntities[j].Count : 1); 190 for (var numberofentities = 0; numberofentities < count; numberofentities++)184 for (var numberofentities = 0; numberofentities < count; ++numberofentities) 191 185 { 192 186 var ux = fx + uDist * cos(uAngle) + numberofentities * uSpace * cos(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * cos(uAngle + PI/2)); 193 187 var uz = fz + uDist * sin(uAngle) + numberofentities * uSpace * sin(uAngle + PI/2) - (0.75 * uSpace * floor(count / 2) * sin(uAngle + PI/2)); … … 205 199 } 206 200 207 201 208 ///////////////////////////////////////////////////////////////////////////////////////// 209 // paintTerrainBasedOnHeight 210 // 211 // paints the tiles which have a height between minheight and maxheight with the given terrain 212 // minheight: minimum height of the tile 213 // maxheight: maximum height of the tile 214 // mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight. 215 // 1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than 216 // minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight 217 // terrain: intended terrain texture 218 // 219 /////////////////////////////////////////////////////////////////////////////////////////// 202 /** 203 * paintTerrainBasedOnHeight 204 * 205 * paints the tiles which have a height between minheight and maxheight with the given terrain 206 * minheight: minimum height of the tile 207 * maxheight: maximum height of the tile 208 * mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight. 209 * 1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than 210 * minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight 211 * terrain: intended terrain texture 212 */ 220 213 221 214 function paintTerrainBasedOnHeight(minheight, maxheight, mode, terrain) 222 215 { 223 216 var mSize = g_Map.size; 224 for (var qx = 0; qx < mSize; qx++)217 for (var qx = 0; qx < mSize; ++qx) 225 218 { 226 for (var qz = 0; qz < mSize; qz++)219 for (var qz = 0; qz < mSize; ++qz) 227 220 { 228 221 if (mode == 0) 229 222 { … … 257 250 } 258 251 } 259 252 260 ///////////////////////////////////////////////////////////////////////////////////////// 261 // paintTileClassBasedOnHeight and unPaintTileClassBasedOnHeight 262 // 263 // paints or unpaints the tiles which have a height between minheight and maxheight with the given tile class 264 // minheight: minimum height of the tile 265 // maxheight: maximum height of the tile 266 // mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight. 267 // 1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than 268 // minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight 269 // tileclass: intended tile class 270 // 271 /////////////////////////////////////////////////////////////////////////////////////////// 272 253 /** 254 * paintTileClassBasedOnHeight and unPaintTileClassBasedOnHeight 255 * 256 * paints or unpaints the tiles which have a height between minheight and maxheight with the given tile class 257 * minheight: minimum height of the tile 258 * maxheight: maximum height of the tile 259 * mode: accepts 4 values. 0 means the it will select tiles with height more than minheight and less than maxheight. 260 * 1 means it selects tiles with height more than or equal to minheight and less than max height. 2 means more than 261 * minheight and less than or equal to maxheight. 3 means more than or equal to minheight and less than or equal to maxheight 262 * tileclass: intended tile class 263 */ 273 264 function paintTileClassBasedOnHeight(minheight, maxheight, mode, tileclass) 274 265 { 275 266 var mSize = g_Map.size; 276 for (var qx = 0; qx < mSize; qx++)267 for (var qx = 0; qx < mSize; ++qx) 277 268 { 278 for (var qz = 0; qz < mSize; qz++)269 for (var qz = 0; qz < mSize; ++qz) 279 270 { 280 271 if (mode == 0) 281 272 { … … 313 304 function unPaintTileClassBasedOnHeight(minheight, maxheight, mode, tileclass) 314 305 { 315 306 var mSize = g_Map.size; 316 for (var qx = 0; qx < mSize; qx++)307 for (var qx = 0; qx < mSize; ++qx) 317 308 { 318 for (var qz = 0; qz < mSize; qz++)309 for (var qz = 0; qz < mSize; ++qz) 319 310 { 320 311 if (mode == 0) 321 312 { … … 349 340 } 350 341 } 351 342 352 ///////////////////////////////////////////////////////////////////////////////////////// 353 // getTIPIADBON 354 // 355 // "get The Intended Point In A Direction Based On Height" 356 // gets the N'th point with a specific height in a line and returns it as a [x, y] array 357 // startPoint: [x, y] array defining the start point 358 // endPoint: [x, y] array defining the ending point 359 // heightRange: [min, max] array defining the range which the height of the intended point can be. includes both "min" and "max" 360 // step: how much tile units per turn should the search go. more value means faster but less accurate 361 // n: how many points to skip before ending the search. skips """n-1 points""". 362 // 363 /////////////////////////////////////////////////////////////////////////////////////////// 364 343 /** 344 * getTIPIADBON 345 * 346 * "get The Intended Point In A Direction Based On Height" 347 * gets the N'th point with a specific height in a line and returns it as a [x, y] array 348 * startPoint: [x, y] array defining the start point 349 * endPoint: [x, y] array defining the ending point 350 * heightRange: [min, max] array defining the range which the height of the intended point can be. includes both "min" and "max" 351 * step: how much tile units per turn should the search go. more value means faster but less accurate 352 * n: how many points to skip before ending the search. skips """n-1 points""". 353 */ 365 354 function getTIPIADBON(startPoint, endPoint, heightRange, step, n) 366 355 { 367 356 var stepX = step*(endPoint[0]-startPoint[0])/(sqrt((endPoint[0]-startPoint[0])*(endPoint[0]-startPoint[0]) + step*(endPoint[1]-startPoint[1])*(endPoint[1]-startPoint[1]))); … … 394 383 return undefined; 395 384 } 396 385 397 ///////////////////////////////////////////////////////////////////////////////////////// 398 // doIntersect 399 // 400 // determines if two lines with the width "width" intersect or collide with each other 401 // x1, y1, x2, y2: determine the position of the first line 402 // x3, y3, x4, y4: determine the position of the second line 403 // width: determines the width of the lines 404 // 405 /////////////////////////////////////////////////////////////////////////////////////////// 406 386 /** 387 * doIntersect 388 * 389 * determines if two lines with the width "width" intersect or collide with each other 390 * x1, y1, x2, y2: determine the position of the first line 391 * x3, y3, x4, y4: determine the position of the second line 392 * width: determines the width of the lines 393 */ 407 394 function checkIfIntersect (x1, y1, x2, y2, x3, y3, x4, y4, width) 408 395 { 409 396 if (x1 == x2) … … 445 432 return false; 446 433 } 447 434 448 ///////////////////////////////////////////////////////////////////////////////////////// 449 // distanceOfPointFromLine 450 // 451 // returns the distance of a point from a line 452 // x1, y1, x2, y2: determine the position of the line 453 // x3, y3: determine the position of the point 454 // 455 /////////////////////////////////////////////////////////////////////////////////////////// 456 435 /** 436 * distanceOfPointFromLine 437 * 438 * returns the distance of a point from a line 439 * x1, y1, x2, y2: determine the position of the line 440 * x3, y3: determine the position of the point 441 */ 457 442 function distanceOfPointFromLine (x1, y1, x2, y2, x3, y3) 458 443 { 459 444 if (x1 == x2) … … 473 458 } 474 459 } 475 460 476 ///////////////////////////////////////////////////////////////////////////////////////// 477 // createRamp 478 // 479 // creates a ramp from point (x1, y1) to (x2, y2). 480 // x1, y1, x2, y2: determine the position of the start and end of the ramp 481 // minHeight, maxHeight: determine the height levels of the start and end point 482 // width: determines the width of the ramp 483 // smoothLevel: determines the smooth level around the edges of the ramp 484 // mainTerrain: (Optional) determines the terrain texture for the ramp 485 // edgeTerrain: (Optional) determines the terrain texture for the edges 486 // tileclass: (Optional) adds the ramp to this tile class 487 // 488 /////////////////////////////////////////////////////////////////////////////////////////// 489 461 /** 462 * createRamp 463 * 464 * creates a ramp from point (x1, y1) to (x2, y2). 465 * x1, y1, x2, y2: determine the position of the start and end of the ramp 466 * minHeight, maxHeight: determine the height levels of the start and end point 467 * width: determines the width of the ramp 468 * smoothLevel: determines the smooth level around the edges of the ramp 469 * mainTerrain: (Optional) determines the terrain texture for the ramp 470 * edgeTerrain: (Optional) determines the terrain texture for the edges 471 * tileclass: (Optional) adds the ramp to this tile class 472 */ 490 473 function createRamp (x1, y1, x2, y2, minHeight, maxHeight, width, smoothLevel, mainTerrain, edgeTerrain, tileclass) 491 474 { 492 475 var halfWidth = width / 2; … … 540 523 } 541 524 } 542 525 543 / ////////////////////////////////////////////////////////////////////////////////////////544 //createMountain545 // 546 //creates a mountain using a tecnique very similar to chain placer.547 // 548 ///////////////////////////////////////////////////////////////////////////////////////////526 /** 527 * createMountain 528 * 529 * creates a mountain using a tecnique very similar to chain placer. 530 * 531 */ 549 532 550 533 function createMountain(maxHeight, minRadius, maxRadius, numCircles, constraint, x, z, terrain, tileclass, fcc, q) 551 534 { … … 617 600 618 601 for (var ix = sx; ix <= lx; ++ix) 619 602 { 620 for (var iz = sz; iz <= lz; ++ 603 for (var iz = sz; iz <= lz; ++iz) 621 604 { 622 605 dx = ix - cx; 623 606 dz = iz - cz; … … 666 649 667 650 for (var ix = sx; ix <= lx; ++ix) 668 651 { 669 for (var iz = sz; iz <= lz; ++ 652 for (var iz = sz; iz <= lz; ++iz) 670 653 { 671 654 if (fcc) 672 655 if ((x - ix) > fcc || (ix - x) > fcc || (z - iz) > fcc || (iz - z) > fcc) … … 737 720 738 721 for (var ix = sx; ix <= lx; ++ix) 739 722 { 740 for (var iz = sz; iz <= lz; ++ 723 for (var iz = sz; iz <= lz; ++iz) 741 724 { 742 725 dx = ix - cx; 743 726 dz = iz - cz; -
binaries/data/mods/public/maps/random/schwarzwald.js
1 // Created by Niek ten Brinke (aka niektb) 2 // Based on FeXoR's Daimond Square Algorithm for heightmap generation and several official random maps 1 /** 2 * Created by Niek ten Brinke (aka niektb) 3 * Based on FeXoR's Daimond Square Algorithm for heightmap generation and several official random maps 4 */ 3 5 4 6 'use strict'; 5 7 6 8 RMS.LoadLibrary('rmgen'); 7 9 8 // initialize map 10 /** 11 * initialize map 12 */ 9 13 10 14 log('Initializing map...'); 11 15 12 16 InitMap(); 13 17 14 18 15 //////////////// 16 // 17 // Initializing 18 // 19 //////////////// 19 /** 20 * Initializing 21 */ 20 22 21 //sky 23 /** 24 *sky 25 */ 22 26 setSkySet("fog"); 23 27 setFogFactor(0.35); 24 28 setFogThickness(0.19); 25 26 // water 29 /** 30 * water 31 */ 27 32 setWaterColor(0.501961, 0.501961, 0.501961); 28 33 setWaterTint(0.25098, 0.501961, 0.501961); 29 34 setWaterWaviness(0.5); 30 35 setWaterType("clap"); 31 36 setWaterMurkiness(0.75); 32 33 // post processing 37 /** 38 * post processing 39 */ 34 40 setPPSaturation(0.37); 35 41 setPPContrast(0.4); 36 42 setPPBrightness(0.4); 37 43 setPPEffect("hdr"); 38 44 setPPBloom(0.4); 39 40 // Setup tile classes 45 /** 46 * Setup tile classes 47 */ 41 48 var clPlayer = createTileClass(); 42 49 var clPath = createTileClass(); 43 50 var clHill = createTileClass(); … … 47 54 var clFood = createTileClass(); 48 55 var clBaseResource = createTileClass(); 49 56 var clOpen = createTileClass(); 50 51 // Setup Templates 57 /** 58 * Setup Templates 59 */ 52 60 var templateStone = 'gaia/geology_stone_alpine_a'; 53 61 var templateStoneMine = 'gaia/geology_stonemine_alpine_quarry'; 54 62 var templateMetal = 'actor|geology/stone_granite_med.xml'; … … 64 72 var aReeds = 'actor|props/flora/reeds_pond_lush_b.xml'; 65 73 var oFish = "gaia/fauna_fish"; 66 74 67 68 // Setup terrain 75 /** 76 * Setup terrain 77 */ 69 78 var terrainWood = ['alpine_forrestfloor|gaia/flora_tree_oak', 'alpine_forrestfloor|gaia/flora_tree_pine']; 70 79 var terrainWoodBorder = ['new_alpine_grass_mossy|gaia/flora_tree_oak', 'alpine_forrestfloor|gaia/flora_tree_pine', 71 80 'temp_grass_long|gaia/flora_bush_temperate', 'temp_grass_clovers|gaia/flora_bush_berry', 'temp_grass_clovers_2|gaia/flora_bush_grapes', … … 108 117 109 118 const BUILDING_ANGlE = -PI/4; 110 119 111 // Setup map 120 /** 121 * Setup map 122 */ 112 123 var mapSize = getMapSize(); 113 124 var mapRadius = mapSize/2; 114 125 var playableMapRadius = mapRadius - 5; 115 126 var mapCenterX = mapRadius; 116 127 var mapCenterZ = mapRadius; 117 128 118 // Setup players and bases 129 /** 130 * Setup players and bases 131 */ 119 132 var numPlayers = getNumPlayers(); 120 133 var baseRadius = 15; 121 134 var minPlayerRadius = min(mapRadius-1.5*baseRadius, 5*mapRadius/8); … … 128 141 var playerAngleAddAvrg = 2*PI / numPlayers; 129 142 var playerAngleMaxOff = playerAngleAddAvrg/4; 130 143 131 // Setup paths 144 /** 145 * Setup paths 146 */ 132 147 var pathSucsessRadius = baseRadius/2; 133 148 var pathAngleOff = PI/2; 134 var pathWidth = 10; // This is not really the path's thickness in tiles but the number of tiles in the clum bs of the path149 var pathWidth = 10; // This is not really the path's thickness in tiles but the number of tiles in the clumps of the path 135 150 136 // Setup additional resources 151 /** 152 * Setup additional resources 153 */ 137 154 var resourceRadius = 2*mapRadius/3; // 3*mapRadius/8; 138 155 //var resourcePerPlayer = [templateStone, templateMetalMine]; 139 156 140 // Setup woods 141 // For large maps there are memory errors with too many trees. A density of 256*192/mapArea works with 0 players. 142 // Around each player there is an area without trees so with more players the max density can increase a bit. 157 /** 158 * Setup woods 159 * For large maps there are memory errors with too many trees. A density of 256*192/mapArea works with 0 players. 160 * Around each player there is an area without trees so with more players the max density can increase a bit. 161 */ 143 162 var maxTreeDensity = min(256 * (192 + 8 * numPlayers) / (mapSize * mapSize), 1); // Has to be tweeked but works ok 144 163 var bushChance = 1/3; // 1 means 50% chance in deepest wood, 0.5 means 25% chance in deepest wood 145 164 146 165 147 //////////////// 148 // 149 // Some general functions 150 // 151 //////////////// 152 166 /** 167 * Some general functions 168 */ 153 169 function HeightPlacer(lowerBound, upperBound) { 154 170 this.lowerBound = lowerBound; 155 171 this.upperBound = upperBound; … … 159 175 constraint = (constraint || new NullConstraint()); 160 176 161 177 var ret = []; 162 for (var x = 0; x < g_Map.size; x++) {163 for (var y = 0; y < g_Map.size; y++) {178 for (var x = 0; x < g_Map.size; ++x) { 179 for (var y = 0; y < g_Map.size; ++y) { 164 180 if (g_Map.height[x][y] >= this.lowerBound && g_Map.height[x][y] <= this.upperBound && constraint.allows(x, y)) { 165 181 ret.push(new PointXZ(x, y)); 166 182 } … … 169 185 return ret; 170 186 }; 171 187 172 /* 173 Takes an array of 2D points (arrays of length 2)174 Returns the order to go through the points for the shortest closed path (array of indices)175 */188 /** 189 * Takes an array of 2D points (arrays of length 2) 190 * Returns the order to go through the points for the shortest closed path (array of indices) 191 */ 176 192 function getOrderOfPointsForShortestClosePath(points) 177 193 { 178 194 var order = []; 179 195 var distances = []; 180 196 if (points.length <= 3) 181 197 { 182 for (var i = 0; i < points.length; i++)198 for (var i = 0; i < points.length; ++i) 183 199 order.push(i); 184 200 185 201 return order; … … 187 203 188 204 // Just add the first 3 points 189 205 var pointsToAdd = deepcopy(points); 190 for (var i = 0; i < min(points.length, 3); i ++)206 for (var i = 0; i < min(points.length, 3); i) 191 207 { 192 208 order.push(i); 193 209 pointsToAdd.shift(i); … … 198 214 199 215 // Add remaining points so the path lengthens the least 200 216 var numPointsToAdd = pointsToAdd.length; 201 for (var i = 0; i < numPointsToAdd; i++)217 for (var i = 0; i < numPointsToAdd; ++i) 202 218 { 203 219 var indexToAddTo = undefined; 204 220 var minEnlengthen = Infinity; 205 221 var minDist1 = 0; 206 222 var minDist2 = 0; 207 for (var k = 0; k < order.length; k++)223 for (var k = 0; k < order.length; ++k) 208 224 { 209 225 var dist1 = getDistance(pointsToAdd[0][0], pointsToAdd[0][1], points[order[k]][0], points[order[k]][1]); 210 226 var dist2 = getDistance(pointsToAdd[0][0], pointsToAdd[0][1], points[order[(k + 1) % order.length]][0], points[order[(k + 1) % order.length]][1]); … … 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]; … … 264 276 var oldHeightRange = getMinAndMaxHeight(heightmap); 265 277 var max_x = heightmap.length; 266 278 var max_y = heightmap[0].length; 267 for (var x = 0; x < max_x; x++)268 for (var y = 0; y < max_y; y++)279 for (var x = 0; x < max_x; ++x) 280 for (var y = 0; y < max_y; ++y) 269 281 heightmap[x][y] = minHeight + (heightmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight); 270 282 } 271 283 272 /* 273 getStartLocationsByHeightmap274 Takes275 hightRange An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)276 heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats277 maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared278 minDistToBorder Optional, default is 20, an integer, how far start locations have to be279 numberOfPlayers Optional, default is getNumPlayers, an integer, how many start locations should be placed280 Returns281 An array of 2D points (arrays of length 2)282 */284 /** 285 * getStartLocationsByHeightmap 286 * Takes 287 * hightRange An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed) 288 * heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats 289 * maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared 290 * minDistToBorder Optional, default is 20, an integer, how far start locations have to be 291 * numberOfPlayers Optional, default is getNumPlayers, an integer, how many start locations should be placed 292 * Returns 293 * An array of 2D points (arrays of length 2) 294 */ 283 295 function getStartLocationsByHeightmap(hightRange, maxTries, minDistToBorder, numberOfPlayers, heightmap) 284 296 { 285 297 maxTries = (maxTries || 1000); … … 288 300 heightmap = (heightmap || g_Map.height); 289 301 290 302 var validStartLocTiles = []; 291 for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; x++)292 for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; y++)303 for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; ++x) 304 for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; ++y) 293 305 if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight 294 306 validStartLocTiles.push([x, y]); 295 307 296 308 var maxMinDist = 0; 297 for (var tries = 0; tries < maxTries; tries++)309 for (var tries = 0; tries < maxTries; ++tries) 298 310 { 299 311 var startLoc = []; 300 312 var minDist = heightmap.length; 301 for (var p = 0; p < numberOfPlayers; p++)313 for (var p = 0; p < numberOfPlayers; ++p) 302 314 startLoc.push(validStartLocTiles[randInt(validStartLocTiles.length)]); 303 for (var p1 = 0; p1 < numberOfPlayers - 1; p1++)315 for (var p1 = 0; p1 < numberOfPlayers - 1; ++p1) 304 316 { 305 for (var p2 = p1 + 1; p2 < numberOfPlayers; p2++)317 for (var p2 = p1 + 1; p2 < numberOfPlayers; ++p2) 306 318 { 307 319 var dist = getDistance(startLoc[p1][0], startLoc[p1][1], startLoc[p2][0], startLoc[p2][1]); 308 320 if (dist < minDist) … … 319 331 return finalStartLoc; 320 332 } 321 333 322 /* 323 derivateEntitiesByHeight324 Takes325 hightRange An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)326 startLoc An array of 2D points (arrays of length 2)327 heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats328 entityList Array of entities/actors (strings to be placed with placeObject())329 maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared330 minDistance Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border331 Returns332 An array of 2D points (arrays of length 2)333 */334 /** 335 *derivateEntitiesByHeight 336 *Takes 337 * hightRange An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed) 338 * startLoc An array of 2D points (arrays of length 2) 339 * heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats 340 * entityList Array of entities/actors (strings to be placed with placeObject()) 341 * maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared 342 * minDistance Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border 343 *Returns 344 * An array of 2D points (arrays of length 2) 345 */ 334 346 function derivateEntitiesByHeight(hightRange, startLoc, entityList, maxTries, minDistance, heightmap) 335 347 { 336 348 entityList = (entityList || [templateMetalMine, templateStoneMine]); … … 340 352 341 353 var placements = deepcopy(startLoc); 342 354 var validTiles = []; 343 for (var x = minDistance; x < heightmap.length - minDistance; x++)344 for (var y = minDistance; y < heightmap[0].length - minDistance; y++)355 for (var x = minDistance; x < heightmap.length - minDistance; ++x) 356 for (var y = minDistance; y < heightmap[0].length - minDistance; ++y) 345 357 if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight 346 358 validTiles.push([x, y]); 347 359 348 360 if (!validTiles.length) 349 361 return; 350 362 351 for (var tries = 0; tries < maxTries; tries++)363 for (var tries = 0; tries < maxTries; ++tries) 352 364 { 353 365 var tile = validTiles[randInt(validTiles.length)]; 354 366 var isValid = true; 355 for (var p = 0; p < placements.length; p++)367 for (var p = 0; p < placements.length; ++p) 356 368 { 357 369 if (getDistance(placements[p][0], placements[p][1], tile[0], tile[1]) < minDistance) 358 370 { … … 370 382 } 371 383 372 384 373 //////////////// 374 // 375 // Base terrain generation functionality 376 // 377 //////////////// 385 /** 386 * Base terrain generation functionality 387 */ 378 388 379 389 function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap) 380 390 { … … 395 405 var newHeightmap = []; 396 406 var oldWidth = initialHeightmap.length; 397 407 // Square 398 for (var x = 0; x < 2 * oldWidth - 1; x++)408 for (var x = 0; x < 2 * oldWidth - 1; ++x) 399 409 { 400 410 newHeightmap.push([]); 401 for (var y = 0; y < 2 * oldWidth - 1; y++)411 for (var y = 0; y < 2 * oldWidth - 1; ++y) 402 412 { 403 413 if (x % 2 === 0 && y % 2 === 0) // Old tile 404 414 newHeightmap[x].push(initialHeightmap[x/2][y/2]); … … 412 422 } 413 423 } 414 424 // Diamond 415 for (var x = 0; x < 2 * oldWidth - 1; x++)425 for (var x = 0; x < 2 * oldWidth - 1; ++x) 416 426 { 417 for (var y = 0; y < 2 * oldWidth - 1; y++)427 for (var y = 0; y < 2 * oldWidth - 1; ++y) 418 428 { 419 429 if (newHeightmap[x][y] === undefined) 420 430 { … … 452 462 453 463 // Cut initialHeightmap to fit target width 454 464 var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)]; 455 for (var x = 0; x < heightmap.length; x++)456 for (var y = 0; y < heightmap[0].length; y++)465 for (var x = 0; x < heightmap.length; ++x) 466 for (var y = 0; y < heightmap[0].length; ++y) 457 467 heightmap[x][y] = newHeightmap[x][y]; 458 468 } 459 469 460 470 461 //////////////// 462 // 463 // Terrain erosion functionality 464 // 465 //////////////// 466 471 /** 472 * Terrain erosion functionality 473 */ 467 474 function decayErrodeHeightmap(strength, heightmap) 468 475 { 469 476 strength = (strength || 0.9); // 0 to 1 … … 474 481 var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother 475 482 var max_x = heightmap.length; 476 483 var max_y = heightmap[0].length; 477 for (var x = 0; x < max_x; x++)478 for (var y = 0; y < max_y; y++)479 for (var i = 0; i < map.length; i++)484 for (var x = 0; x < max_x; ++x) 485 for (var y = 0; y < max_y; ++y) 486 for (var i = 0; i < map.length; ++i) 480 487 heightmap[x][y] += strength / map.length * (referenceHeightmap[(x + map[i][0] + max_x) % max_x][(y + map[i][1] + max_y) % max_y] - referenceHeightmap[x][y]); // Not entirely sure if scaling with map.length is perfect but tested values seam to indicate it is 481 488 } 482 489 … … 490 497 heightmap = (heightmap || g_Map.height); 491 498 492 499 var heightmapWin = []; 493 for (var wx = 0; wx < 2 * dx + 1; wx++)500 for (var wx = 0; wx < 2 * dx + 1; ++wx) 494 501 { 495 502 heightmapWin.push([]); 496 for (var wy = 0; wy < 2 * dy + 1; wy++)503 for (var wy = 0; wy < 2 * dy + 1; ++wy) 497 504 { 498 505 var actualX = x - dx + wx; 499 506 var actualY = y - dy + wy; … … 503 510 heightmapWin[wx].push(targetHeight); 504 511 } 505 512 } 506 for (var wx = 0; wx < 2 * dx + 1; wx++)513 for (var wx = 0; wx < 2 * dx + 1; ++wx) 507 514 { 508 for (var wy = 0; wy < 2 * dy + 1; wy++)515 for (var wy = 0; wy < 2 * dy + 1; ++wy) 509 516 { 510 517 var actualX = x - dx + wx; 511 518 var actualY = y - dy + wy; … … 522 529 } 523 530 524 531 525 //////////////// 526 // 527 // Actually do stuff 528 // 529 //////////////// 532 /** 533 * Actually do stuff 534 */ 530 535 531 / ///////////////532 //Set height limits and water level by map size533 ////////////////536 /** 537 * Set height limits and water level by map size 538 */ 534 539 535 // Set target min and max height depending on map size to make average steepness about the same on all map sizes 540 /** 541 * Set target min and max height depending on map size to make average steepness about the same on all map sizes 542 */ 536 543 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}; 537 544 538 // Set average water coverage 545 /** 546 * Set average water coverage 547 */ 539 548 var averageWaterCoverage = 1/5; // NOTE: Since erosion is not predictable actual water coverage might vary much with the same values 540 549 var waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); 541 550 var waterHeightAdjusted = waterHeight + MIN_HEIGHT; 542 551 setWaterHeight(waterHeight); 543 552 544 / ///////////////545 //Generate base terrain546 ////////////////553 /** 554 * Generate base terrain 555 */ 547 556 548 // Setting a 3x3 Grid as initial heightmap 557 /** 558 * Setting a 3x3 Grid as initial heightmap 559 */ 549 560 var initialReliefmap = [[heightRange.max, heightRange.max, heightRange.max], [heightRange.max, heightRange.min, heightRange.max], [heightRange.max, heightRange.max, heightRange.max]]; 550 561 551 562 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, 0.5, initialReliefmap, g_Map.height); 552 // Apply simple erosion 553 for (var i = 0; i < 5; i++) 563 /** 564 * Apply simple erosion 565 */ 566 for (var i = 0; i < 5; ++i) 554 567 decayErrodeHeightmap(0.5); 555 568 rescaleHeightmap(heightRange.min, heightRange.max); 556 569 557 570 RMS.SetProgress(50); 558 571 559 / /////////560 //Setup height limit561 //////////572 /** 573 * Setup height limit 574 */ 562 575 563 // Height presets 576 /** 577 * Height presets 578 */ 564 579 var heighLimits = [ 565 580 heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water 566 581 heightRange.min + 2/3 * (waterHeightAdjusted - heightRange.min), // 1 Medium Water … … 574 589 waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border 575 590 waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop 576 591 577 / /////////578 //Place start locations and apply terrain texture and decorative props579 //////////592 /** 593 * Place start locations and apply terrain texture and decorative props 594 */ 580 595 581 // Get start locations 596 /** 597 * Get start locations 598 */ 582 599 var startLocations = getStartLocationsByHeightmap({'min': heighLimits[4], 'max': heighLimits[5]}); 583 600 var playerHeight = (heighLimits[4] + heighLimits[5]) / 2; 584 601 585 for (var i=0; i < numPlayers; i++)602 for (var i=0; i < numPlayers; ++i) 586 603 { 587 604 playerAngle[i] = (playerAngleStart + i*playerAngleAddAvrg + randFloat(0, playerAngleMaxOff))%(2*PI); 588 605 var x = round(mapCenterX + randFloat(minPlayerRadius, maxPlayerRadius)*cos(playerAngle[i])); … … 604 621 var distToSL = 15; 605 622 var resStartAngle = playerAngle[i] + PI; 606 623 var resAddAngle = 2*PI / startingResources.length; 607 for (var rIndex = 0; rIndex < startingResources.length; rIndex++)624 for (var rIndex = 0; rIndex < startingResources.length; ++rIndex) 608 625 { 609 626 var angleOff = randFloat(-resAddAngle/2, resAddAngle/2); 610 627 var placeX = x + distToSL*cos(resStartAngle + rIndex*resAddAngle + angleOff); … … 614 631 } 615 632 } 616 633 617 // Add further stone and metal mines 634 /** 635 * Add further stone and metal mines 636 */ 618 637 derivateEntitiesByHeight({'min': heighLimits[3], 'max': ((heighLimits[4]+heighLimits[3])/2)}, startLocations); 619 638 derivateEntitiesByHeight({'min': ((heighLimits[5]+heighLimits[6])/2), 'max': heighLimits[7]}, startLocations); 620 639 621 640 RMS.SetProgress(50); 622 641 623 //place water & open terrain textures and assign TileClasses 642 /** 643 * place water & open terrain textures and assign TileClasses 644 */ 624 645 log("Painting textures..."); 625 646 var placer = new HeightPlacer(heighLimits[2], (heighLimits[3]+heighLimits[2])/2); 626 647 var painter = new LayeredPainter([terrainBase, terrainBaseBorder], [5]); … … 634 655 635 656 RMS.SetProgress(60); 636 657 637 // Place paths 658 /** 659 * Place paths 660 */ 638 661 log("Placing paths..."); 639 662 var doublePaths = true; 640 663 if (numPlayers > 4) … … 644 667 var maxI = numPlayers+1; 645 668 else 646 669 var maxI = numPlayers; 647 for (var i = 0; i < maxI; i++)670 for (var i = 0; i < maxI; ++i) 648 671 { 649 672 if (doublePaths === true) 650 673 var minJ = 0; 651 674 else 652 675 var minJ = i+1; 653 for (var j = minJ; j < numPlayers+1; j++)676 for (var j = minJ; j < numPlayers+1; ++j) 654 677 { 655 678 // Setup start and target coordinates 656 679 if (i < numPlayers) … … 700 723 } 701 724 if (getDistance(x, z, targetX, targetZ) < pathSucsessRadius) 702 725 targetReached = true; 703 tries++;726 ++tries; 704 727 705 728 } 706 729 } … … 708 731 709 732 RMS.SetProgress(75); 710 733 711 //create general decoration 734 /** 735 *create general decoration 736 */ 712 737 log("Creating decoration..."); 713 738 createDecoration 714 739 ( … … 730 755 731 756 RMS.SetProgress(80); 732 757 733 //create fish 758 /** 759 *create fish 760 */ 734 761 log("Growing fish..."); 735 762 createFood 736 763 ( … … 745 772 746 773 RMS.SetProgress(85); 747 774 748 // create reeds 775 /** 776 * create reeds 777 */ 749 778 log("Planting reeds..."); 750 779 var types = [aReeds]; // some variation 751 780 for (var i = 0; i < types.length; ++i) … … 759 788 760 789 RMS.SetProgress(90); 761 790 762 // place trees 791 /** 792 * place trees 793 */ 763 794 log("Planting trees..."); 764 for (var x = 0; x < mapSize; x++)795 for (var x = 0; x < mapSize; ++x) 765 796 { 766 for (var z = 0; z < mapSize;z++)797 for (var z = 0; z < mapSize; ++z) 767 798 { 768 799 // Some variables 769 800 var radius = Math.pow(Math.pow(mapCenterX - x - 0.5, 2) + Math.pow(mapCenterZ - z - 0.5, 2), 1/2); // The 0.5 is a correction for the entities placed on the center of tiles 770 801 var minDistToSL = mapSize; 771 for (var i=0; i < numPlayers; i++)802 for (var i=0; i < numPlayers; ++i) 772 803 minDistToSL = min(minDistToSL, getDistance(playerStartLocX[i], playerStartLocZ[i], x, z)); 773 804 // Woods tile based 774 805 var tDensFactSL = max(min((minDistToSL - baseRadius) / baseRadius, 1), 0); … … 793 824 794 825 RMS.SetProgress(100); 795 826 796 // Export map data 827 /** 828 * Export map data 829 */ 797 830 ExportMap();