Changeset View
Standalone View
binaries/data/mods/public/maps/random/rmgen-common/mountain_range_builder.js
Engine.LoadLibrary("rmgen"); | |||||
elexis: wrong include | |||||
Not Done Inline ActionsEh? What include should I use then? It uses Constraint, library, math, TerrainPainter, SmoothElevationPainter, TileClassPainter, PathPlacer, RandomMap and who knows what else. Also it works with this include. aeonios: Eh? What include should I use then? It uses Constraint, library, math, TerrainPainter… | |||||
/** | |||||
Not Done Inline ActionsApparently the argument that maps should rather do something unique than copying the mapgen approach of other maps wasn't convincing. elexis: Apparently the argument that maps should rather do something unique than copying the mapgen… | |||||
Not Done Inline ActionsWell the main point is that it can be used in a lot of different ways depending on how it's configured. It could also be extended to be applied to specific areas only. IMO extracting such potentially reusable algorithms and making them configurable is a better way to go about it. After all that's what libraries are for. The ancient stuff in rmgen2 was going in that direction but it wasn't particularly well designed and is no longer compatible with current features. Of course people are free to implement whatever custom functionality they need if they do want a unique map, but that doesn't mean they should have to reinvent the wheel even for really basic stuff. Making it friendlier to new devs is also always a plus. aeonios: Well the main point is that it can be used in a lot of different ways depending on how it's… | |||||
* @file This class creates random mountainranges ensuring that there are enough breaks to pass between them. | |||||
* | |||||
* To determine their location, random points are connected in shortest-cycle order and then trimmed to max length. | |||||
* | |||||
* Constructor arguments: | |||||
* @param {number} "height": how tall the mountains should be, in absolute height (ie set not modify). | |||||
* @param {number} "width": How wide the mountains will be. | |||||
* @param {number} "count": How many mountain range segments to create. | |||||
* @param {number} "maxLength": The longest that any connected mountain strip is allowed to be. Use to ensure that passable gaps are created. | |||||
* @param {number} "bumpiness": (0..1) How much the height of each peak should vary. | |||||
* @param {number} "waviness": (0..1) How much the mountain ranges should deviate from a straight line. | |||||
* @param {array strings} "terrain": String or array of two or more strings specifying terrain textures to paint. | |||||
* @param {number} "tileclass": The tileclass that the mountains should be assigned. | |||||
* @param {array constraints} "constraint": List of tileclasses to avoid painting over. | |||||
*/ | |||||
function MountainRangeBuilder(args) | |||||
{ | |||||
/** | |||||
* These parameters paint the mountainranges after their location was determined. | |||||
*/ | |||||
var height = args.height || scaleByMapSize(60, 120); | |||||
this.mountainWidth = args.width || height/4; | |||||
this.pathplacer = new PathPlacer(undefined, undefined, undefined, args.bumpiness || 0.4, scaleByMapSize(10, 25), args.waviness || 0.75, 0.2, 0.1); | |||||
this.painters = [ | |||||
new TerrainPainter(args.terrain), | |||||
new SmoothElevationPainter(ELEVATION_SET, height, 4 * height/this.mountainWidth, 3), | |||||
new TileClassPainter(args.tileclass) | |||||
]; | |||||
this.constraint = args.constraint; | |||||
/** | |||||
* Array of Vector2D locations where a mountainrange can start or end. | |||||
*/ | |||||
this.vertices = new Array(2 * (args.count || 16)).fill(0).map(() => g_Map.randomCoordinate(false)); | |||||
/** | |||||
* Maximum length that a mountain path can have. | |||||
*/ | |||||
this.maxLength = args.maxLength || fractionToTiles(0.6); | |||||
/** | |||||
* Number of mountainranges starting or ending at the given point. | |||||
*/ | |||||
this.vertexDegree = this.vertices.map(p => 0); | |||||
/** | |||||
* Each possible edge is an array containing two vertex indices. | |||||
* The algorithm adds possible edges consecutively and removes subsequently invalid edges. | |||||
*/ | |||||
this.possibleEdges = []; | |||||
this.InitPossibleEdges(); | |||||
/** | |||||
* Currently iterated item of possibleEdges that is either used as a mountainrange or removed from the possibleEdges. | |||||
*/ | |||||
this.index = undefined; | |||||
/** | |||||
* These variables hold the indices of the two points of that edge and the location of them as a Vector2D. | |||||
*/ | |||||
this.currentEdge = undefined; | |||||
this.currentEdgeStart = undefined; | |||||
this.currentEdgeEnd = undefined; | |||||
} | |||||
MountainRangeBuilder.prototype.InitPossibleEdges = function() | |||||
{ | |||||
var order = sortPointsShortestCycle(this.vertices); | |||||
var length = 0; | |||||
for (let i = 0; i < order.length - 1; ++i) | |||||
{ | |||||
length += this.vertices[order[i]].distanceTo(this.vertices[order[i+1]]); | |||||
if (length <= this.maxLength) | |||||
{ | |||||
this.possibleEdges.push([order[i], order[i+1]]); | |||||
}else | |||||
{ | |||||
length = 0; | |||||
} | |||||
} | |||||
}; | |||||
MountainRangeBuilder.prototype.UpdateCurrentEdge = function() | |||||
{ | |||||
this.currentEdge = this.possibleEdges[this.index]; | |||||
this.currentEdgeStart = this.vertices[this.currentEdge[0]]; | |||||
this.currentEdgeEnd = this.vertices[this.currentEdge[1]]; | |||||
}; | |||||
MountainRangeBuilder.prototype.PaintCurrentEdge = function() | |||||
{ | |||||
this.pathplacer.start = this.currentEdgeStart; | |||||
this.pathplacer.end = this.currentEdgeEnd; | |||||
this.pathplacer.width = this.mountainWidth; | |||||
// Creating mountainrange | |||||
if (!createArea(this.pathplacer, this.painters, this.constraint)) | |||||
return false; | |||||
return true; | |||||
}; | |||||
/** | |||||
* This is the only function meant to be publicly accessible. | |||||
*/ | |||||
MountainRangeBuilder.prototype.CreateMountainRanges = function() | |||||
{ | |||||
g_Map.log("Creating mountain ranges with " + this.possibleEdges.length + " possible edges"); | |||||
for (let i = 0; i < this.possibleEdges.length; ++i) | |||||
{ | |||||
this.index = i; | |||||
this.UpdateCurrentEdge(); | |||||
if (this.PaintCurrentEdge()) | |||||
{ | |||||
++this.vertexDegree[this.currentEdge[0]]; | |||||
++this.vertexDegree[this.currentEdge[1]]; | |||||
} | |||||
} | |||||
// Create circular mountains to connect ranges that share vertices. | |||||
for (let i = 0; i < this.vertexDegree.length; ++i) | |||||
{ | |||||
if (this.vertexDegree[i] > 1) | |||||
{ | |||||
createArea( | |||||
new ClumpPlacer(diskArea(this.mountainWidth / 2), 0.95, 0.6, Infinity, this.vertices[i]), | |||||
this.painters, | |||||
this.constraint); | |||||
} | |||||
} | |||||
}; | |||||
No newline at end of file | |||||
Not Done Inline ActionsI didn't investigate the algorithm changes, but we might consider if squashing this into a single function would end up in considerably less code. elexis: I didn't investigate the algorithm changes, but we might consider if squashing this into a… | |||||
Not Done Inline ActionsIt's technically possible just a PITA. I guess I'll work on that then. :P aeonios: It's technically possible just a PITA. I guess I'll work on that then. :P |
wrong include