1
0
forked from 0ad/0ad

Extend the random map river algorithm (7d0cc59136) to allow arbitrary start and end points.

Reverse engineer and cleanup obfuscated Rivers map code, refs
a9b963c3a5.
Use vector algebra to replace magic equations and express geometric
intend.
Fix seed typo by removing river curve duplicate.

On the Rivers map, use areAllies directly instead of copying the
diplomacies to a 2D array called isRiver.
Use modulo operator instead of an if-statement with duplication to
determine the neighbor of the last player, refs 9272153ee7.

This was SVN commit r20429.
This commit is contained in:
elexis 2017-11-09 19:08:31 +00:00
parent b3dbcc457b
commit e596ef6011
12 changed files with 196 additions and 215 deletions

View File

@ -153,9 +153,11 @@ for (var i = 0; i < numPlayers; i++)
RMS.SetProgress(30);
paintRiver({
"horizontal": false,
"parallel": false,
"position": 0.5,
"startX": 0.5,
"startZ": 0,
"endX": 0.5,
"endZ": 1,
"width": 0.35,
"fadeDist": 0.025,
"deviation": 0,
@ -163,7 +165,7 @@ paintRiver({
"landHeight": 2,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
if (height < 0.7)
addToClass(ix, iz, clWater);

View File

@ -404,11 +404,12 @@ for (let i = 0; i < numPlayers; ++i)
}
RMS.SetProgress(20);
log("Creating the river");
paintRiver({
"horizontal": false,
"parallel": true,
"position": 0.5,
"startX": 0.5,
"startZ": 0,
"endX": 0.5,
"endZ": 1,
"width": waterWidth,
"fadeDist": 0.025,
"deviation": 0,
@ -416,7 +417,7 @@ paintRiver({
"landHeight": 2,
"meanderShort": 30,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
if (height < 0.7)
addToClass(ix, iz, clWater);

View File

@ -147,9 +147,11 @@ for (var i = 0; i < numPlayers; i++)
RMS.SetProgress(10);
paintRiver({
"horizontal": true,
"parallel": false,
"position": 0.5,
"startX": 0,
"startZ": 0.5,
"endX": 1,
"endZ": 0.5,
"width": 0.25,
"fadeDist": 0.01,
"deviation": 0,
@ -157,7 +159,7 @@ paintRiver({
"landHeight": landHeight,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
placeTerrain(ix, iz, height < -1.5 ? tWater : tShore);
},
"landFunc": (ix, iz, shoreDist1, shoreDist2) => {

View File

@ -52,6 +52,11 @@ var clLand = createTileClass();
var clRiver = createTileClass();
var clShallow = createTileClass();
var landHeight = 3;
var shoreHeight = 2;
var shallowHeight = -1.5;
var waterHeight = -3;
log("Create the continent body");
createArea(
new ChainPlacer(
@ -65,7 +70,7 @@ createArea(
[Math.floor(mapSize * 0.49)]),
[
new LayeredPainter([tGrass, tGrass, tGrass], [4, 2]),
new SmoothElevationPainter(ELEVATION_SET, 3, 4),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 4),
paintClass(clLand)
],
null);
@ -161,28 +166,26 @@ for (var i = 0; i < numPlayers; i++)
placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius);
}
RMS.SetProgress(20);
var shallowHeight = -1.5;
paintRiver({
"horizontal": false,
"parallel": true,
"constraint": stayClasses(clLand, 0),
"position": 0.5,
"startX": 0.5,
"startZ": 0,
"endX": 0.5,
"endZ": 1,
"width": 0.07,
"fadeDist": 0.025,
"deviation": 0.005,
"waterHeight": -3,
"landHeight": 2,
"deviation": 0.0025,
"waterHeight": waterHeight,
"landHeight": shoreHeight,
"meanderShort": 12,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, z) => {
addToClass(ix, iz, clRiver);
placeTerrain(ix, iz, tWater);
let z = iz / (mapSize + 1.0);
if (height < shallowHeight && (
z > 0.3 && z < 0.4 ||
z > 0.5 && z < 0.6 ||

View File

@ -135,9 +135,11 @@ for (var i = 0; i < numPlayers; i++)
RMS.SetProgress(10);
paintRiver({
"horizontal": true,
"parallel": true,
"position": 1,
"startX": 0,
"startZ": 1,
"endX": 1,
"endZ": 1,
"width": 0.5,
"fadeDist": 0.05,
"deviation": 0,
@ -145,7 +147,7 @@ paintRiver({
"landHeight": 1,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
if (height < 0)
addToClass(ix, iz, clWater);

View File

@ -136,9 +136,11 @@ const waterPos = 0.31;
const mountainPos = 0.69;
paintRiver({
"horizontal": false,
"parallel": false,
"position": 0,
"startX": 0,
"startZ": 0,
"endX": 0,
"endZ": 1,
"width": 2 * waterPos,
"fadeDist": 0.025,
"deviation": 0,
@ -146,11 +148,11 @@ paintRiver({
"landHeight": 3,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
addToClass(ix, iz, clWater);
},
"landFunc": (ix, iz, shoreDist1, shoreDist2) => {
if (ix > mountainPos * mapSize)
if (ix > fractionToTiles(mountainPos))
addToClass(ix, iz, clMountains)
}
});

View File

@ -113,17 +113,19 @@ for (var i = 0; i < numPlayers; i++)
RMS.SetProgress(15);
paintRiver({
"horizontal": true,
"parallel": true,
"position": 1,
"startX": 0,
"startZ": 1,
"endX": 1,
"endZ": 1,
"width": 0.62,
"fadeDist": 8 / mapSize,
"fadeDist": tilesToFraction(8),
"deviation": 0,
"waterHeight": -5,
"landHeight": 3,
"meanderShort": 0,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
addToClass(ix, iz, clWater);
},
"landFunc": (ix, iz, shoreDist1, shoreDist2) => {

View File

@ -138,9 +138,11 @@ for (var i = 0; i < numPlayers; i++)
RMS.SetProgress(30);
paintRiver({
"horizontal": false,
"parallel": true,
"position": 0,
"startX": 0,
"startZ": 0,
"endX": 0,
"endZ": 1,
"width": 1,
"fadeDist": 0.05,
"deviation": 0,
@ -148,7 +150,7 @@ paintRiver({
"landHeight": 1,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
if (height < 0)
addToClass(ix, iz, clWater);

View File

@ -63,19 +63,24 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
var clShallow = createTileClass();
var waterHeight = -3;
var shallowHeight = -1;
initTerrain(tMainTerrain);
log("Creating central lake...");
var centralLake = [0.5, 0.5];
createArea(
new ClumpPlacer(
mapArea / 100 * Math.pow(scaleByMapSize(1, 6), 1/8),
0.7,
0.1,
10,
Math.round(fractionToTiles(0.5)),
Math.round(fractionToTiles(0.5))),
fractionToTiles(centralLake[0]),
fractionToTiles(centralLake[1])),
[
new LayeredPainter([tShore, tWater, tWater, tWater], [1, 4, 2]),
new SmoothElevationPainter(ELEVATION_SET, -3, 4),
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
paintClass(clWater)
],
null);
@ -92,10 +97,10 @@ for (var i = 0; i < numPlayers; i++)
var elevation = 20;
// get the x and z in tiles
fx = fractionToTiles(playerX[i]);
fz = fractionToTiles(playerZ[i]);
ix = round(fx);
iz = round(fz);
var fx = fractionToTiles(playerX[i]);
var fz = fractionToTiles(playerZ[i]);
var ix = round(fx);
var iz = round(fz);
addCivicCenterAreaToClass(ix, iz, clPlayer);
// create the city patch
@ -157,142 +162,53 @@ for (var i = 0; i < numPlayers; i++)
placeDefaultDecoratives(fx, fz, aGrassShort, clBaseResource, radius);
}
RMS.SetProgress(20);
//init rivers
var PX = [];
var PZ = [];
//isRiver actually tells us if two points must be joined by river
var isRiver = [];
for (let m = 0; m < numPlayers + 1; ++m)
log("Creating rivers between opponents...");
var [riverX, riverZ] = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, 0.5, ...centralLake);
for (let i = 0; i < numPlayers; ++i)
{
isRiver[m] = [];
for (let n = 0; n < numPlayers + 1; ++n)
isRiver[m][n] = 0;
if (areAllies(playerIDs[i] - 1, playerIDs[(i + 1) % numPlayers] - 1))
continue;
let shallowLocation = randFloat(0.2, 0.7);
let shallowWidth = randFloat(0.12, 0.21);
paintRiver({
"parallel": true,
"startX": riverX[i],
"startZ": riverZ[i],
"endX": centralLake[0],
"endZ": centralLake[1],
"width": tilesToFraction(scaleByMapSize(10, 30)),
"fadeDist": tilesToFraction(5),
"deviation": 0,
"landHeight": getMapBaseHeight(),
"waterHeight": waterHeight,
"minHeight": waterHeight,
"meanderShort": tilesToFraction(scaleByMapSize(20, 60) * scaleByMapSize(35, 160)),
"meanderLong": 0,
"waterFunc": (ix, iz, height, riverFraction) => {
addToClass(ix, iz, clWater);
let isShallow = height < shallowHeight &&
riverFraction > shallowLocation &&
riverFraction < shallowLocation + shallowWidth;
let newHeight = isShallow ? shallowHeight : Math.max(height, waterHeight);
if (getHeight(ix, iz) < newHeight)
return;
setHeight(ix, iz, newHeight);
placeTerrain(ix, iz, height >= 0 ? tShore : tWater);
if (isShallow)
addToClass(ix, iz, clShallow);
}
});
}
//creating the first point in the center. all others are
//connected to this one so all of our rivers join together
//in the middle of the map
var fx = fractionToTiles(0.5);
var fz = fractionToTiles(0.5);
var ix = round(fx);
var iz = round(fz);
PX[numPlayers]= fx;
PZ[numPlayers]= fz;
var riverAngle = [];
for (var c = 0 ; c < numPlayers ; c++)
{
//creating other points of the river and making them
// join the point in the center of the map
riverAngle[c] = startAngle + (((2 * c + 1) / (numPlayers * 2)) * TWO_PI );
PX[c] = round(fractionToTiles(0.5 + 0.5 * cos(riverAngle[c])));
PZ[c] = round(fractionToTiles(0.5 + 0.5 * sin(riverAngle[c])));
//log (playerIDs[c], ",,," ,playerIDs[0]);
//isRiver[c][numPlayers]=1;
if ((c == numPlayers-1)&&(!areAllies(playerIDs[c]-1, playerIDs[0]-1)))
isRiver[c][numPlayers]=1;
else if ((c < numPlayers-1)&&(!areAllies(playerIDs[c]-1, playerIDs[c+1]-1)))
isRiver[c][numPlayers]=1;
}
//theta is the start value for rndRiver function. seed implies
//the randomness. we must have one of these for each river we create.
//shallowpoint and shallow length define the place and size of the shallow part
var theta = [];
var seed = [];
var shallowpoint = [];
var shallowlength = [];
for (let q = 0; q < numPlayers + 1; ++q)
{
theta[q]=randFloat(0, 1);
seed[q]=randFloat(2,3);
shallowpoint[q]=randFloat(0.2,0.7);
shallowlength[q]=randFloat(0.12,0.21);
}
log ("Creating rivers...");
//checking all the tiles
for (var ix = 0; ix < mapSize; ix++)
for (var iz = 0; iz < mapSize; iz++)
for (var m = 0; m < numPlayers+1; m++)
for (var n = 0; n < numPlayers+1; n++)
{
//checking if there is a river between those points
if(isRiver[m][n] == 1)
{
//very important calculations. don't change anything. results
//"dis" which is the distance to the riverline and "y" and "xm" which are
//the coordinations for the point it's image is in.
var a = PZ[m]-PZ[n];
var b = PX[n]-PX[m];
var c = (PZ[m]*(PX[m]-PX[n]))-(PX[m]*(PZ[m]-PZ[n]));
var dis = abs(a*ix + b*iz + c)/sqrt(a*a + b*b);
if (abs(a*ix + b*iz + c) != 0)
var alamat = (a*ix + b*iz + c)/abs(a*ix + b*iz + c);
else
var alamat = 1;
var k = (a*ix + b*iz + c)/(a*a + b*b);
var y = iz-(b*k);
var xm = ix-(a*k);
//this calculates which "part" of the river are we in now.
//used for the function rndRiver.
var sit = sqrt((PZ[n]-y)*(PZ[n]-y)+(PX[n]-xm)*(PX[n]-xm))/sqrt((PZ[n]-PZ[m])*(PZ[n]-PZ[m])+(PX[n]-PX[m])*(PX[n]-PX[m]));
var sbms = scaleByMapSize(5,15) + alamat * ( scaleByMapSize(20, 60) * rndRiver( theta[m] + sit * 0.5 * (mapSize/64) , seed[m]) );
if((dis < sbms)&&(y <= Math.max(PZ[m],PZ[n]))&&(y >= Math.min(PZ[m],PZ[n])))
{
//create the deep part of the river
if (dis <= sbms-5){
if ((sit > shallowpoint[m])&&(sit < shallowpoint[m]+shallowlength[m]))
{
//create the shallow part
var h = -1;
addToClass(ix, iz, clShallow);
}
else
var h = -3;
var t = tWater;
addToClass(ix, iz, clWater);
}
//creating the rough edges
else if (dis <= sbms)
{
if ((sit > shallowpoint[m])&&(sit < shallowpoint[m]+shallowlength[m]))
{
if (2-(sbms-dis)<-1)
{
//checking if there is shallow water here
var h = -1;
addToClass(ix, iz, clShallow);
}
else
var h = 2-(sbms-dis);
}
else
var h = 2-(sbms-dis);
//we must create shore lines for more beautiful terrain
if (sbms-dis<=2)
var t = tShore;
else
var t = tWater;
addToClass(ix, iz, clWater);
}
//we don't want to cause problems when river joins sea
if (getHeight(ix, iz)>h)
{
placeTerrain(ix, iz, t);
setHeight(ix, iz, h);
}
}
}
}
RMS.SetProgress(40);
createBumps(avoidClasses(clWater, 2, clPlayer, 20));

View File

@ -317,34 +317,61 @@ function createLayeredPatches(sizes, terrains, terrainWidths, constraint, count,
/**
* Creates a meandering river at the given location and width.
* Optionally calls a function on the affected tiles.
* Horizontal locations and widths (including fadeDist, meandering) are fractions of the mapsize.
*
* @property horizontal - Whether the river is horizontal or vertical
* @property parallel - Whether the shorelines should be parallel or meander separately.
* @property position - Location of the river. Number between 0 and 1.
* @property width - Size between the two shorelines. Number between 0 and 1.
* @property position - Location of the river.
* @property width - Size between the two shorelines.
* @property fadeDist - Size of the shoreline.
* @property deviation - Fuzz effect on the shoreline if greater than 0.
* @property waterHeight - Ground height of the riverbed.
* @proeprty landHeight - Ground height of the end of the shoreline.
* @property meanderShort - Strength of frequent meanders.
* @property meanderLong - Strength of less frequent meanders.
* @property waterFunc - Optional function called on water tiles, providing ix, iz, height.
* @property landFunc - Optional function called on land tiles, providing ix, iz, shoreDist1, shoreDist2.
* @property [constraint] - If given, ignores any tiles that don't satisfy the given Constraint.
* @property [waterFunc] - Optional function called on tiles within the river.
* Provides location on the tilegrid, new elevation and
* the location on the axis parallel to the river as a fraction of the river length.
* @property [landFunc] - Optional function called on land tiles, providing ix, iz, shoreDist1, shoreDist2.
* @property [minHeight] - If given, only changes the elevation below this height while still calling the given functions.
*/
function paintRiver(args)
{
log("Creating the river");
let theta1 = randFloat(0, 1);
let theta2 = randFloat(0, 1);
// Model the river meandering as the sum of two sine curves.
let meanderShort = fractionToTiles(args.meanderShort / scaleByMapSize(35, 160));
let meanderLong = fractionToTiles(args.meanderLong / scaleByMapSize(35, 100));
// Unless the river is parallel, each riverside will receive an own random seed and starting angle.
let seed1 = randFloat(2, 3);
let seed2 = randFloat(2, 3);
let meanderShort = args.meanderShort / scaleByMapSize(35, 160);
let meanderLong = args.meanderLong / scaleByMapSize(35, 100);
let startingAngle1 = randFloat(0, 1);
let startingAngle2 = randFloat(0, 1);
// Computes the deflection of the river at a given point.
let riverCurve = (riverFraction, startAngle, seed) =>
meanderShort * rndRiver(startAngle + fractionToTiles(riverFraction) / 128, seed) +
meanderLong * rndRiver(startAngle + fractionToTiles(riverFraction) / 256, seed);
// Describe river width and length of the shoreline.
let halfWidth = fractionToTiles(args.width / 2);
let fadeDist = fractionToTiles(args.fadeDist);
// Describe river location in vectors.
let mapSize = getMapSize();
let vecStart = new Vector2D(args.startX, args.startZ).mult(mapSize);
let vecEnd = new Vector2D(args.endX, args.endZ).mult(mapSize);
let vecRiver = Vector2D.sub(vecStart, vecEnd);
let riverLength = vecRiver.length();
// Describe river boundaries.
let riverMinX = Math.min(vecStart.x, vecEnd.x);
let riverMinZ = Math.min(vecStart.y, vecEnd.y);
let riverMaxX = Math.max(vecStart.x, vecEnd.x);
let riverMaxZ = Math.max(vecStart.y, vecEnd.y);
for (let ix = 0; ix < mapSize; ++ix)
for (let iz = 0; iz < mapSize; ++iz)
@ -352,41 +379,49 @@ function paintRiver(args)
if (args.constraint && !args.constraint.allows(ix, iz))
continue;
let x = ix / (mapSize + 1);
let z = iz / (mapSize + 1);
let vecPoint = new Vector2D(ix, iz);
let coord1 = args.horizontal ? z : x;
let coord2 = args.horizontal ? x : z;
// Compute the shortest distance to the river.
let distanceToRiver = vecRiver.cross(Vector2D.sub(vecPoint, vecEnd)) / riverLength;
// River curve at this place
let curve1 =
meanderShort * rndRiver(theta1 + coord2 * mapSize / 128, seed1) +
meanderLong * rndRiver(theta2 + coord2 * mapSize / 256, seed2);
// Closest point on the river (i.e the foot of the perpendicular).
let river = Vector2D.sub(vecPoint, vecRiver.perpendicular().mult(distanceToRiver / riverLength));
let curve2 = args.parallel ? curve1 :
meanderShort * rndRiver(theta2 + coord2 * mapSize / 128, seed2) +
meanderLong * rndRiver(theta2 + coord2 * mapSize / 256, seed2);
// Only process points that actually are perpendicular with the river.
if (river.x < riverMinX || river.x > riverMaxX ||
river.y < riverMinZ || river.y > riverMaxZ)
continue;
// Fuzz the river border
let devCoord1 = coord1 * randFloat(1 - args.deviation, 1 + args.deviation);
let devCoord2 = coord2 * randFloat(1 - args.deviation, 1 + args.deviation);
// Coordinate between 0 and 1 on the axis parallel to the river.
let riverFraction = river.distanceTo(vecStart) / riverLength;
let shoreDist1 = -devCoord1 + curve1 + args.position - args.width / 2;
let shoreDist2 = -devCoord1 + curve2 + args.position + args.width / 2;
// Amplitude of the river at this location.
let riverCurve1 = riverCurve(riverFraction, startingAngle1, seed1);
let riverCurve2 = args.parallel ? riverCurve1 : riverCurve(riverFraction, startingAngle2, seed2);
// Add noise.
let deviation = fractionToTiles(args.deviation) * randFloat(-1, 1);
// Compute the distance to the shoreline.
let sign = Math.sign(distanceToRiver || 1);
let shoreDist1 = sign * riverCurve1 + Math.abs(distanceToRiver) - deviation - halfWidth;
let shoreDist2 = sign * riverCurve2 + Math.abs(distanceToRiver) - deviation + halfWidth;
// Create the elevation for the water and the slopy shoreline and call the user functions.
if (shoreDist1 < 0 && shoreDist2 > 0)
{
let height = args.waterHeight;
if (shoreDist1 > -args.fadeDist)
height += (args.landHeight - args.waterHeight) * (1 + shoreDist1 / args.fadeDist);
else if (shoreDist2 < args.fadeDist)
height += (args.landHeight - args.waterHeight) * (1 - shoreDist2 / args.fadeDist);
if (shoreDist1 > -fadeDist)
height += (args.landHeight - args.waterHeight) * (1 + shoreDist1 / fadeDist);
else if (shoreDist2 < fadeDist)
height += (args.landHeight - args.waterHeight) * (1 - shoreDist2 / fadeDist);
setHeight(ix, iz, height);
if (args.minHeight === undefined || height < args.minHeight)
setHeight(ix, iz, height);
if (args.waterFunc)
args.waterFunc(ix, iz, height);
args.waterFunc(ix, iz, height, riverFraction);
}
else if (args.landFunc)
args.landFunc(ix, iz, shoreDist1, shoreDist2);

View File

@ -168,21 +168,22 @@ const riverTextures = [
];
const plantFrequency = 2;
var plantID = 0;
paintRiver({
"horizontal": false,
"parallel": true,
"position": 0.5,
"startX": 0.5,
"startZ": 0,
"endX": 0.5,
"endZ": 1,
"width": 0.1,
"fadeDist": 0.025,
"deviation": 0.005,
"deviation": 0.0025,
"waterHeight": -3,
"landHeight": 2,
"meanderShort": 12,
"meanderLong": 50,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
addToClass(ix, iz, clWater);
placeTerrain(ix, iz, tShore);

View File

@ -289,13 +289,15 @@ function unknownContinent()
function unknownCentralSea()
{
let waterHeight = -3;
let horizontal = randBool();
let [start, end] = centralRiverCoordinates(horizontal);
paintRiver({
"horizontal": horizontal,
"parallel": false,
"position": 0.5,
"startX": tilesToFraction(start[0]),
"startZ": tilesToFraction(start[1]),
"endX": tilesToFraction(end[0]),
"endZ": tilesToFraction(end[1]),
"width": randFloat(0.22, 0.3) + scaleByMapSize(0.05, 0.2),
"fadeDist": 0.025,
"deviation": 0,
@ -303,7 +305,7 @@ function unknownCentralSea()
"landHeight": landHeight,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (ix, iz, height) => {
"waterFunc": (ix, iz, height, riverFraction) => {
if (height < 0)
addToClass(ix, iz, clWater);
},
@ -531,10 +533,20 @@ function unknownEdgeSeas()
}
for (let location of pickRandom([["first"], ["second"], ["first", "second"]]))
{
let margin = randFloat(0, scaleByMapSize(0, 0.1));
let positionX = location == "first" ? [0, margin] : [1 - margin, 1];
let positionZ = [0, 1];
if (!horizontal)
[positionX, positionZ] = [positionZ, positionX];
paintRiver({
"horizontal": horizontal,
"parallel": false,
"position": (location == "first" ? 0 : 1) + (location == "first" ? -1 : 1) * randFloat(0, scaleByMapSize(0, 0.1)),
"startX": positionX[0],
"startZ": positionZ[0],
"endX": positionX[1],
"endX": positionZ[1],
"width": 0.62,
"fadeDist": 0.015,
"deviation": 0,
@ -543,6 +555,7 @@ function unknownEdgeSeas()
"meanderShort": 20,
"meanderLong": 0
});
}
createExtensionsOrIslands();
paintTileClassBasedOnHeight(0, cliffHeight, 1, clLand);