Index: ps/trunk/binaries/data/mods/public/maps/random/caledonian_meadows.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/caledonian_meadows.js +++ ps/trunk/binaries/data/mods/public/maps/random/caledonian_meadows.js @@ -6,136 +6,6 @@ let genStartTime = Date.now(); /** - * Returns an approximation of the heights of the tiles between the vertices, a tile centered heightmap - * A tile centered heightmap is one smaller in width and height than an ordinary heightmap - * It is meant to e.g. texture a map by height (x/y coordinates correspond to those of the terrain texture map) - * Don't use this to override g_Map height (Potentially breaks the map)! - * @param {array} [heightmap=g_Map.height] - A reliefmap the tile centered version should be build from - */ -function getTileCenteredHeightmap(heightmap = g_Map.height) -{ - let max_x = heightmap.length - 1; - let max_y = heightmap[0].length - 1; - let tchm = []; - for (let x = 0; x < max_x; ++x) - { - tchm[x] = new Float32Array(max_y); - for (let y = 0; y < max_y; ++y) - tchm[x][y] = 0.25 * (heightmap[x][y] + heightmap[x + 1][y] + heightmap[x][y + 1] + heightmap[x + 1][y + 1]); - } - return tchm; -} - -/** - * Returns an inclination map corresponding to the tiles between the heightmaps vertices: - * array of heightmap width-1 arrays of height-1 vectors (associative arrays) of the from: - * {"x": x_slope, "y": y_slope] so a 2D Vector pointing to the hightest incline (with the length the incline in the vectors direction) - * The x and y coordinates of a tile in the terrain texture map correspond to those of the inclination map - * @param {array} [heightmap=g_Map.height] - The reliefmap the inclination map is to be generated from - */ -function getInclineMap(heightmap) -{ - heightmap = (heightmap || g_Map.height); - let max_x = heightmap.length - 1; - let max_y = heightmap[0].length - 1; - let inclineMap = []; - for (let x = 0; x < max_x; ++x) - { - inclineMap[x] = []; - for (let y = 0; y < max_y; ++y) - { - let dx = heightmap[x + 1][y] - heightmap[x][y]; - let dy = heightmap[x][y + 1] - heightmap[x][y]; - let next_dx = heightmap[x + 1][y + 1] - heightmap[x][y + 1]; - let next_dy = heightmap[x + 1][y + 1] - heightmap[x + 1][y]; - inclineMap[x][y] = {"x": 0.5 * (dx + next_dx), "y": 0.5 * (dy + next_dy)}; - } - } - return inclineMap; -} - -/** - * Returns a slope map (same form as the a heightmap with one less width and height) - * Not normalized. Only returns the steepness (float), not the direction of incline. - * The x and y coordinates of a tile in the terrain texture map correspond to those of the slope map - * @param {array} [inclineMap=getInclineMap(g_Map.height)] - A map with the absolute inclination for each tile - */ -function getSlopeMap(inclineMap = getInclineMap(g_Map.height)) -{ - let max_x = inclineMap.length; - let slopeMap = []; - for (let x = 0; x < max_x; ++x) - { - let max_y = inclineMap[x].length; - slopeMap[x] = new Float32Array(max_y); - for (let y = 0; y < max_y; ++y) - slopeMap[x][y] = Math.pow(inclineMap[x][y].x * inclineMap[x][y].x + inclineMap[x][y].y * inclineMap[x][y].y, 0.5); - } - return slopeMap; -} - -/** - * Returns the order to go through the points for the shortest closed path (array of indices) - * @param {array} [points] - Points to be sorted of the form {"x": x_value, "y": y_value} - */ -function getOrderOfPointsForShortestClosePath(points) -{ - let order = []; - let distances = []; - if (points.length <= 3) - { - for (let i = 0; i < points.length; ++i) - order.push(i); - - return order; - } - - // Just add the first 3 points - let pointsToAdd = deepcopy(points); - for (let i = 0; i < 3; ++i) - { - order.push(i); - pointsToAdd.shift(i); - if (i) - distances.push(getDistance(points[order[i]].x, points[order[i]].y, points[order[i - 1]].x, points[order[i - 1]].y)); - } - - distances.push(getDistance( - points[order[0]].x, - points[order[0]].y, - points[order[order.length - 1]].x, - points[order[order.length - 1]].y)); - - // Add remaining points so the path lengthens the least - let numPointsToAdd = pointsToAdd.length; - for (let i = 0; i < numPointsToAdd; ++i) - { - let indexToAddTo = undefined; - let minEnlengthen = Infinity; - let minDist1 = 0; - let minDist2 = 0; - for (let k = 0; k < order.length; ++k) - { - let dist1 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[k]].x, points[order[k]].y); - let dist2 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[(k + 1) % order.length]].x, points[order[(k + 1) % order.length]].y); - let 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; -} - -/** * Drags a path to a target height smoothing it at the edges and return some points along the path. */ function placeRandomPathToHeight( @@ -168,126 +38,6 @@ return pathPoints; } -function getGrad(wrapped = true, scalarField = g_Map.height) -{ - let vectorField = []; - let max_x = scalarField.length; - let max_y = scalarField[0].length; - if (!wrapped) - { - max_x -= 1; - max_y -= 1; - } - - for (let x = 0; x < max_x; ++x) - { - vectorField.push([]); - for (let y = 0; y < max_y; ++y) - vectorField[x].push({"x": scalarField[(x + 1) % max_x][y] - scalarField[x][y], "y": scalarField[x][(y + 1) % max_y] - scalarField[x][y]}); - } - - return vectorField; -} - -function splashErodeMap(strength = 1, heightmap = g_Map.height) -{ - let max_x = heightmap.length; - let max_y = heightmap[0].length; - - let dHeight = getGrad(heightmap); - - for (let x = 0; x < max_x; ++x) - { - let next_x = (x + 1) % max_x; - let prev_x = (x + max_x - 1) % max_x; - for (let y = 0; y < max_y; ++y) - { - let next_y = (y + 1) % max_y; - let prev_y = (y + max_y - 1) % max_y; - - let slopes = [- dHeight[x][y].x, - dHeight[x][y].y, dHeight[prev_x][y].x, dHeight[x][prev_y].y]; - - let sumSlopes = 0; - for (let i = 0; i < slopes.length; ++i) - if (slopes[i] > 0) - sumSlopes += slopes[i]; - - let drain = []; - for (let i = 0; i < slopes.length; ++i) - { - drain.push(0); - if (slopes[i] > 0) - drain[i] += min(strength * slopes[i] / sumSlopes, slopes[i]); - } - - let sumDrain = 0; - for (let i = 0; i < drain.length; ++i) - sumDrain += drain[i]; - - // Apply changes to maps - heightmap[x][y] -= sumDrain; - heightmap[next_x][y] += drain[0]; - heightmap[x][next_y] += drain[1]; - heightmap[prev_x][y] += drain[2]; - heightmap[x][prev_y] += drain[3]; - } - } -} - -/** - * 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 objects of the form {"x": int, "y": int, "dist": int}, points that will be avoided in the given dist e.g. start locations - * @param {object} [avoidClass=undefined] - TileClass to be avoided - * @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 {integer} [maxTries=2 * g_Map.size] - How often random player distributions are rolled to be compared (256 to 1024) - * @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular - */ -function getPointsByHeight(heightRange, avoidPoints = [], avoidClass = undefined, minDistance = 20, maxTries = 2 * g_Map.size, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap) -{ - let points = []; - let placements = deepcopy(avoidPoints); - let validVertices = []; - let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius - let avoidMap; - - if (avoidClass !== undefined) - avoidMap = g_Map.tileClasses[avoidClass].inclusionCount; - - for (let x = minDistance; x < heightmap.length - minDistance; ++x) - { - for (let y = minDistance; y < heightmap[0].length - minDistance; ++y) - { - if (avoidClass !== undefined && // Avoid adjecting tiles in avoidClass - (avoidMap[max(x - 1, 0)][y] > 0 || - avoidMap[x][max(y - 1, 0)] > 0 || - avoidMap[min(x + 1, avoidMap.length - 1)][y] > 0 || - avoidMap[x][min(y + 1, avoidMap[0].length - 1)] > 0)) - continue; - - if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && // Has correct height - (!isCircular || r - getDistance(x, y, r, r) >= minDistance)) // Enough distance to map border - validVertices.push({ "x": x, "y": y , "dist": minDistance}); - } - } - - for (let tries = 0; tries < maxTries; ++tries) - { - let point = pickRandom(validVertices); - if (placements.every(p => getDistance(p.x, p.y, point.x, point.y) > max(minDistance, p.dist))) - { - points.push(point); - placements.push(point); - } - if (tries != 0 && tries % 100 == 0) // Time Check - log(points.length + " points found after " + tries + " tries after " + ((Date.now() - genStartTime) / 1000) + "s"); - } - - return points; -} - - /** * Design resource spots */ Index: ps/trunk/binaries/data/mods/public/maps/random/heightmap/heightmap.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/heightmap/heightmap.js +++ ps/trunk/binaries/data/mods/public/maps/random/heightmap/heightmap.js @@ -336,3 +336,196 @@ } } } + +/** + * 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 objects of the form { "x": int, "y": int, "dist": int }, points that will be avoided in the given dist e.g. start locations + * @param {object} [avoidClass=undefined] - TileClass to be avoided + * @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 {integer} [maxTries=2 * g_Map.size] - How often random player distributions are rolled to be compared (256 to 1024) + * @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular + */ +function getPointsByHeight(heightRange, avoidPoints = [], avoidClass = undefined, minDistance = 20, maxTries = 2 * g_Map.size, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap) +{ + let points = []; + let placements = deepcopy(avoidPoints); + let validVertices = []; + let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius + let avoidMap; + + if (avoidClass !== undefined) + avoidMap = g_Map.tileClasses[avoidClass].inclusionCount; + + for (let x = minDistance; x < heightmap.length - minDistance; ++x) + { + for (let y = minDistance; y < heightmap[0].length - minDistance; ++y) + { + if (avoidClass !== undefined && // Avoid adjecting tiles in avoidClass + (avoidMap[max(x - 1, 0)][y] > 0 || + avoidMap[x][max(y - 1, 0)] > 0 || + avoidMap[min(x + 1, avoidMap.length - 1)][y] > 0 || + avoidMap[x][min(y + 1, avoidMap[0].length - 1)] > 0)) + continue; + + if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && // Has correct height + (!isCircular || r - getDistance(x, y, r, r) >= minDistance)) // Enough distance to map border + validVertices.push({ "x": x, "y": y , "dist": minDistance}); + } + } + + for (let tries = 0; tries < maxTries; ++tries) + { + let point = pickRandom(validVertices); + if (placements.every(p => getDistance(p.x, p.y, point.x, point.y) > max(minDistance, p.dist))) + { + points.push(point); + placements.push(point); + } + if (tries != 0 && tries % 100 == 0) // Time Check + log(points.length + " points found after " + tries + " tries after " + ((Date.now() - genStartTime) / 1000) + "s"); + } + + return points; +} + +/** + * Returns an approximation of the heights of the tiles between the vertices, a tile centered heightmap + * A tile centered heightmap is one smaller in width and height than an ordinary heightmap + * It is meant to e.g. texture a map by height (x/y coordinates correspond to those of the terrain texture map) + * Don't use this to override g_Map height (Potentially breaks the map)! + * @param {array} [heightmap=g_Map.height] - A reliefmap the tile centered version should be build from + */ +function getTileCenteredHeightmap(heightmap = g_Map.height) +{ + let max_x = heightmap.length - 1; + let max_y = heightmap[0].length - 1; + let tchm = []; + for (let x = 0; x < max_x; ++x) + { + tchm[x] = new Float32Array(max_y); + for (let y = 0; y < max_y; ++y) + tchm[x][y] = 0.25 * (heightmap[x][y] + heightmap[x + 1][y] + heightmap[x][y + 1] + heightmap[x + 1][y + 1]); + } + return tchm; +} + +/** + * Returns a slope map (same form as the a heightmap with one less width and height) + * Not normalized. Only returns the steepness (float), not the direction of incline. + * The x and y coordinates of a tile in the terrain texture map correspond to those of the slope map + * @param {array} [inclineMap=getInclineMap(g_Map.height)] - A map with the absolute inclination for each tile + */ +function getSlopeMap(inclineMap = getInclineMap(g_Map.height)) +{ + let max_x = inclineMap.length; + let slopeMap = []; + for (let x = 0; x < max_x; ++x) + { + let max_y = inclineMap[x].length; + slopeMap[x] = new Float32Array(max_y); + for (let y = 0; y < max_y; ++y) + slopeMap[x][y] = Math.pow(inclineMap[x][y].x * inclineMap[x][y].x + inclineMap[x][y].y * inclineMap[x][y].y, 0.5); + } + return slopeMap; +} + +/** + * Returns an inclination map corresponding to the tiles between the heightmaps vertices: + * array of heightmap width-1 arrays of height-1 vectors (associative arrays) of the form: + * { "x": x_slope, "y": y_slope ] so a 2D Vector pointing to the hightest incline (with the length the incline in the vectors direction) + * The x and y coordinates of a tile in the terrain texture map correspond to those of the inclination map + * @param {array} [heightmap=g_Map.height] - The reliefmap the inclination map is to be generated from + */ +function getInclineMap(heightmap) +{ + heightmap = (heightmap || g_Map.height); + let max_x = heightmap.length - 1; + let max_y = heightmap[0].length - 1; + let inclineMap = []; + for (let x = 0; x < max_x; ++x) + { + inclineMap[x] = []; + for (let y = 0; y < max_y; ++y) + { + let dx = heightmap[x + 1][y] - heightmap[x][y]; + let dy = heightmap[x][y + 1] - heightmap[x][y]; + let next_dx = heightmap[x + 1][y + 1] - heightmap[x][y + 1]; + let next_dy = heightmap[x + 1][y + 1] - heightmap[x + 1][y]; + inclineMap[x][y] = { "x" : 0.5 * (dx + next_dx), "y" : 0.5 * (dy + next_dy) }; + } + } + return inclineMap; +} + +function getGrad(wrapped = true, scalarField = g_Map.height) +{ + let vectorField = []; + let max_x = scalarField.length; + let max_y = scalarField[0].length; + if (!wrapped) + { + max_x -= 1; + max_y -= 1; + } + + for (let x = 0; x < max_x; ++x) + { + vectorField.push([]); + for (let y = 0; y < max_y; ++y) + { + vectorField[x].push({ + "x" : scalarField[(x + 1) % max_x][y] - scalarField[x][y], + "y" : scalarField[x][(y + 1) % max_y] - scalarField[x][y] + }); + } + } + + return vectorField; +} + +function splashErodeMap(strength = 1, heightmap = g_Map.height) +{ + let max_x = heightmap.length; + let max_y = heightmap[0].length; + + let dHeight = getGrad(heightmap); + + for (let x = 0; x < max_x; ++x) + { + let next_x = (x + 1) % max_x; + let prev_x = (x + max_x - 1) % max_x; + for (let y = 0; y < max_y; ++y) + { + let next_y = (y + 1) % max_y; + let prev_y = (y + max_y - 1) % max_y; + + let slopes = [- dHeight[x][y].x, - dHeight[x][y].y, dHeight[prev_x][y].x, dHeight[x][prev_y].y]; + + let sumSlopes = 0; + for (let i = 0; i < slopes.length; ++i) + if (slopes[i] > 0) + sumSlopes += slopes[i]; + + let drain = []; + for (let i = 0; i < slopes.length; ++i) + { + drain.push(0); + if (slopes[i] > 0) + drain[i] += min(strength * slopes[i] / sumSlopes, slopes[i]); + } + + let sumDrain = 0; + for (let i = 0; i < drain.length; ++i) + sumDrain += drain[i]; + + // Apply changes to maps + heightmap[x][y] -= sumDrain; + heightmap[next_x][y] += drain[0]; + heightmap[x][next_y] += drain[1]; + heightmap[prev_x][y] += drain[2]; + heightmap[x][prev_y] += drain[3]; + } + } +} Index: ps/trunk/binaries/data/mods/public/maps/random/rmgen/library.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/rmgen/library.js +++ ps/trunk/binaries/data/mods/public/maps/random/rmgen/library.js @@ -537,3 +537,64 @@ { return g_Map.getTexture(x, y); } + +/** + * Returns the order to go through the points for the shortest closed path (array of indices) + * @param {array} [points] - Points to be sorted of the form { "x": x_value, "y": y_value } + */ +function getOrderOfPointsForShortestClosePath(points) +{ + let order = []; + let distances = []; + if (points.length <= 3) + { + for (let i = 0; i < points.length; ++i) + order.push(i); + + return order; + } + + // Just add the first 3 points + let pointsToAdd = deepcopy(points); + for (let i = 0; i < 3; ++i) + { + order.push(i); + pointsToAdd.shift(i); + if (i) + distances.push(getDistance(points[order[i]].x, points[order[i]].y, points[order[i - 1]].x, points[order[i - 1]].y)); + } + + distances.push(getDistance( + points[order[0]].x, + points[order[0]].y, + points[order[order.length - 1]].x, + points[order[order.length - 1]].y)); + + // Add remaining points so the path lengthens the least + let numPointsToAdd = pointsToAdd.length; + for (let i = 0; i < numPointsToAdd; ++i) + { + let indexToAddTo; + let minEnlengthen = Infinity; + let minDist1 = 0; + let minDist2 = 0; + for (let k = 0; k < order.length; ++k) + { + let dist1 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[k]].x, points[order[k]].y); + let dist2 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[(k + 1) % order.length]].x, points[order[(k + 1) % order.length]].y); + let 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; +} Index: ps/trunk/binaries/data/mods/public/maps/random/wild_lake.js =================================================================== --- ps/trunk/binaries/data/mods/public/maps/random/wild_lake.js +++ ps/trunk/binaries/data/mods/public/maps/random/wild_lake.js @@ -6,261 +6,6 @@ let genStartTime = Date.now(); /** - * Returns an approximation of the heights of the tiles between the vertices, a tile centered heightmap - * A tile centered heightmap is one smaller in width and height than an ordinary heightmap - * It is meant to e.g. texture a map by height (x/y coordinates correspond to those of the terrain texture map) - * Don't use this to override g_Map height (Potentially breaks the map)! - * @param {array} [heightmap=g_Map.height] - A reliefmap the tile centered version should be build from - */ -function getTileCenteredHeightmap(heightmap = g_Map.height) -{ - let max_x = heightmap.length - 1; - let max_y = heightmap[0].length - 1; - let tchm = []; - for (let x = 0; x < max_x; ++x) - { - tchm[x] = new Float32Array(max_y); - for (let y = 0; y < max_y; ++y) - tchm[x][y] = 0.25 * (heightmap[x][y] + heightmap[x + 1][y] + heightmap[x][y + 1] + heightmap[x + 1][y + 1]); - } - return tchm; -} - -/** - * Returns an inclination map corresponding to the tiles between the heightmaps vertices: - * array of heightmap width-1 arrays of height-1 vectors (associative arrays) of the from: - * {"x": x_slope, "y": y_slope] so a 2D Vector pointing to the hightest incline (with the length the incline in the vectors direction) - * The x and y coordinates of a tile in the terrain texture map correspond to those of the inclination map - * @param {array} [heightmap=g_Map.height] - The reliefmap the inclination map is to be generated from - */ -function getInclineMap(heightmap) -{ - heightmap = (heightmap || g_Map.height); - let max_x = heightmap.length - 1; - let max_y = heightmap[0].length - 1; - let inclineMap = []; - for (let x = 0; x < max_x; ++x) - { - inclineMap[x] = []; - for (let y = 0; y < max_y; ++y) - { - let dx = heightmap[x + 1][y] - heightmap[x][y]; - let dy = heightmap[x][y + 1] - heightmap[x][y]; - let next_dx = heightmap[x + 1][y + 1] - heightmap[x][y + 1]; - let next_dy = heightmap[x + 1][y + 1] - heightmap[x + 1][y]; - inclineMap[x][y] = { "x" : 0.5 * (dx + next_dx), "y" : 0.5 * (dy + next_dy) }; - } - } - return inclineMap; -} - -/** - * Returns a slope map (same form as the a heightmap with one less width and height) - * Not normalized. Only returns the steepness (float), not the direction of incline. - * The x and y coordinates of a tile in the terrain texture map correspond to those of the slope map - * @param {array} [inclineMap=getInclineMap(g_Map.height)] - A map with the absolute inclination for each tile - */ -function getSlopeMap(inclineMap = getInclineMap(g_Map.height)) -{ - let max_x = inclineMap.length; - let slopeMap = []; - for (let x = 0; x < max_x; ++x) - { - let max_y = inclineMap[x].length; - slopeMap[x] = new Float32Array(max_y); - for (let y = 0; y < max_y; ++y) - slopeMap[x][y] = Math.pow(inclineMap[x][y].x * inclineMap[x][y].x + inclineMap[x][y].y * inclineMap[x][y].y, 0.5); - } - return slopeMap; -} - -/** - * Returns the order to go through the points for the shortest closed path (array of indices) - * @param {array} [points] - Points to be sorted of the form {"x": x_value, "y": y_value} - */ -function getOrderOfPointsForShortestClosePath(points) -{ - let order = []; - let distances = []; - if (points.length <= 3) - { - for (let i = 0; i < points.length; ++i) - order.push(i); - - return order; - } - - // Just add the first 3 points - let pointsToAdd = deepcopy(points); - for (let i = 0; i < 3; ++i) - { - order.push(i); - pointsToAdd.shift(i); - if (i) - distances.push(getDistance(points[order[i]].x, points[order[i]].y, points[order[i - 1]].x, points[order[i - 1]].y)); - } - - distances.push(getDistance( - points[order[0]].x, - points[order[0]].y, - points[order[order.length - 1]].x, - points[order[order.length - 1]].y)); - - // Add remaining points so the path lengthens the least - let numPointsToAdd = pointsToAdd.length; - for (let i = 0; i < numPointsToAdd; ++i) - { - let indexToAddTo; - let minEnlengthen = Infinity; - let minDist1 = 0; - let minDist2 = 0; - for (let k = 0; k < order.length; ++k) - { - let dist1 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[k]].x, points[order[k]].y); - let dist2 = getDistance(pointsToAdd[0].x, pointsToAdd[0].y, points[order[(k + 1) % order.length]].x, points[order[(k + 1) % order.length]].y); - let 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; -} - -function getGrad(wrapped = true, scalarField = g_Map.height) -{ - let vectorField = []; - let max_x = scalarField.length; - let max_y = scalarField[0].length; - if (!wrapped) - { - max_x -= 1; - max_y -= 1; - } - - for (let x = 0; x < max_x; ++x) - { - vectorField.push([]); - for (let y = 0; y < max_y; ++y) - { - vectorField[x].push({ - "x" : scalarField[(x + 1) % max_x][y] - scalarField[x][y], - "y" : scalarField[x][(y + 1) % max_y] - scalarField[x][y] - }); - } - } - - return vectorField; -} - -function splashErodeMap(strength = 1, heightmap = g_Map.height) -{ - let max_x = heightmap.length; - let max_y = heightmap[0].length; - - let dHeight = getGrad(heightmap); - - for (let x = 0; x < max_x; ++x) - { - let next_x = (x + 1) % max_x; - let prev_x = (x + max_x - 1) % max_x; - for (let y = 0; y < max_y; ++y) - { - let next_y = (y + 1) % max_y; - let prev_y = (y + max_y - 1) % max_y; - - let slopes = [- dHeight[x][y].x, - dHeight[x][y].y, dHeight[prev_x][y].x, dHeight[x][prev_y].y]; - - let sumSlopes = 0; - for (let i = 0; i < slopes.length; ++i) - if (slopes[i] > 0) - sumSlopes += slopes[i]; - - let drain = []; - for (let i = 0; i < slopes.length; ++i) - { - drain.push(0); - if (slopes[i] > 0) - drain[i] += min(strength * slopes[i] / sumSlopes, slopes[i]); - } - - let sumDrain = 0; - for (let i = 0; i < drain.length; ++i) - sumDrain += drain[i]; - - // Apply changes to maps - heightmap[x][y] -= sumDrain; - heightmap[next_x][y] += drain[0]; - heightmap[x][next_y] += drain[1]; - heightmap[prev_x][y] += drain[2]; - heightmap[x][prev_y] += drain[3]; - } - } -} - -/** - * Meant to place e.g. resource spots within a height range - * @param {object} [heightRange] - The height range in which to place the entities (An associative array with keys "min" and "max" each containing a float) - * @param {object} [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 - * @param {object} [avoidClass=undefined] - TileClass to be avoided - * @param {integer} [minDistance=20] - How many tile widths the entities to place have to be away from each other, start locations and the map border - * @param {object} [heightmap=g_Map.height] - The reliefmap the entities should be distributed on - * @param {integer} [maxTries=2 * g_Map.size] - How often random player distributions are rolled to be compared (256 to 1024) - * @param {boolean} [isCircular=g_MapSettings.CircularMap] - If the map is circular or rectangular - */ -function getPointsByHeight(heightRange, avoidPoints = [], avoidClass = undefined, minDistance = 20, maxTries = 2 * g_Map.size, heightmap = g_Map.height, isCircular = g_MapSettings.CircularMap) -{ - let points = []; - let placements = deepcopy(avoidPoints); - let validVertices = []; - let r = 0.5 * (heightmap.length - 1); // Map center x/y as well as radius - let avoidMap; - - if (avoidClass !== undefined) - avoidMap = g_Map.tileClasses[avoidClass].inclusionCount; - - for (let x = minDistance; x < heightmap.length - minDistance; ++x) - { - for (let y = minDistance; y < heightmap[0].length - minDistance; ++y) - { - if (avoidClass !== undefined && // Avoid adjecting tiles in avoidClass - (avoidMap[max(x - 1, 0)][y] > 0 || - avoidMap[x][max(y - 1, 0)] > 0 || - avoidMap[min(x + 1, avoidMap.length - 1)][y] > 0 || - avoidMap[x][min(y + 1, avoidMap[0].length - 1)] > 0)) - continue; - - if (heightmap[x][y] > heightRange.min && heightmap[x][y] < heightRange.max && // Has correct height - (!isCircular || r - getDistance(x, y, r, r) >= minDistance)) // Enough distance to map border - validVertices.push({ "x": x, "y": y , "dist": minDistance }); - } - } - - for (let tries = 0; tries < maxTries; ++tries) - { - let point = pickRandom(validVertices); - if (placements.every(p => getDistance(p.x, p.y, point.x, point.y) > max(minDistance, p.dist))) - { - points.push(point); - placements.push(point); - } - if (tries != 0 && tries % 100 == 0) // Time Check - log(points.length + " points found after " + tries + " tries after " + ((Date.now() - genStartTime) / 1000) + "s"); - } - - return points; -} - - -/** * getArray - To ensure a terrain texture is contained within an array */ function getArray(stringOrArrayOfStrings) @@ -270,11 +15,6 @@ return stringOrArrayOfStrings; } - -/** - * Biome settings - */ - randomizeBiome() // Terrain, entities and actors