mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Separate global heightmap manipulation functions to a library. Fixes #3764
This was SVN commit r18141.
This commit is contained in:
parent
1a3fb29ff3
commit
7471a0db63
5 changed files with 339 additions and 499 deletions
|
|
@ -4,6 +4,7 @@ timeArray.push(new Date().getTime());
|
|||
|
||||
// Importing rmgen libraries
|
||||
RMS.LoadLibrary("rmgen");
|
||||
RMS.LoadLibrary("heightmap");
|
||||
|
||||
const BUILDING_ANGlE = -PI/4;
|
||||
|
||||
|
|
@ -17,37 +18,7 @@ var numPlayers = getNumPlayers();
|
|||
var mapSize = getMapSize();
|
||||
|
||||
|
||||
//////////
|
||||
// Heightmap functionality
|
||||
//////////
|
||||
|
||||
// Some general heightmap settings
|
||||
const MIN_HEIGHT = - SEA_LEVEL; // 20, should be set in the libs!
|
||||
const MAX_HEIGHT = 0xFFFF/HEIGHT_UNITS_PER_METRE - SEA_LEVEL; // A bit smaler than 90, should be set in the libs!
|
||||
|
||||
// Add random heightmap generation functionality
|
||||
function getRandomReliefmap(minHeight, maxHeight)
|
||||
{
|
||||
minHeight = (minHeight || MIN_HEIGHT);
|
||||
maxHeight = (maxHeight || MAX_HEIGHT);
|
||||
|
||||
if (minHeight < MIN_HEIGHT)
|
||||
warn("getRandomReliefmap: Argument minHeight is smaler then the supported minimum height of " + MIN_HEIGHT + " (const MIN_HEIGHT): " + minHeight)
|
||||
|
||||
if (maxHeight > MAX_HEIGHT)
|
||||
warn("getRandomReliefmap: Argument maxHeight is smaler then the supported maximum height of " + MAX_HEIGHT + " (const MAX_HEIGHT): " + maxHeight)
|
||||
|
||||
var reliefmap = [];
|
||||
for (var x = 0; x <= mapSize; x++)
|
||||
{
|
||||
reliefmap.push([]);
|
||||
for (var y = 0; y <= mapSize; y++)
|
||||
reliefmap[x].push(randFloat(minHeight, maxHeight));
|
||||
}
|
||||
return reliefmap;
|
||||
}
|
||||
|
||||
// Apply a heightmap
|
||||
// Function to apply a heightmap
|
||||
function setReliefmap(reliefmap)
|
||||
{
|
||||
// g_Map.height = reliefmap;
|
||||
|
|
@ -56,70 +27,6 @@ function setReliefmap(reliefmap)
|
|||
setHeight(x, y, reliefmap[x][y]);
|
||||
}
|
||||
|
||||
// Get minimum and maxumum height used in a heightmap
|
||||
function getMinAndMaxHeight(reliefmap)
|
||||
{
|
||||
var height = {};
|
||||
height.min = Infinity;
|
||||
height.max = -Infinity;
|
||||
|
||||
for (var x = 0; x <= mapSize; x++)
|
||||
for (var y = 0; y <= mapSize; y++)
|
||||
{
|
||||
if (reliefmap[x][y] < height.min)
|
||||
height.min = reliefmap[x][y];
|
||||
else if (reliefmap[x][y] > height.max)
|
||||
height.max = reliefmap[x][y];
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
// Rescale a heightmap (Waterlevel is not taken into consideration!)
|
||||
function getRescaledReliefmap(reliefmap, minHeight, maxHeight)
|
||||
{
|
||||
var newReliefmap = deepcopy(reliefmap);
|
||||
minHeight = (minHeight || MIN_HEIGHT);
|
||||
maxHeight = (maxHeight || MAX_HEIGHT);
|
||||
|
||||
if (minHeight < MIN_HEIGHT)
|
||||
warn("getRescaledReliefmap: Argument minHeight is smaler then the supported minimum height of " + MIN_HEIGHT + " (const MIN_HEIGHT): " + minHeight)
|
||||
|
||||
if (maxHeight > MAX_HEIGHT)
|
||||
warn("getRescaledReliefmap: Argument maxHeight is smaler then the supported maximum height of " + MAX_HEIGHT + " (const MAX_HEIGHT): " + maxHeight)
|
||||
|
||||
var oldHeightRange = getMinAndMaxHeight(reliefmap);
|
||||
|
||||
for (var x = 0; x <= mapSize; x++)
|
||||
for (var y = 0; y <= mapSize; y++)
|
||||
newReliefmap[x][y] = minHeight + (reliefmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight);
|
||||
|
||||
return newReliefmap
|
||||
}
|
||||
|
||||
// Applying decay errosion (terrain independent)
|
||||
function getHeightErrosionedReliefmap(reliefmap, strength)
|
||||
{
|
||||
var newReliefmap = deepcopy(reliefmap);
|
||||
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
|
||||
|
||||
var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // Default
|
||||
|
||||
for (var x = 0; x <= mapSize; x++)
|
||||
for (var y = 0; y <= mapSize; y++)
|
||||
{
|
||||
var div = 0;
|
||||
for (var i = 0; i < map.length; i++)
|
||||
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
|
||||
}
|
||||
|
||||
return newReliefmap;
|
||||
}
|
||||
|
||||
|
||||
//////////
|
||||
// Prepare for hightmap munipulation
|
||||
//////////
|
||||
|
||||
// Set target min and max height depending on map size to make average stepness the same on all map sizes
|
||||
var heightRange = {"min": MIN_HEIGHT * mapSize / 8192, "max": MAX_HEIGHT * mapSize / 8192};
|
||||
|
|
@ -215,11 +122,12 @@ while (!goodStartPositionsFound)
|
|||
log("Starting giant while loop try " + tries);
|
||||
|
||||
// Generate reliefmap
|
||||
var myReliefmap = getRandomReliefmap(heightRange.min, heightRange.max);
|
||||
var myReliefmap = deepcopy(g_Map.height);
|
||||
setRandomHeightmap(heightRange.min, heightRange.max, myReliefmap);
|
||||
for (var i = 0; i < 50 + mapSize/4; i++) // Cycles depend on mapsize (more cycles -> bigger structures)
|
||||
myReliefmap = getHeightErrosionedReliefmap(myReliefmap, 1);
|
||||
globalSmoothHeightmap(0.8, myReliefmap);
|
||||
|
||||
myReliefmap = getRescaledReliefmap(myReliefmap, heightRange.min, heightRange.max);
|
||||
rescaleHeightmap(heightRange.min, heightRange.max, myReliefmap);
|
||||
setReliefmap(myReliefmap);
|
||||
|
||||
// Find good start position tiles
|
||||
|
|
|
|||
319
binaries/data/mods/public/maps/random/heightmap/heightmap.js
Normal file
319
binaries/data/mods/public/maps/random/heightmap/heightmap.js
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
/**
|
||||
* Heightmap manipulation functionality
|
||||
*
|
||||
* A heightmapt is an array of width arrays of height floats
|
||||
* Width and height is normally mapSize+1 (Number of vertices is one bigger than number of tiles in each direction)
|
||||
* The default heightmap is g_Map.height (See the Map object)
|
||||
*
|
||||
* @warning - Ambiguous naming and potential confusion:
|
||||
* To use this library use TILE_CENTERED_HEIGHT_MAP = false (default)
|
||||
* Otherwise TILE_CENTERED_HEIGHT_MAP has nothing to do with any tile centered map in this library
|
||||
* @todo - TILE_CENTERED_HEIGHT_MAP should be removed and g_Map.height should never be tile centered
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the height range of a heightmap
|
||||
* @param {array} [heightmap=g_Map.height] - The reliefmap the minimum and maximum height should be determined for
|
||||
* @return {object} [height] - Height range with 2 floats in properties "min" and "max"
|
||||
*/
|
||||
function getMinAndMaxHeight(heightmap = g_Map.height)
|
||||
{
|
||||
let height = {};
|
||||
height.min = Infinity;
|
||||
height.max = - Infinity;
|
||||
for (let x = 0; x < heightmap.length; ++x)
|
||||
{
|
||||
for (let y = 0; y < heightmap[x].length; ++y)
|
||||
{
|
||||
if (heightmap[x][y] < height.min)
|
||||
height.min = heightmap[x][y];
|
||||
else if (heightmap[x][y] > height.max)
|
||||
height.max = heightmap[x][y];
|
||||
}
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescales a heightmap so its minimum and maximum height is as the arguments told preserving it's global shape
|
||||
* @param {float} [minHeight=MIN_HEIGHT] - Minimum height that should be used for the resulting heightmap
|
||||
* @param {float} [maxHeight=MAX_HEIGHT] - Maximum height that should be used for the resulting heightmap
|
||||
* @param {array} [heightmap=g_Map.height] - A reliefmap
|
||||
* @todo Add preserveCostline to leave a certain height untoucht and scale below and above that seperately
|
||||
*/
|
||||
function rescaleHeightmap(minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT, heightmap = g_Map.height)
|
||||
{
|
||||
let oldHeightRange = getMinAndMaxHeight(heightmap);
|
||||
let max_x = heightmap.length;
|
||||
let max_y = heightmap[0].length;
|
||||
for (let x = 0; x < max_x; ++x)
|
||||
for (let y = 0; y < max_y; ++y)
|
||||
heightmap[x][y] = minHeight + (heightmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get start location with the largest minimum distance between players
|
||||
* @param {array} [heightRange] - The height range start locations are allowed
|
||||
* @param {integer} [maxTries=1000] - How often random player distributions are rolled to be compared
|
||||
* @param {float} [minDistToBorder=20] - How far start locations have to be away from the map border
|
||||
* @param {integer} [numberOfPlayers=g_MapSettings.PlayerData.length] - How many start locations should be placed
|
||||
* @param {array} [heightmap=g_Map.height] - The reliefmap for the start locations to be placed on
|
||||
* @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular
|
||||
* @return {array} [finalStartLoc] - Array of 2D points in the format { "x": float, "y": float}
|
||||
*/
|
||||
function getStartLocationsByHeightmap(heightRange, maxTries = 1000, minDistToBorder = 20, numberOfPlayers = g_MapSettings.PlayerData.length - 1, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap)
|
||||
{
|
||||
let validStartLoc = [];
|
||||
let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius
|
||||
for (let x = minDistToBorder; x < heightmap.length - minDistToBorder; ++x)
|
||||
for (let y = minDistToBorder; y < heightmap[0].length - minDistToBorder; ++y)
|
||||
if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max) // Is in height range
|
||||
if (!isCircular || r - getDistance(x, y, r, r) >= minDistToBorder) // Is far enough away from map border
|
||||
validStartLoc.push({ "x": x, "y": y });
|
||||
|
||||
let maxMinDist = 0;
|
||||
let finalStartLoc;
|
||||
for (let tries = 0; tries < maxTries; ++tries)
|
||||
{
|
||||
let startLoc = [];
|
||||
let minDist = Infinity;
|
||||
for (let p = 0; p < numberOfPlayers; ++p)
|
||||
startLoc.push(validStartLoc[randInt(validStartLoc.length)]);
|
||||
for (let p1 = 0; p1 < numberOfPlayers - 1; ++p1)
|
||||
{
|
||||
for (let p2 = p1 + 1; p2 < numberOfPlayers; ++p2)
|
||||
{
|
||||
let dist = getDistance(startLoc[p1].x, startLoc[p1].y, startLoc[p2].x, startLoc[p2].y);
|
||||
if (dist < minDist)
|
||||
minDist = dist;
|
||||
}
|
||||
}
|
||||
if (minDist > maxMinDist)
|
||||
{
|
||||
maxMinDist = minDist;
|
||||
finalStartLoc = startLoc;
|
||||
}
|
||||
}
|
||||
|
||||
return finalStartLoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
* @param {array} [avoidPoints] - An array of 2D points (arrays of length 2), points that will be avoided in the given minDistance e.g. start locations
|
||||
* @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
|
||||
* @param {array} [heightmap=g_Map.height] - The reliefmap the entities should be distributed on
|
||||
* @param {array} [entityList=[g_Gaia.stoneLarge, g_Gaia.metalLarge]] - Entity/actor strings to be placed with placeObject()
|
||||
* @param {integer} [maxTries=1000] - How often random player distributions are rolled to be compared
|
||||
* @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular
|
||||
*/
|
||||
function distributeEntitiesByHeight(heightRange, avoidPoints, minDistance = 30, entityList = [g_Gaia.stoneLarge, g_Gaia.metalLarge], maxTries = 1000, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap)
|
||||
{
|
||||
let placements = deepcopy(avoidPoints);
|
||||
let validTiles = [];
|
||||
let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius
|
||||
for (let x = minDistance; x < heightmap.length - minDistance; ++x)
|
||||
for (let y = minDistance; y < heightmap[0].length - minDistance; ++y)
|
||||
if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max) // Has the right height
|
||||
if (!isCircular || r - getDistance(x, y, r, r) >= minDistance) // Is far enough away from map border
|
||||
validTiles.push({ "x": x, "y": y });
|
||||
|
||||
for (let tries = 0; tries < maxTries; ++tries)
|
||||
{
|
||||
let tile = validTiles[randInt(validTiles.length)];
|
||||
let isValid = true;
|
||||
for (let p = 0; p < placements.length; ++p)
|
||||
{
|
||||
if (getDistance(placements[p].x, placements[p].y, tile.x, tile.y) < minDistance)
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isValid)
|
||||
{
|
||||
placeObject(tile.x, tile.y, entityList[randInt(entityList.length)], 0, randFloat(0, 2*PI));
|
||||
placements.push(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a given heightmap to entirely random values within a given range
|
||||
* @param {float} [minHeight=MIN_HEIGHT] - Lower limit of the random height to be rolled
|
||||
* @param {float} [maxHeight=MAX_HEIGHT] - Upper limit of the random height to be rolled
|
||||
* @param {array} [heightmap=g_Map.height] - The reliefmap that should be randomized
|
||||
*/
|
||||
function setRandomHeightmap(minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT, heightmap = g_Map.height)
|
||||
{
|
||||
for (let x = 0; x < heightmap.length; ++x)
|
||||
for (let y = 0; y < heightmap[0].length; ++y)
|
||||
heightmap[x][y] = randFloat(minHeight, maxHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the heightmap to a relatively realistic shape
|
||||
* The function doubles the size of the initial heightmap (if given, else a random 2x2 one) until it's big enough, then the extend is cut off
|
||||
* @note min/maxHeight will not necessarily be present in the heightmap
|
||||
* @note On circular maps the edges (given by initialHeightmap) may not be in the playable map area
|
||||
* @note The impact of the initial heightmap depends on its size and target map size
|
||||
* @param {float} [minHeight=MIN_HEIGHT] - Lower limit of the random height to be rolled
|
||||
* @param {float} [maxHeight=MAX_HEIGHT] - Upper limit of the random height to be rolled
|
||||
* @param {array} [initialHeightmap] - Optional, Small (e.g. 3x3) heightmap describing the global shape of the map e.g. an island [[MIN_HEIGHT, MIN_HEIGHT, MIN_HEIGHT], [MIN_HEIGHT, MAX_HEIGHT, MIN_HEIGHT], [MIN_HEIGHT, MIN_HEIGHT, MIN_HEIGHT]]
|
||||
* @param {float} [smoothness=0.5] - Float between 0 (rough, more local structures) to 1 (smoother, only larger scale structures)
|
||||
* @param {array} [heightmap=g_Map.height] - The reliefmap that will be set by this function
|
||||
*/
|
||||
function setBaseTerrainDiamondSquare(minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT, initialHeightmap = undefined, smoothness = 0.5, heightmap = g_Map.height)
|
||||
{
|
||||
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)
|
||||
warn("setBaseTerrainDiamondSquare: heightRange <= 0");
|
||||
|
||||
let offset = heightRange / 2;
|
||||
|
||||
// Double initialHeightmap width until target width is reached (diamond square method)
|
||||
let newHeightmap = [];
|
||||
while (initialHeightmap.length < heightmap.length)
|
||||
{
|
||||
newHeightmap = [];
|
||||
let oldWidth = initialHeightmap.length;
|
||||
// Square
|
||||
for (let x = 0; x < 2 * oldWidth - 1; ++x)
|
||||
{
|
||||
newHeightmap.push([]);
|
||||
for (let y = 0; y < 2 * oldWidth - 1; ++y)
|
||||
{
|
||||
if (x % 2 == 0 && y % 2 == 0) // Old tile
|
||||
newHeightmap[x].push(initialHeightmap[x/2][y/2]);
|
||||
else if (x % 2 == 1 && y % 2 == 1) // New tile with diagonal old tile neighbors
|
||||
{
|
||||
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);
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else // New tile with straight old tile neighbors
|
||||
newHeightmap[x].push(undefined); // Define later
|
||||
}
|
||||
}
|
||||
// Diamond
|
||||
for (let x = 0; x < 2 * oldWidth - 1; ++x)
|
||||
{
|
||||
for (let y = 0; y < 2 * oldWidth - 1; ++y)
|
||||
{
|
||||
if (newHeightmap[x][y] !== undefined)
|
||||
continue;
|
||||
|
||||
if (x > 0 && x + 1 < newHeightmap.length - 1 && y > 0 && y + 1 < newHeightmap.length - 1) // Not a border tile
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 4;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x < newHeightmap.length - 1 && y > 0 && y < newHeightmap.length - 1) // Left border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x][y-1]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x > 0 && y > 0 && y < newHeightmap.length - 1) // Right border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x > 0 && x < newHeightmap.length - 1 && y < newHeightmap.length - 1) // Bottom border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x > 0 && x < newHeightmap.length - 1 && y > 0) // Top border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
initialHeightmap = deepcopy(newHeightmap);
|
||||
offset /= Math.pow(2, smoothness);
|
||||
}
|
||||
|
||||
// Cut initialHeightmap to fit target width
|
||||
let shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)];
|
||||
for (let x = 0; x < heightmap.length; ++x)
|
||||
for (let y = 0; y < heightmap[0].length; ++y)
|
||||
heightmap[x][y] = newHeightmap[x + shift[0]][y + shift[1]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Smoothens the entire map
|
||||
* @param {float} [strength=0.8] - How strong the smooth effect should be: 0 means no effect at all, 1 means quite strong, higher values might cause interferences, better apply it multiple times
|
||||
* @param {array} [heightmap=g_Map.height] - The heightmap to be smoothed
|
||||
* @param {array} [smoothMap=[[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]] - Array of offsets discribing the neighborhood tiles to smooth the height of a tile to
|
||||
*/
|
||||
function globalSmoothHeightmap(strength = 0.8, heightmap = g_Map.height, smoothMap = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]])
|
||||
{
|
||||
let referenceHeightmap = deepcopy(heightmap);
|
||||
let max_x = heightmap.length;
|
||||
let max_y = heightmap[0].length;
|
||||
for (let x = 0; x < max_x; ++x)
|
||||
{
|
||||
for (let y = 0; y < max_y; ++y)
|
||||
{
|
||||
for (let i = 0; i < smoothMap.length; ++i)
|
||||
{
|
||||
let mapX = x + smoothMap[i][0];
|
||||
let mapY = y + smoothMap[i][1];
|
||||
if (mapX >= 0 && mapX < max_x && mapY >= 0 && mapY < max_y)
|
||||
heightmap[x][y] += strength / smoothMap.length * (referenceHeightmap[mapX][mapY] - referenceHeightmap[x][y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {object} [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 = round(center.x);
|
||||
let y = round(center.y);
|
||||
dx = round(dx);
|
||||
dy = 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,3 @@
|
|||
function decayErrodeHeightmap(strength, heightmap)
|
||||
{
|
||||
strength = strength || 0.9; // 0 to 1
|
||||
heightmap = heightmap || g_Map.height;
|
||||
|
||||
let referenceHeightmap = deepcopy(heightmap);
|
||||
// let map = [[1, 0], [0, 1], [-1, 0], [0, -1]]; // faster
|
||||
let map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother
|
||||
let max_x = heightmap.length;
|
||||
let max_y = heightmap[0].length;
|
||||
for (let x = 0; x < max_x; ++x)
|
||||
for (let y = 0; y < max_y; ++y)
|
||||
for (let i = 0; i < map.length; ++i)
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns starting position in tile coordinates for the given player.
|
||||
*/
|
||||
|
|
@ -28,6 +12,7 @@ function getPlayerTileCoordinates(playerIdx, teamIdx, fractionX, fractionZ)
|
|||
}
|
||||
|
||||
RMS.LoadLibrary("rmgen");
|
||||
RMS.LoadLibrary("heightmap");
|
||||
|
||||
const g_InitialMines = 1;
|
||||
const g_InitialMineDistance = 14;
|
||||
|
|
@ -372,7 +357,7 @@ RMS.SetProgress(70);
|
|||
|
||||
log("Smoothing heightmap...");
|
||||
for (let i = 0; i < 5; ++i)
|
||||
decayErrodeHeightmap(0.5);
|
||||
globalSmoothHeightmap();
|
||||
|
||||
// repaint clLand to compensate for smoothing
|
||||
unPaintTileClassBasedOnHeight(-10, 10, 3, clLand);
|
||||
|
|
@ -420,7 +405,7 @@ createAreas(
|
|||
scaleByMapSize(4, 13)
|
||||
);
|
||||
for (let i = 0; i < 3; ++i)
|
||||
decayErrodeHeightmap(0.2);
|
||||
globalSmoothHeightmap();
|
||||
|
||||
createStragglerTrees(
|
||||
[oTree1, oTree2, oTree4, oTree3],
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@ const HEIGHT_UNITS_PER_METRE = 92;
|
|||
const MIN_MAP_SIZE = 128;
|
||||
const MAX_MAP_SIZE = 512;
|
||||
const FALLBACK_CIV = "athen";
|
||||
/**
|
||||
* Constants needed for heightmap_manipulation.js
|
||||
*/
|
||||
const MAX_HEIGHT_RANGE = 0xFFFF / HEIGHT_UNITS_PER_METRE // Engine limit, Roughly 700 meters
|
||||
const MIN_HEIGHT = - SEA_LEVEL;
|
||||
const MAX_HEIGHT = MAX_HEIGHT_RANGE - SEA_LEVEL;
|
||||
|
||||
function fractionToTiles(f)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
// Created by Niek ten Brinke (aka niektb)
|
||||
// Based on FeXoR's Daimond Square Algorithm for heightmap generation and several official random maps
|
||||
|
||||
'use strict';
|
||||
|
||||
RMS.LoadLibrary('rmgen');
|
||||
|
||||
// initialize map
|
||||
RMS.LoadLibrary("heightmap");
|
||||
|
||||
log('Initializing map...');
|
||||
|
||||
|
|
@ -178,378 +172,6 @@ HeightPlacer.prototype.place = function (constraint) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
/*
|
||||
Takes an array of 2D points (arrays of length 2)
|
||||
Returns the order to go through the points for the shortest closed path (array of indices)
|
||||
*/
|
||||
function getOrderOfPointsForShortestClosePath(points)
|
||||
{
|
||||
var order = [];
|
||||
var distances = [];
|
||||
|
||||
if (points.length <= 3)
|
||||
{
|
||||
for (var i = 0; i < points.length; i++)
|
||||
order.push(i);
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
// Just add the first 3 points
|
||||
var pointsToAdd = deepcopy(points);
|
||||
for (var i = 0; i < min(points.length, 3); i++)
|
||||
{
|
||||
order.push(i);
|
||||
pointsToAdd.shift(i);
|
||||
if (i)
|
||||
distances.push(getDistance(points[order[i]][0], points[order[i]][1], points[order[i - 1]][0], points[order[i - 1]][1]));
|
||||
}
|
||||
distances.push(getDistance(points[order[0]][0], points[order[0]][1], points[order[order.length - 1]][0], points[order[order.length - 1]][1]));
|
||||
|
||||
// Add remaining points so the path lengthens the least
|
||||
var numPointsToAdd = pointsToAdd.length;
|
||||
for (var i = 0; i < numPointsToAdd; i++)
|
||||
{
|
||||
var indexToAddTo = undefined;
|
||||
var minEnlengthen = Infinity;
|
||||
var minDist1 = 0;
|
||||
var minDist2 = 0;
|
||||
|
||||
for (var k = 0; k < order.length; k++)
|
||||
{
|
||||
var dist1 = getDistance(pointsToAdd[0][0], pointsToAdd[0][1], points[order[k]][0], points[order[k]][1]);
|
||||
var dist2 = getDistance(pointsToAdd[0][0], pointsToAdd[0][1], points[order[(k + 1) % order.length]][0], points[order[(k + 1) % order.length]][1]);
|
||||
var enlengthen = dist1 + dist2 - distances[k];
|
||||
|
||||
if (enlengthen < minEnlengthen)
|
||||
{
|
||||
indexToAddTo = k;
|
||||
minEnlengthen = enlengthen;
|
||||
minDist1 = dist1;
|
||||
minDist2 = dist2;
|
||||
}
|
||||
}
|
||||
|
||||
order.splice(indexToAddTo + 1, 0, i + 3);
|
||||
distances.splice(indexToAddTo, 1, minDist1, minDist2);
|
||||
pointsToAdd.shift();
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
//
|
||||
// Heightmap functionality
|
||||
//
|
||||
////////////////
|
||||
|
||||
// Some heightmap constants
|
||||
const MIN_HEIGHT = - SEA_LEVEL; // -20
|
||||
const MAX_HEIGHT = 0xFFFF/HEIGHT_UNITS_PER_METRE - SEA_LEVEL; // A bit smaller than 90
|
||||
|
||||
// Get the diferrence between minimum and maxumum height
|
||||
function getMinAndMaxHeight(reliefmap)
|
||||
{
|
||||
var height = {};
|
||||
height.min = Infinity;
|
||||
height.max = - Infinity;
|
||||
|
||||
for (var x = 0; x < reliefmap.length; x++)
|
||||
for (var y = 0; y < reliefmap[x].length; y++)
|
||||
{
|
||||
if (reliefmap[x][y] < height.min)
|
||||
height.min = reliefmap[x][y];
|
||||
else if (reliefmap[x][y] > height.max)
|
||||
height.max = reliefmap[x][y];
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
function rescaleHeightmap(minHeight, maxHeight, heightmap)
|
||||
{
|
||||
minHeight = (minHeight || - SEA_LEVEL);
|
||||
maxHeight = (maxHeight || 0xFFFF / HEIGHT_UNITS_PER_METRE - SEA_LEVEL);
|
||||
heightmap = (heightmap || g_Map.height);
|
||||
|
||||
var oldHeightRange = getMinAndMaxHeight(heightmap);
|
||||
var max_x = heightmap.length;
|
||||
var max_y = heightmap[0].length;
|
||||
|
||||
for (var x = 0; x < max_x; x++)
|
||||
for (var y = 0; y < max_y; y++)
|
||||
heightmap[x][y] = minHeight + (heightmap[x][y] - oldHeightRange.min) / (oldHeightRange.max - oldHeightRange.min) * (maxHeight - minHeight);
|
||||
}
|
||||
|
||||
/*
|
||||
getStartLocationsByHeightmap
|
||||
Takes
|
||||
hightRange An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)
|
||||
heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
|
||||
maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
|
||||
minDistToBorder Optional, default is 20, an integer, how far start locations have to be
|
||||
numberOfPlayers Optional, default is getNumPlayers, an integer, how many start locations should be placed
|
||||
Returns
|
||||
An array of 2D points (arrays of length 2)
|
||||
*/
|
||||
function getStartLocationsByHeightmap(hightRange, maxTries, minDistToBorder, numberOfPlayers, heightmap)
|
||||
{
|
||||
maxTries = (maxTries || 1000);
|
||||
minDistToBorder = (minDistToBorder || 20);
|
||||
numberOfPlayers = (numberOfPlayers || getNumPlayers());
|
||||
heightmap = (heightmap || g_Map.height);
|
||||
|
||||
var validStartLocTiles = [];
|
||||
|
||||
for (var x = minDistToBorder; x < heightmap.length - minDistToBorder; x++)
|
||||
for (var y = minDistToBorder; y < heightmap[0].length - minDistToBorder; y++)
|
||||
|
||||
if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight
|
||||
validStartLocTiles.push([x, y]);
|
||||
|
||||
var maxMinDist = 0;
|
||||
for (var tries = 0; tries < maxTries; tries++)
|
||||
{
|
||||
var startLoc = [];
|
||||
var minDist = heightmap.length;
|
||||
|
||||
for (var p = 0; p < numberOfPlayers; p++)
|
||||
startLoc.push(validStartLocTiles[randInt(validStartLocTiles.length)]);
|
||||
|
||||
for (var p1 = 0; p1 < numberOfPlayers - 1; p1++)
|
||||
for (var p2 = p1 + 1; p2 < numberOfPlayers; p2++)
|
||||
{
|
||||
var dist = getDistance(startLoc[p1][0], startLoc[p1][1], startLoc[p2][0], startLoc[p2][1]);
|
||||
if (dist < minDist)
|
||||
minDist = dist;
|
||||
}
|
||||
|
||||
if (minDist > maxMinDist)
|
||||
{
|
||||
maxMinDist = minDist;
|
||||
var finalStartLoc = startLoc;
|
||||
}
|
||||
}
|
||||
|
||||
return finalStartLoc;
|
||||
}
|
||||
|
||||
/*
|
||||
derivateEntitiesByHeight
|
||||
Takes
|
||||
hightRange An associative array with keys 'min' and 'max' each containing a float (the height range start locations are allowed)
|
||||
startLoc An array of 2D points (arrays of length 2)
|
||||
heightmap Optional, default is g_Map.height, an array of (map width) arrays of (map depth) floats
|
||||
entityList Array of entities/actors (strings to be placed with placeObject())
|
||||
maxTries Optional, default is 1000, an integer, how often random player distributions are rolled to be compared
|
||||
minDistance Optional, default is 30, an integer, how far start locations have to be away from start locations and the map border
|
||||
Returns
|
||||
An array of 2D points (arrays of length 2)
|
||||
*/
|
||||
function derivateEntitiesByHeight(hightRange, startLoc, entityList, maxTries, minDistance, heightmap)
|
||||
{
|
||||
entityList = (entityList || [templateMetalMine, templateStoneMine]);
|
||||
maxTries = (maxTries || 1000);
|
||||
minDistance = (minDistance || 40);
|
||||
heightmap = (heightmap || g_Map.height);
|
||||
|
||||
var placements = deepcopy(startLoc);
|
||||
var validTiles = [];
|
||||
|
||||
for (var x = minDistance; x < heightmap.length - minDistance; x++)
|
||||
for (var y = minDistance; y < heightmap[0].length - minDistance; y++)
|
||||
if (heightmap[x][y] > hightRange.min && heightmap[x][y] < hightRange.max) // Has the right hight
|
||||
validTiles.push([x, y]);
|
||||
|
||||
if (!validTiles.length)
|
||||
return;
|
||||
|
||||
for (var tries = 0; tries < maxTries; tries++)
|
||||
{
|
||||
var tile = validTiles[randInt(validTiles.length)];
|
||||
var isValid = true;
|
||||
|
||||
for (var p = 0; p < placements.length; p++)
|
||||
if (getDistance(placements[p][0], placements[p][1], tile[0], tile[1]) < minDistance)
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
placeObject(tile[0], tile[1], entityList[randInt(entityList.length)], 0, randFloat(0, 2*PI));
|
||||
// placeObject(tile[0], tile[1], 'actor|geology/decal_stone_medit_b.xml', 0, randFloat(0, 2*PI));
|
||||
placements.push(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
//
|
||||
// Base terrain generation functionality
|
||||
//
|
||||
////////////////
|
||||
|
||||
function setBaseTerrainDiamondSquare(minHeight, maxHeight, smoothness, initialHeightmap, heightmap)
|
||||
{
|
||||
// Make some arguments optional
|
||||
minHeight = (minHeight || 0);
|
||||
maxHeight = (maxHeight || 1);
|
||||
|
||||
var heightRange = maxHeight - minHeight;
|
||||
if (heightRange <= 0)
|
||||
warn('setBaseTerrainDiamondSquare: heightRange < 0');
|
||||
|
||||
smoothness = (smoothness || 1);
|
||||
|
||||
var offset = heightRange / 2;
|
||||
initialHeightmap = (initialHeightmap || [[randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)], [randFloat(minHeight / 2, maxHeight / 2), randFloat(minHeight / 2, maxHeight / 2)]]);
|
||||
|
||||
// Double initialHeightmap width untill target width is reached (diamond square method)
|
||||
while (initialHeightmap.length < heightmap.length)
|
||||
{
|
||||
var newHeightmap = [];
|
||||
var oldWidth = initialHeightmap.length;
|
||||
|
||||
// Square
|
||||
for (var x = 0; x < 2 * oldWidth - 1; x++)
|
||||
{
|
||||
newHeightmap.push([]);
|
||||
for (var y = 0; y < 2 * oldWidth - 1; y++)
|
||||
{
|
||||
if (x % 2 === 0 && y % 2 === 0) // Old tile
|
||||
newHeightmap[x].push(initialHeightmap[x/2][y/2]);
|
||||
else if (x % 2 == 1 && y % 2 == 1) // New tile with diagonal old tile neighbors
|
||||
{
|
||||
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);
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else // New tile with straight old tile neighbors
|
||||
newHeightmap[x].push(undefined); // Define later
|
||||
}
|
||||
}
|
||||
|
||||
// Diamond
|
||||
for (var x = 0; x < 2 * oldWidth - 1; x++)
|
||||
for (var y = 0; y < 2 * oldWidth - 1; y++)
|
||||
{
|
||||
if (newHeightmap[x][y] === undefined)
|
||||
{
|
||||
if (x > 0 && x + 1 < newHeightmap.length - 1 && y > 0 && y + 1 < newHeightmap.length - 1) // Not a border tile
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 4;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x < newHeightmap.length - 1 && y > 0 && y < newHeightmap.length - 1) // Left border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x][y-1]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x > 0 && y > 0 && y < newHeightmap.length - 1) // Right border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x][y+1] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x > 0 && x < newHeightmap.length - 1 && y < newHeightmap.length - 1) // Bottom border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x][y+1] + newHeightmap[x-1][y]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
else if (x > 0 && x < newHeightmap.length - 1 && y > 0) // Top border
|
||||
{
|
||||
newHeightmap[x][y] = (newHeightmap[x+1][y] + newHeightmap[x-1][y] + newHeightmap[x][y-1]) / 3;
|
||||
newHeightmap[x][y] += (newHeightmap[x][y] - minHeight) / heightRange * randFloat(-offset, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initialHeightmap = deepcopy(newHeightmap);
|
||||
offset /= Math.pow(2, smoothness);
|
||||
}
|
||||
|
||||
// Cut initialHeightmap to fit target width
|
||||
var shift = [floor((newHeightmap.length - heightmap.length) / 2), floor((newHeightmap[0].length - heightmap[0].length) / 2)];
|
||||
for (var x = 0; x < heightmap.length; x++)
|
||||
for (var y = 0; y < heightmap[0].length; y++)
|
||||
heightmap[x][y] = newHeightmap[x + shift[0]][y + shift[1]];
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
//
|
||||
// Terrain erosion functionality
|
||||
//
|
||||
////////////////
|
||||
|
||||
function decayErrodeHeightmap(strength, heightmap)
|
||||
{
|
||||
strength = (strength || 0.9); // 0 to 1
|
||||
heightmap = (heightmap || g_Map.height);
|
||||
|
||||
var referenceHeightmap = deepcopy(heightmap);
|
||||
// var map = [[1, 0], [0, 1], [-1, 0], [0, -1]]; // faster
|
||||
var map = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]; // smoother
|
||||
var max_x = heightmap.length;
|
||||
var max_y = heightmap[0].length;
|
||||
|
||||
for (var x = 0; x < max_x; x++)
|
||||
for (var y = 0; y < max_y; y++)
|
||||
for (var i = 0; i < map.length; i++)
|
||||
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
|
||||
}
|
||||
|
||||
function rectangularSmoothToHeight(center, dx, dy, targetHeight, strength, heightmap)
|
||||
{
|
||||
var x = round(center[0]);
|
||||
var y = round(center[1]);
|
||||
dx = round(dx);
|
||||
dy = round(dy);
|
||||
strength = (strength || 1);
|
||||
heightmap = (heightmap || g_Map.height);
|
||||
|
||||
var heightmapWin = [];
|
||||
for (var wx = 0; wx < 2 * dx + 1; wx++)
|
||||
{
|
||||
heightmapWin.push([]);
|
||||
for (var wy = 0; wy < 2 * dy + 1; wy++)
|
||||
{
|
||||
var actualX = x - dx + wx;
|
||||
var 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 (var wx = 0; wx < 2 * dx + 1; wx++)
|
||||
for (var wy = 0; wy < 2 * dy + 1; wy++)
|
||||
{
|
||||
var actualX = x - dx + wx;
|
||||
var 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
|
||||
var scaleX = 1 - (wx / dx - 1) * (wx / dx - 1);
|
||||
var scaleY = 1 - (wy / dy - 1) * (wy / dy - 1);
|
||||
|
||||
heightmap[actualX][actualY] = heightmapWin[wx][wy] + strength * scaleX * scaleY * (targetHeight - heightmapWin[wx][wy]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////
|
||||
//
|
||||
// Actually do stuff
|
||||
//
|
||||
////////////////
|
||||
|
||||
////////////////
|
||||
// Set height limits and water level by map size
|
||||
|
|
@ -571,10 +193,10 @@ setWaterHeight(waterHeight);
|
|||
// Setting a 3x3 Grid as initial heightmap
|
||||
var initialReliefmap = [[heightRange.max, heightRange.max, heightRange.max], [heightRange.max, heightRange.min, heightRange.max], [heightRange.max, heightRange.max, heightRange.max]];
|
||||
|
||||
setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, 0.5, initialReliefmap, g_Map.height);
|
||||
setBaseTerrainDiamondSquare(heightRange.min, heightRange.max, initialReliefmap);
|
||||
// Apply simple erosion
|
||||
for (var i = 0; i < 5; i++)
|
||||
decayErrodeHeightmap(0.5);
|
||||
globalSmoothHeightmap();
|
||||
rescaleHeightmap(heightRange.min, heightRange.max);
|
||||
|
||||
RMS.SetProgress(50);
|
||||
|
|
@ -642,8 +264,8 @@ for (var i=0; i < numPlayers; i++)
|
|||
}
|
||||
|
||||
// Add further stone and metal mines
|
||||
derivateEntitiesByHeight({'min': heighLimits[3], 'max': ((heighLimits[4]+heighLimits[3])/2)}, startLocations);
|
||||
derivateEntitiesByHeight({'min': ((heighLimits[5]+heighLimits[6])/2), 'max': heighLimits[7]}, startLocations);
|
||||
distributeEntitiesByHeight({ 'min': heighLimits[3], 'max': ((heighLimits[4] + heighLimits[3]) / 2) }, startLocations, 40);
|
||||
distributeEntitiesByHeight({ 'min': ((heighLimits[5] + heighLimits[6]) / 2), 'max': heighLimits[7] }, startLocations, 40);
|
||||
|
||||
RMS.SetProgress(50);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue