diff --git a/binaries/data/mods/public/maps/random/caledonian_meadows.js b/binaries/data/mods/public/maps/random/caledonian_meadows.js index d306ff72a0..2803aeccbe 100644 --- a/binaries/data/mods/public/maps/random/caledonian_meadows.js +++ b/binaries/data/mods/public/maps/random/caledonian_meadows.js @@ -9,30 +9,6 @@ var oGroveEntities = ["structures/gaul_outpost", "gaia/flora_tree_oak_new"]; var g_Map = new RandomMap(0, "whiteness"); -/** - * Drags a path to a target height smoothing it at the edges and return some points along the path. - */ -function placeRandomPathToHeight(start, target, targetHeight, tileClass, texture, width, distance, strength, heightmap) -{ - let painters = [new TerrainPainter(texture)]; - - if (tileClass) - painters.push(new TileClassPainter(tileClass)); - - let position = start.clone(); - while (position.distanceTo(target) >= distance / 2) - { - rectangularSmoothToHeight(position, width * 3, width * 3, targetHeight, strength, heightmap); - - createArea( - new ClumpPlacer(diskArea(width), 1, 1, Infinity, position), - painters); - - position.add(new Vector2D(distance, 0).rotate( - -getAngle(position.x, position.y, target.x, target.y) - randFloat(-1, 1) * Math.PI / 2)); - } -} - /** * Design resource spots */ @@ -318,15 +294,29 @@ myBiome.push({ // 10 Hilltop let [playerIDs, playerPosition] = sortPlayersByLocation(getStartLocationsByHeightmap({ "min": heighLimits[4], "max": heighLimits[5] }, 1000, 30)); Engine.SetProgress(30); -g_Map.log("Smooth player locations"); -for (let p = 0; p < playerIDs.length; ++p) - rectangularSmoothToHeight(playerPosition[p], 35, 35, playerHeight, 0.7); +g_Map.log("Smoothing player locations"); +for (let position of playerPosition) + createArea( + new ClumpPlacer(diskArea(35), 1, 1, Infinity, position), + new SmoothElevationPainter(ELEVATION_SET, g_Map.getHeight(position), 35)); -g_Map.log("Creating paths"); -let tchm = getTileCenteredHeightmap(); +g_Map.log("Creating paths between players"); let clPath = g_Map.createTileClass(); for (let i = 0; i < playerPosition.length; ++i) - placeRandomPathToHeight(playerPosition[i], playerPosition[(i + 1) % playerPosition.length], playerHeight, clPath, tPath, 4, 4, 0.08, g_Map.height); + createArea( + new RandomPathPlacer(playerPosition[i], playerPosition[(i + 1) % playerPosition.length], 4, 2, false), + [ + new TerrainPainter(tPath), + new ElevationBlendingPainter(playerHeight, 0.4), + new TileClassPainter(clPath) + ]); + +g_Map.log("Smoothing paths"); +createArea( + new MapBoundsPlacer(), + new SmoothingPainter(5, 1, 1), + new NearTileClassConstraint(clPath, 5)); + Engine.SetProgress(45); g_Map.log("Determining resource locations"); @@ -339,6 +329,7 @@ Engine.SetProgress(55); /** * Divide tiles in areas by height and avoid paths */ +let tchm = getTileCenteredHeightmap(); let areas = heighLimits.map(heightLimit => []); for (let x = 0; x < tchm.length; ++x) for (let y = 0; y < tchm[0].length; ++y) diff --git a/binaries/data/mods/public/maps/random/heightmap/heightmap.js b/binaries/data/mods/public/maps/random/heightmap/heightmap.js index 5d17e50583..f51cba12a9 100644 --- a/binaries/data/mods/public/maps/random/heightmap/heightmap.js +++ b/binaries/data/mods/public/maps/random/heightmap/heightmap.js @@ -120,6 +120,8 @@ function getStartLocationsByHeightmap(heightRange, maxTries = 1000, minDistToBor */ function setBaseTerrainDiamondSquare(minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT, initialHeightmap = undefined, smoothness = 0.5, heightmap = g_Map.height) { + g_Map.log("Generating map using the diamond-square algorithm"); + initialHeightmap = (initialHeightmap || [[randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)], [randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)]]); let heightRange = maxHeight - minHeight; if (heightRange <= 0) @@ -196,56 +198,6 @@ function setBaseTerrainDiamondSquare(minHeight = MIN_HEIGHT, maxHeight = MAX_HEI heightmap[x][y] = newHeightmap[x + shift[0]][y + shift[1]]; } -/** - * Pushes a rectangular area towards a given height smoothing it into the original terrain - * @note The window function to determine the smooth is not exactly a gaussian to ensure smooth edges - * @param {Vector2D} center - The x and y coordinates of the center point (rounded in this function) - * @param {float} [dx] - Distance from the center in x direction the rectangle ends (half width, rounded in this function) - * @param {float} [dy] - Distance from the center in y direction the rectangle ends (half depth, rounded in this function) - * @param {float} [targetHeight] - Height the center of the rectangle will be pushed to - * @param {float} [strength=1] - How strong the height is pushed: 0 means not at all, 1 means the center will be pushed to the target height - * @param {array} [heightmap=g_Map.height] - The heightmap to be manipulated - * @todo Make the window function an argument and maybe add some - */ -function rectangularSmoothToHeight(center, dx, dy, targetHeight, strength = 0.8, heightmap = g_Map.height) -{ - let x = Math.round(center.x); - let y = Math.round(center.y); - dx = Math.round(dx); - dy = Math.round(dy); - - let heightmapWin = []; - for (let wx = 0; wx < 2 * dx + 1; ++wx) - { - heightmapWin.push([]); - for (let wy = 0; wy < 2 * dy + 1; ++wy) - { - let actualX = x - dx + wx; - let actualY = y - dy + wy; - if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map - heightmapWin[wx].push(heightmap[actualX][actualY]); - else - heightmapWin[wx].push(targetHeight); - } - } - for (let wx = 0; wx < 2 * dx + 1; ++wx) - { - for (let wy = 0; wy < 2 * dy + 1; ++wy) - { - let actualX = x - dx + wx; - let actualY = y - dy + wy; - if (actualX >= 0 && actualX < heightmap.length - 1 && actualY >= 0 && actualY < heightmap[0].length - 1) // Is in map - { - // Window function polynomial 2nd degree - let scaleX = 1 - (wx / dx - 1) * (wx / dx - 1); - let scaleY = 1 - (wy / dy - 1) * (wy / dy - 1); - - heightmap[actualX][actualY] = heightmapWin[wx][wy] + strength * scaleX * scaleY * (targetHeight - heightmapWin[wx][wy]); - } - } - } -} - /** * Meant to place e.g. resource spots within a height range * @param {array} [heightRange] - The height range in which to place the entities (An associative array with keys "min" and "max" each containing a float) diff --git a/binaries/data/mods/public/maps/random/rmgen/painter.js b/binaries/data/mods/public/maps/random/rmgen/painter.js index 50e14b3d09..7f0407c57d 100644 --- a/binaries/data/mods/public/maps/random/rmgen/painter.js +++ b/binaries/data/mods/public/maps/random/rmgen/painter.js @@ -211,6 +211,21 @@ RandomElevationPainter.prototype.paint = function(area) } }; +/** + * The ElevationBlendingPainter sets the elevation of each point of the given area to the weighted targetHeight. + */ +function ElevationBlendingPainter(targetHeight, strength) +{ + this.targetHeight = targetHeight; + this.strength = strength; +} + +ElevationBlendingPainter.prototype.paint = function(area) +{ + for (let point of area.points) + g_Map.setHeight(point, this.strength * this.targetHeight + (1 - this.strength) * g_Map.getHeight(point)); +}; + /** * Absolute height change. */ diff --git a/binaries/data/mods/public/maps/random/schwarzwald.js b/binaries/data/mods/public/maps/random/schwarzwald.js index 94c235875c..09c17dbf98 100644 --- a/binaries/data/mods/public/maps/random/schwarzwald.js +++ b/binaries/data/mods/public/maps/random/schwarzwald.js @@ -136,8 +136,7 @@ var heighLimits = [ heightSeaGroundAdjusted + 7/8 * (heightRange.max - heightSeaGroundAdjusted), // 9 Upper forest border heightSeaGroundAdjusted + (heightRange.max - heightSeaGroundAdjusted)]; // 10 Hilltop -var playerHeight = (heighLimits[4] + heighLimits[5]) / 2; - +g_Map.log("Locating and smoothing playerbases"); for (let i = 0; i < numPlayers; ++i) { playerPosition[i] = Vector2D.add( @@ -145,7 +144,9 @@ for (let i = 0; i < numPlayers; ++i) new Vector2D(randFloat(minPlayerRadius, maxPlayerRadius), 0).rotate( -((playerAngleStart + i * playerAngleAddAvrg + randFloat(0, playerAngleMaxOff)) % (2 * Math.PI)))).round(); - rectangularSmoothToHeight(playerPosition[i], 20, 20, playerHeight, 0.8); + createArea( + new ClumpPlacer(diskArea(20), 0.8, 0.8, Infinity, playerPosition[i]), + new SmoothElevationPainter(ELEVATION_SET, g_Map.getHeight(playerPosition[i]), 20)); } placePlayerBases({ diff --git a/binaries/data/mods/public/maps/random/wild_lake.js b/binaries/data/mods/public/maps/random/wild_lake.js index 8417c42f78..9d4faee81d 100644 --- a/binaries/data/mods/public/maps/random/wild_lake.js +++ b/binaries/data/mods/public/maps/random/wild_lake.js @@ -466,15 +466,16 @@ if (g_Map.size >= 384) setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialHeightmap, 0.8); -// Apply simple erosion +g_Map.log("Eroding map"); for (let i = 0; i < 5; ++i) splashErodeMap(0.1); +g_Map.log("Smoothing map"); createArea( new MapBoundsPlacer(), new SmoothingPainter(1, 0.8, 1)); -// Final rescale +g_Map.log("Rescaling map"); rescaleHeightmap(heightRange.min, heightRange.max); Engine.SetProgress(25); @@ -499,16 +500,13 @@ let playerHeight = (playerHeightRange.min + playerHeightRange.max) / 2; // Avera g_Map.log("Chosing starting locations"); let [playerIDs, playerPosition] = sortPlayersByLocation(getStartLocationsByHeightmap(playerHeightRange, 1000, 30)); -Engine.SetProgress(30); +g_Map.log("Smoothing starting locations before height calculation"); +for (let position of playerPosition) + createArea( + new ClumpPlacer(diskArea(20), 0.8, 0.8, Infinity, position), + new SmoothElevationPainter(ELEVATION_SET, g_Map.getHeight(position), 20)); -/** - * Smooth Start Locations before height region calculation - */ -let playerBaseRadius = 35; -if (g_Map.size < 256) - playerBaseRadius = 25; -for (let p = 0; p < playerIDs.length; ++p) - rectangularSmoothToHeight(playerPosition[p], playerBaseRadius, playerBaseRadius, playerHeight, 0.7); +Engine.SetProgress(30); /** * Calculate tile centered height map after start position smoothing but before placing paths @@ -607,6 +605,7 @@ let mercenaryCamps = isNomad() ? 0 : Math.ceil(g_Map.size / 256); g_Map.log("Placing at most " + mercenaryCamps + " mercenary camps"); for (let i = 0; i < resourceSpots.length; ++i) { + let radius; let choice = i % 5; if (choice == 0) placeMine(resourceSpots[i], g_Gaia.stoneLarge); @@ -617,22 +616,27 @@ for (let i = 0; i < resourceSpots.length; ++i) if (choice == 3) { placeCamp(resourceSpots[i]); - rectangularSmoothToHeight(resourceSpots[i], 5, 5, g_Map.getHeight(resourceSpots[i]) - 10, 0.5); + radius = 5; } if (choice == 4) { if (mercenaryCamps) { placeStartingEntities(resourceSpots[i], 0, mercenaryCampGuards[currentBiome()]); - rectangularSmoothToHeight(resourceSpots[i], 15, 15, g_Map.getHeight(resourceSpots[i]), 0.5); + radius = 15; --mercenaryCamps; } else { placeCustomFortress(resourceSpots[i], pickRandom(fences), "other", 0, randomAngle()); - rectangularSmoothToHeight(resourceSpots[i], 10, 10, g_Map.getHeight(resourceSpots[i]), 0.5); + radius = 10; } } + + if (radius) + createArea( + new ClumpPlacer(diskArea(radius), 1, 1, Infinity, resourceSpots[i]), + new SmoothElevationPainter(ELEVATION_SET, g_Map.getHeight(resourceSpots[i]), radius)); } g_Map.ExportMap();