Ticket #4275: optimize_caledonian_meadows2016_10_9.patch
File optimize_caledonian_meadows2016_10_9.patch, 14.8 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/maps/random/caledonian_meadows.js
142 142 143 143 /** 144 144 * Drags a path to a target height smoothing it at the edges and return some points along the path. 145 *146 * TODO:147 * Would be nice to tell the function what to do and how often in the arguments148 * Adding painted tiles to a tile class149 145 */ 150 function placeRandomPathToHeight(start, pathTexture, target, targetHeight, width = 10, occurrence = 2, strength = 0.1, heightmap = g_Map.height)146 function placeRandomPathToHeight(start, target, targetHeight, tileClass = undefined, texture = "road_rome_a", width = 10, distance = 4, strength = 0.08, heightmap = g_Map.height) 151 147 { 152 if (pathTexture === true) 153 pathTexture = ['temp_road', "temp_road_overgrown", 'temp_grass_b']; 154 155 let clTempPath = createTileClass(); 148 let pathPoints = []; 156 149 let targetReached = false; 157 150 let position = deepcopy(start); 158 151 while (!targetReached) 159 152 { 160 153 rectangularSmoothToHeight(position, width, width, targetHeight, strength, heightmap); 161 if (pathTexture) 162 createArea(new ClumpPlacer(0.2 * width * width, 1, 1, 1, floor(position.x), floor(position.y)), [new TerrainPainter(pathTexture), paintClass(clTempPath)]); 163 164 // Set lets for next loop 165 let angleToTarget = getAngle(position.x, position.y, target.x, target.y); 166 let angleOff = PI * (randFloat() - 0.5); 167 position.x += occurrence * cos(angleToTarget + angleOff); 168 position.y += occurrence * sin(angleToTarget + angleOff); 169 if (getDistance(position.x, position.y, target.x, target.y) < occurrence / 2) 154 if (texture) 155 createArea(new ClumpPlacer(0.3 * width * width, 1, 1, 1, floor(position.x), floor(position.y)), [new TerrainPainter(texture), paintClass(tileClass)]); 156 pathPoints.push({"x" : position.x, "y" : position.y, "dist" : distance}); 157 // Setup for next loop 158 if (getDistance(position.x, position.y, target.x, target.y) < distance / 2) 170 159 targetReached = true; 160 else 161 { 162 let angleToTarget = getAngle(position.x, position.y, target.x, target.y); 163 let angleOff = PI * (randFloat() - 0.5); 164 position.x += distance * cos(angleToTarget + angleOff); 165 position.y += distance * sin(angleToTarget + angleOff); 166 } 171 167 } 172 return clTempPath;168 return pathPoints; 173 169 } 174 170 175 171 function getGrad(wrapped = true, scalarField = g_Map.height) … … 240 236 /** 241 237 * Meant to place e.g. resource spots within a height range 242 238 * @param {array} [heightRange] - The height range in which to place the entities (An associative array with keys "min" and "max" each containing a float) 243 * @param {array} [avoidPoints ] - An array of objects of the form {"x": int, "y": int, "dist": int}, points that will be avoided in the given dist e.g. start locations244 * @param { array} [avoidArea] - List of tiles to avoid239 * @param {array} [avoidPoints=[]] - An array of objects of the form {"x": int, "y": int, "dist": int}, points that will be avoided in the given dist e.g. start locations 240 * @param {object} [avoidClass=undefined] - TileClass to be avoided 245 241 * @param {integer} [minDistance=30] - How many tile widths the entities to place have to be away from each other, start locations and the map border 246 242 * @param {array} [heightmap=g_Map.height] - The reliefmap the entities should be distributed on 247 * @param {integer} [maxTries= 1000] - How often random player distributions are rolled to be compared243 * @param {integer} [maxTries=2 * g_Map.size] - How often random player distributions are rolled to be compared (256 to 1024) 248 244 * @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular 249 245 */ 250 function getPointsByHeight(heightRange, avoidPoints , avoidArea, minDistance = 20, maxTries = 1000, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap)246 function getPointsByHeight(heightRange, avoidPoints = [], avoidClass = undefined, minDistance = 20, maxTries = 2 * g_Map.size, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap) 251 247 { 252 248 let points = []; 253 249 let placements = deepcopy(avoidPoints); 254 250 let validVertices = []; 255 251 let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius 252 256 253 for (let x = minDistance; x < heightmap.length - minDistance; ++x) 257 254 { 258 255 for (let y = minDistance; y < heightmap[0].length - minDistance; ++y) 259 256 { 260 let isValid = true;261 for (let i = 0; i < pathArea.length; ++i)262 if (pathArea[i].x == x && pathArea[i].y == y)263 isValid = false;264 if (!isValid)257 if (avoidClass !== undefined && // Avoid adjecting tiles in avoidClass 258 (g_Map.tileClasses[avoidClass].inclusionCount[max(x - 1, 0)][y] > 0 || g_Map.tileClasses[avoidClass].inclusionCount[x][max(y - 1, 0)] > 0 || 259 g_Map.tileClasses[avoidClass].inclusionCount[min(x + 1, g_Map.tileClasses[avoidClass].inclusionCount.length - 1)][y] > 0 || 260 g_Map.tileClasses[avoidClass].inclusionCount[x][min(y + 1, g_Map.tileClasses[avoidClass].inclusionCount[0].length - 1)] > 0) 261 ) 265 262 continue; 266 267 if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && (!isCircular || r - getDistance(x, y, r, r) >= minDistance)) // Has correct height and enough distance to map border 263 264 if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && // Has correct height 265 (!isCircular || r - getDistance(x, y, r, r) >= minDistance) // Enough distance to map border 266 ) 268 267 validVertices.push({ "x": x, "y": y , "dist": minDistance}); 269 268 } 270 269 } 271 270 272 271 for (let tries = 0; tries < maxTries; ++tries) 273 272 { 274 273 let point = validVertices[randInt(validVertices.length)]; … … 277 276 points.push(point); 278 277 placements.push(point); 279 278 } 279 if ((tries != 0) && (tries % 100 == 0)) // Time Check 280 log(points.length + " points found after " + tries + " tries after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); 280 281 } 281 282 282 283 return points; 283 284 } 284 285 … … 294 295 "actor|props/flora/bush.xml", "actor|props/flora/bush_dry_a.xml", "actor|props/flora/bush_highlands.xml", 295 296 "actor|props/flora/bush_tempe_a.xml", "actor|props/flora/bush_tempe_b.xml", "actor|props/flora/ferns.xml" 296 297 ]; 297 298 298 function placeMine(point, centerEntity) 299 299 { 300 300 placeObject(point.x, point.y, centerEntity, 0, randFloat(0, TWO_PI)); … … 334 334 fences.push(new Fortress("fence", deepcopy(fences[i].wall).reverse())); 335 335 336 336 // Groves, only Wood 337 let groveEntities = [ 338 "gaia/flora_bush_temperate", "gaia/flora_tree_euro_beech" 339 ]; 337 let groveEntities = ["gaia/flora_bush_temperate", "gaia/flora_tree_euro_beech"]; 340 338 let groveActors = [ 341 339 "actor|geology/highland1_moss.xml", "actor|geology/highland2_moss.xml", 342 340 "actor|props/flora/bush.xml", "actor|props/flora/bush_dry_a.xml", "actor|props/flora/bush_highlands.xml", … … 383 381 } 384 382 } 385 383 386 let foodEntities = ["gaia/flora_bush_berry", "gaia/fauna_chicken", "gaia/fauna_chicken"]; 387 // Start loaction resources 388 function placeStartLocationResources(point) 384 385 function placeStartLocationResources(point, foodEntities = ["gaia/flora_bush_berry", "gaia/fauna_chicken", "gaia/fauna_chicken"]) 389 386 { 390 387 let currentAngle = randFloat(0, TWO_PI); 391 388 // Stone and chicken … … 438 435 } 439 436 } 440 437 438 log("Functions loaded after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check 439 441 440 /** 442 * Set height limits and water level by map size441 * Environment settings 443 442 */ 443 setBiome(g_BiomeAlpine); 444 g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0.01 }; 445 g_Environment.Water.WaterBody.Colour = { "r" : 0.3, "g" : 0.05, "b" : 0.1, "a" : 0.1 }; 446 g_Environment.Water.WaterBody.Murkiness = 0.4; 447 448 /** 449 * Base terrain shape generation and settings 450 */ 451 // Height range by map size 444 452 let heightScale = (g_Map.size + 256) / 768 / 4; 445 453 let heightRange = { "min": MIN_HEIGHT * heightScale, "max": MAX_HEIGHT * heightScale }; 446 454 // Water coverage 447 455 let averageWaterCoverage = 1/5; // NOTE: Since terrain generation is quite unpredictable actual water coverage might vary much with the same value 448 456 let waterHeight = -MIN_HEIGHT + heightRange.min + averageWaterCoverage * (heightRange.max - heightRange.min); // Water height in environment and the engine 449 457 let waterHeightAdjusted = waterHeight + MIN_HEIGHT; // Water height in RMGEN 450 458 setWaterHeight(waterHeight); 451 452 453 /** 454 * Generate base terrain 455 */ 459 // Generate base terrain shape 456 460 let medH = (heightRange.min + heightRange.max) / 2; 457 461 let initialHeightmap = [[medH, medH], [medH, medH]]; 458 462 setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialHeightmap, 0.8); 459 460 /** 461 * Apply simple erosion 462 */ 463 // globalSmoothHeightmap(0.5); 463 // Apply simple erosion 464 464 for (let i = 0; i < 5; ++i) 465 465 splashErodeMap(0.1); 466 466 // Final rescale 467 467 rescaleHeightmap(heightRange.min, heightRange.max); 468 468 469 RMS.SetProgress(25); 470 469 471 /** 470 * Height presets472 * Prepare terrain texture placement 471 473 */ 472 474 let heighLimits = [ 473 475 heightRange.min + 1/3 * (waterHeightAdjusted - heightRange.min), // 0 Deep water … … 481 483 waterHeightAdjusted + 6/8 * (heightRange.max - waterHeightAdjusted), // 8 Forest 482 484 waterHeightAdjusted + 7/8 * (heightRange.max - waterHeightAdjusted), // 9 Upper forest border 483 485 waterHeightAdjusted + (heightRange.max - waterHeightAdjusted)]; // 10 Hilltop 484 485 /** 486 * Set environment 487 */ 488 setBiome(g_BiomeAlpine); 489 g_Environment.Fog.FogColor = { "r": 0.8, "g": 0.8, "b": 0.8, "a": 0.01 }; 490 g_Environment.Water.WaterBody.Colour = { "r" : 0.3, "g" : 0.05, "b" : 0.1, "a" : 0.1 }; 491 g_Environment.Water.WaterBody.Murkiness = 0.4; 492 493 /** 494 * Add tile painting presets 495 */ 496 let dummyActor = "actor|props/special/common/waypoint_flag.xml"; 486 let playerHeight = (heighLimits[4] + heighLimits[5]) / 2; // Average player height 487 // Texture and actor presets 497 488 let myBiome = []; 498 489 myBiome.push({ // 0 Deep water 499 490 "texture": ["shoreline_stoney_a"], … … 551 542 "textureHS": ["alpine_cliff_c"], "actorHS": [["actor|geology/highland1.xml"], 0.0] 552 543 }); 553 544 545 log("Terrain shape generation and texture presets after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check 554 546 555 547 /** 556 548 * Get start locations … … 573 565 teams.push(t); 574 566 } 575 567 playerIDs = sortPlayers(playerIDs); 576 577 568 // Minimize maximum distance between players within a team 578 569 if (teams.length) 579 570 { … … 616 607 } 617 608 } 618 609 619 let playerHeight = (heighLimits[4] + heighLimits[5]) / 2; 610 log("Start location chosen after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check 611 RMS.SetProgress(30); 620 612 621 613 /** 622 * Place start locations (resources later)614 * Add paths 623 615 */ 624 for (let p = 0; p < playerIDs.length; ++p) 625 { 626 let point = startLocations[p]; 627 rectangularSmoothToHeight(point, 35, 35, playerHeight, 0.7); 628 placeCivDefaultEntities(point.x, point.y, playerIDs[p], { "iberWall": true }); 629 } 630 631 /** 632 * Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false (default) for this map to work properly) 633 */ 634 let tchm = getTileCenteredHeightmap(); 635 636 /** 637 * Add paths class and area but don't paint before resource spots are chosen! 638 */ 639 let pathTerrainClassIDs = []; 616 let tchm = getTileCenteredHeightmap(); // Calculate tileCenteredHeightMap (This has nothing to to with TILE_CENTERED_HEIGHT_MAP which should be false) 617 let pathPoints = []; 618 let clPath = createTileClass(); 640 619 for (let i = 0; i < startLocations.length; ++i) 641 620 { 642 621 let start = startLocations[i]; 643 622 let target = startLocations[(i + 1) % startLocations.length]; 644 pathTerrainClassIDs.push(placeRandomPathToHeight(start, ["road_rome_a"], target, playerHeight, 8, 3, 0.1)); 623 let points = placeRandomPathToHeight(start, target, playerHeight, clPath); 624 for (let pi = 0; pi < points.length; ++pi) 625 pathPoints.push(points[pi]); 645 626 } 646 let pathArea = [];647 for (let x = 0; x < tchm.length; ++x)648 for (let y = 0; y < tchm[0].length; ++y)649 for (let i = 0; i < pathTerrainClassIDs.length; ++i)650 if (getTileClass(pathTerrainClassIDs[i]).countMembersInRadius(x, y, 0.5))651 pathArea.push({ "x": x, "y": y });652 627 628 log("Paths placed after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check 629 RMS.SetProgress(45); 630 653 631 /** 654 * Get resource spots after players start locations after path are calculated but before they are placed!632 * Get resource spots after players start locations calculation 655 633 */ 656 634 let avoidPoints = deepcopy(startLocations); 657 635 for (let i = 0; i < avoidPoints.length; ++i) 658 636 avoidPoints[i].dist = 30; 659 let resourceSpots = getPointsByHeight({ "min": (heighLimits[3] + heighLimits[4]) / 2, "max": (heighLimits[5] + heighLimits[6]) / 2 }, avoidPoints, pathArea);637 let resourceSpots = getPointsByHeight({ "min": (heighLimits[3] + heighLimits[4]) / 2, "max": (heighLimits[5] + heighLimits[6]) / 2 }, avoidPoints, clPath); 660 638 661 /** 662 * Calculate slope map 663 */ 664 let slopeMap = getSlopeMap(); 639 log("Resource spots chosen after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check 640 RMS.SetProgress(55); 665 641 666 642 /** 667 643 * Divide tiles in areas by height and avoid paths … … 673 649 { 674 650 for (let y = 0; y < tchm[0].length; ++y) 675 651 { 676 let isPath = false; 677 for (let i = 0; i < pathArea.length; ++i) 678 if (pathArea[i].x == x && pathArea[i].y == y) 679 isPath = true; 680 if (isPath) 652 if (g_Map.tileClasses[clPath].inclusionCount[x][y] > 0) // Avoid paths 681 653 continue; 682 654 683 655 let minHeight = heightRange.min; … … 697 669 /** 698 670 * Get max slope of each area 699 671 */ 672 let slopeMap = getSlopeMap(); // Calculate slope map 700 673 let minSlope = []; 701 674 let maxSlope = []; 702 675 for (let h = 0; h < heighLimits.length; ++h) … … 744 717 } 745 718 } 746 719 720 log("Terrain texture placement finished after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); // Time Check 721 RMS.SetProgress(80); 722 747 723 /** 748 * Add start ing resources after terrain texturepainting724 * Add start locations and resource spots after terrain texture and path painting 749 725 */ 750 for (let p = 0; p < g_MapSettings.PlayerData.length - 1; ++p) 726 for (let p = 0; p < playerIDs.length; ++p) 727 { 728 let point = startLocations[p]; 729 rectangularSmoothToHeight(point, 40, 40, playerHeight, 0.7); 730 placeCivDefaultEntities(point.x, point.y, playerIDs[p], { "iberWall": true }); 751 731 placeStartLocationResources(startLocations[p]); 752 753 /** 754 * Add resource spots after terrain texture painting 755 */ 732 } 756 733 for (let i = 0; i < resourceSpots.length; ++i) 757 734 { 758 735 let choice = i % 5; … … 771 748 /** 772 749 * Stop Timer 773 750 */ 774 log("Map generation finished after " + ((new Date().getTime() - genStartTime) / 1000) + "s") 751 log("Map generation finished after " + ((new Date().getTime() - genStartTime) / 1000) + "s"); 775 752 776 753 /** 777 754 * Export map data