Index: binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js +++ binaries/data/mods/public/maps/random/rmgen/painter/DunePainter.js @@ -0,0 +1,65 @@ +/* + *@param {Bool} type - ELEVATION_MODIFY or ELEVATION_SET + *@param {Number} scale - global scale of dune size (width and height) - higher values make dunes bigger + *@param {Number} vertical_scale - scale of dune height - higher makes dunes taller + */ +function DunePainter(type = ELEVATION_MODIFY, scale = 1, vertical_scale = 1) +{ + this.type = type; + this.scale = scale; + this.vertical_scale = vertical_scale; +} +/* + *Dune base shape + *@param {Number} - x [0,1] value + *@param {Number} - xm [0,1] position of the dune crest + *@param {Bool} - d true for dune false for dome + */ +DunePainter.prototype.dune = function(x, crest_x = 0.75, dune = true) +{ + if (x < crest_x) + { + return (1 - Math.cos(Math.PI * x / crest_x)) * 0.5; + } + else + { + if (dune) + { + return (1 - Math.cos(Math.PI * (x - 1) / (crest_x - 1))) * 0.5; + } + else + { + return 1 - Math.cos(Math.PI * (x - 1) / (crest_x - 1) * 0.5); + } + } +} + +DunePainter.prototype.paint = function(area) +{ + const points = area.getPoints(); + const length = points.length; + //1st dune layer + const h1_1 = perlin_noise(points, 6, 2, 1, 1.1, true); + const h1_2 = perlin_noise(points, 3, 2, 1, 1, true); + const L1 = 17.0 * this.scale; + //2on dune layer + const h2_1 = perlin_noise(points, 8, 1, 2, 2, true); + const h2_2 = perlin_noise(points, 4, 2, 2, 2, true); + const L2 = 14.0 * this.scale; + //loop all the points + let elevation_type = this.type ? (i) => g_Map.getHeight(points[i]) : () => 0; + for (var i = 0; i < length; i++) + { + const X = points[i].x; + const Y = points[i].y; + //main dunes creation + const v1 = ((X + h1_1[i] * 13 + Y * 0.1) % L1) / L1; + const h1 = this.dune(v1, 0.60, false) * 9 * 0.9 + h1_1[i] * h1_2[i] * 150 * 0.1; + //secondary dunes + const v2 = ((X + h2_1[i] * 13 + Y * 0.1) % L2) / L2; + const h2 = this.dune(v2, 0.60, false) * 9 * 0.9 + h2_2[i] * 50 * 0.1; + const height = (h1 * 0.8 + h2 * 0.2) * this.vertical_scale * this.scale; + //add or set height + g_Map.setHeight(points[i], height + elevation_type(i)); + } +} \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/painter/PerlinPainter.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/painter/PerlinPainter.js +++ binaries/data/mods/public/maps/random/rmgen/painter/PerlinPainter.js @@ -0,0 +1,30 @@ +/* + *@param {Bool} type - ELEVATION_MODIFY or ELEVATION_SET + *@param {Array} points - Points + *@param {Number} frequency - float value >= 1. Noise size, higher value makes noise more compact + *@param {Number} octaves - int value >= 1Sub noise levels, higher the value gives noise more definition **can be as high as desired while the original frequcny doesn't reach < 1 + *@param {Number} scale - Scale size of the perlin noise (in the three axis) + *@param {Number} vertical_scale - Scale size of the perlin noise (in the vertical axis) + *@param {Bool} positive - Returns only on the range of [0,1] (in the case of default paremeters) + */ +function PerlinPainter(type = ELEVATION_MODIFY, frequency = 25, octaves = 10, scale = 1, vertical_scale = 2, positive = true) +{ + this.type = type; + this.frequency = frequency; + this.octaves = octaves; + this.scale = scale; + this.vertical_scale = vertical_scale; + this.positive = positive; +} +PerlinPainter.prototype.paint = function(area) +{ + const points = area.getPoints(); + const length = points.length; + const perlinHeights = perlin_noise(points, this.frequency, this.octaves, this.scale, this.vertical_scale, this.positive); + let elevation_type = this.type ? (i) => g_Map.getHeight(points[i]) : () => 0; + for (var i = 0; i < length; i++) + { + const height = perlinHeights[i]; + g_Map.setHeight(points[i], height + elevation_type(i)); + } +} \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/perlin_noise.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/perlin_noise.js +++ binaries/data/mods/public/maps/random/rmgen/perlin_noise.js @@ -0,0 +1,94 @@ +/* + *Perlin noise. Returns 1D array list of heights for each point given. With default paramenters returns in the range of [-1,1] + *@param {Array} points - Points + *@param {Number} frequency - float value >= 1. Noise size, higher value makes noise more compact + *@param {Number} octaves - int value >= 1Sub noise levels, higher the value gives noise more definition **can be as high as desired while the original frequcny doesn't reach < 1 + *@param {Number} scale - Scale size of the perlin noise (in the three axis) + *@param {Number} vertical_scale - Scale size of the perlin noise (in the vertical axis) + *@param {Bool} positive - Returns only on the range of [0,1] (in the case of default paremeters) + *@return {Array} + */ +function perlin_noise(points, frequency = 10, octaves = 1, scale = 1, vertical_scale = 1, positive = false) +{ + const size = points.length; + let heights = Array(size).fill(0); + frequency /= scale; + let weight = 1; + const frequency_multiplier = 1.333333; + const weight_multiplier = 0.75; + + for (let octave = 0; octave < octaves; octave++) + { + let noise2D = new Noise2D(frequency); + for (let i = 0; i < size; i++) + { + const ix = (points[i].x / 256.0) % 1; + const iy = (points[i].y / 256.0) % 1; + //noise2D gives values between [0.15,0.85] + //transform to a range of [-1,1] + let val = (noise2D.get(ix, iy) - 0.15) / 0.7 * 2 - 1; + heights[i] += val * weight; + } + frequency *= frequency_multiplier; + weight *= weight_multiplier; + } + heights.forEach((v, i, a) => a[i] *= scale * vertical_scale) + + if (positive) + { + const hdisp = vertical_scale * scale * Array(octaves).fill().map((e, i) => Math.pow(weight_multiplier, i + 1)).reduce((a, b) => a + b); + heights.forEach((v, i, a) => a[i] = (a[i] + hdisp) * 0.5) + } + + return heights; +} + +/* + *Perlin noise for individual points + */ +function perlin_noise_point(frequency = 10, octaves = 1, scale = 1, vertical_scale = 1, positive = false) +{ + this.frequency = frequency / scale; + this.octaves = octaves; + this.scale = scale; + this.vertical_scale = vertical_scale; + this.positive = positive; + this.frequency_multiplier = 1.333333; + this.weight_multiplier = 0.75; + let frequency_i = this.frequency; + let weight_i = 1; + + this.noise2D = []; + this.weights = []; + for (let octave = 0; octave < this.octaves; octave++) + { + this.noise2D.push(new Noise2D(frequency_i)) + this.weights.push(weight_i) + frequency_i *= this.frequency_multiplier; + weight_i *= this.weight_multiplier; + } + + this.hdisp = this.vertical_scale * this.scale * Array(this.octaves).fill().map((e, i) => Math.pow(this.weight_multiplier, i + 1)).reduce((a, b) => a + b) +} + +perlin_noise_point.prototype.get = function(point) +{ + let height = 0; + for (let i = 0; i < this.octaves; i++) + { + const x = (point.x / 256.0) % 1; + const y = (point.y / 256.0) % 1; + //noise2D gives values between [0.15,0.85] + let val = this.noise2D[i].get(x, y); + //give range [-1,1] + val = (val - 0.15) / 0.7 * 2 - 1; + height += val * this.weights[i]; + } + height *= this.scale * this.vertical_scale; + + if (this.positive) + { + height = (height + this.hdisp) * 0.5; + } + return height +} \ No newline at end of file Index: binaries/data/mods/public/maps/random/rmgen/placer/noncentered/PolygonPlacer.js =================================================================== --- binaries/data/mods/public/maps/random/rmgen/placer/noncentered/PolygonPlacer.js +++ binaries/data/mods/public/maps/random/rmgen/placer/noncentered/PolygonPlacer.js @@ -0,0 +1,72 @@ +/* + *Fills the inside of any polygon given 3 or more points - filling also works with degenerated and overlapping sections + */ +function PolygonPlacer(points) +{ + this.points = points; + this.horiLine = function(p1, p2) + { + this.dy = p2.y - p1.y; + this.dx = p2.x - p1.x; + if (this.dy != 0) + { + this.idy = 1 / this.dy; + } + this.py = p1.y; + this.px = p1.x; + this.minx = Math.min(p1.x, p2.x); + } + this.horiLine.prototype.cross = function(p) + { + if (this.dy == 0) + { + return false; + } + if (p.x < this.minx) + { + return false; + } + //0.000000000001131979327 added to fix a unknow artifact (doesn't crash but doesn't paint well if not added, unkown reason) + const t = (p.y - this.py) * this.idy + 0.000000000001131979327; + const x = this.px + t * this.dx; + return p.x >= x && t >= 0 && t <= 1; + } +}; +PolygonPlacer.prototype.isOdd = function(num) +{ + return num % 2; +} +PolygonPlacer.prototype.place = function(constraint) +{ + //bounding box + let plength = this.points.length; + let xlist = this.points.map((v) => v.x).sort((a, b) => a - b); + let ylist = this.points.map((v) => v.y).sort((a, b) => a - b); + const minx = Math.floor(xlist[0]); + const miny = Math.floor(ylist[0]); + const maxx = Math.ceil(xlist[plength - 1]); + const maxy = Math.ceil(ylist[plength - 1]); + //make lines + let lines = []; + for (let i = 0; i < plength; i++) + { + lines.push(new this.horiLine(this.points[i % plength], this.points[(i + 1) % plength])); + } + //check every possible point + let inpoints = []; + for (let x = minx; x < maxx; x++) + { + for (let y = miny; y < maxy; y++) + { + const p = new Vector2D(x, y); + if (constraint.allows(p)) + { + if (this.isOdd(lines.filter((v) => v.cross(p)).length)) + { + inpoints.push(p); + } + } + } + } + return inpoints; +}; \ No newline at end of file Index: binaries/data/mods/public/simulation/templates/special/filter/disableGarrisonHolder.xml =================================================================== --- binaries/data/mods/public/simulation/templates/special/filter/disableGarrisonHolder.xml +++ binaries/data/mods/public/simulation/templates/special/filter/disableGarrisonHolder.xml @@ -0,0 +1,4 @@ + + + +