Engine.LoadLibrary("rmgen");
Engine.LoadLibrary("rmgen-common");
TILE_CENTERED_HEIGHT_MAP = true;
const tGrassSpecific = ["new_alpine_grass_d","new_alpine_grass_d", "new_alpine_grass_e"];
const tGrass = ["new_alpine_grass_d", "new_alpine_grass_b", "new_alpine_grass_e"];
const tGrassMidRange = ["new_alpine_grass_b", "alpine_grass_a"];
const tGrassHighRange = ["new_alpine_grass_a", "alpine_grass_a", "alpine_grass_rocky"];
const tHighRocks = ["alpine_cliff_b", "alpine_cliff_c","alpine_cliff_c", "alpine_grass_rocky"];
const tSnowedRocks = ["alpine_cliff_b", "alpine_cliff_snow"];
const tTopSnow = ["alpine_snow_rocky","alpine_snow_a"];
const tTopSnowOnly = ["alpine_snow_a"];
const tDirtyGrass = ["new_alpine_grass_d","alpine_grass_d","alpine_grass_c", "alpine_grass_b"];
const tLushGrass = ["new_alpine_grass_a","new_alpine_grass_d"];
const tMidRangeCliffs = ["alpine_cliff_b","alpine_cliff_c"];
const tHighRangeCliffs = ["alpine_mountainside","alpine_cliff_snow" ];
const tSand = ["beach_c", "beach_d"];
const tSandTransition = ["beach_scrub_50_"];
const tWater = ["sand_wet_a","sand_wet_b","sand_wet_b","sand_wet_b"];
const tGrassLandForest = "alpine_forrestfloor";
const tGrassLandForest2 = "alpine_grass_d";
const tForestTransition = ["new_alpine_grass_d", "new_alpine_grass_b","alpine_grass_d"];
const tRoad = "new_alpine_citytile";
const tRoadWild = "new_alpine_citytile";
const oBeech = "gaia/tree/euro_beech";
const oPine = "gaia/tree/aleppo_pine";
const oBerryBush = "gaia/fruit/berry_01";
const oDeer = "gaia/fauna_deer";
const oFish = "gaia/fish/generic";
const oRabbit = "gaia/fauna_rabbit";
const oStoneLarge = "gaia/rock/alpine_large";
const oStoneSmall = "gaia/rock/alpine_small";
const oMetalLarge = "gaia/ore/alpine_large";
const oMetalSmall = "gaia/ore/alpine_small";
const aGrass = "actor|props/flora/grass_soft_small_tall.xml";
const aGrassShort = "actor|props/flora/grass_soft_large.xml";
const aRockLarge = "actor|geology/stone_granite_med.xml";
const aRockMedium = "actor|geology/stone_granite_med.xml";
const aBushMedium = "actor|props/flora/bush_medit_me.xml";
const aBushSmall = "actor|props/flora/bush_medit_sm.xml";
const pForestLand = [tGrassLandForest + TERRAIN_SEPARATOR + oPine,tGrassLandForest + TERRAIN_SEPARATOR + oBeech,
tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech,
tGrassLandForest,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2];
const pForestLandLight = [tGrassLandForest + TERRAIN_SEPARATOR + oPine,tGrassLandForest + TERRAIN_SEPARATOR + oBeech,
tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech,
tGrassLandForest,tGrassLandForest2,tForestTransition,tGrassLandForest2,
tGrassLandForest,tForestTransition,tGrassLandForest2,tForestTransition,
tGrassLandForest2,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2];
const pForestLandVeryLight = [ tGrassLandForest2 + TERRAIN_SEPARATOR + oPine,tGrassLandForest2 + TERRAIN_SEPARATOR + oBeech,
tForestTransition,tGrassLandForest2,tForestTransition,tForestTransition,tForestTransition,
tGrassLandForest,tForestTransition,tGrassLandForest2,tForestTransition,
tGrassLandForest2,tGrassLandForest2,tGrassLandForest2,tGrassLandForest2];
const heightInit = -100;
const heightOcean = -22;
const heightWaterTerrain = -14;
const heightBase = -6;
const heightSand = -2;
const heightSandTransition = 0;
const heightGrass = 6;
const heightWaterLevel = 8;
const heightPyreneans = 15;
const heightGrassMidRange = 18;
const heightGrassHighRange = 30;
const heightPassage = scaleByMapSize(25, 40);
const heightHighRocks = heightPassage + 5;
const heightSnowedRocks = heightHighRocks + 10;
const heightMountain = heightHighRocks + 20;
const heightOffsetHill = 7;
const heightOffsetHillRandom = 2;
var g_Map = new RandomMap(heightInit, tGrass);
const numPlayers = getNumPlayers();
const mapSize = g_Map.getSize();
const mapCenter = g_Map.getCenter();
var clDirt = g_Map.createTileClass();
var clRock = g_Map.createTileClass();
var clMetal = g_Map.createTileClass();
var clFood = g_Map.createTileClass();
var clBaseResource = g_Map.createTileClass();
var clPass = g_Map.createTileClass();
var clPyrenneans = g_Map.createTileClass();
var clPlayer = g_Map.createTileClass();
var clHill = g_Map.createTileClass();
var clForest = g_Map.createTileClass();
var clWater = g_Map.createTileClass();
var startAngle = randomAngle();
var oceanAngle = startAngle + randFloat(-1, 1) * Math.PI / 12;
var mountainLength = fractionToTiles(0.68);
var mountainWidth = scaleByMapSize(15, 55);
var mountainPeaks = 100 * scaleByMapSize(1, 10);
var mountainOffset = randFloat(-1, 1) * scaleByMapSize(1, 12);
var passageLength = scaleByMapSize(8, 50);
var terrainPerHeight = [
{
"maxHeight": heightGrass,
"steepness": 5,
"terrainGround": tGrass,
"terrainSteep": tMidRangeCliffs
},
{
"maxHeight": heightGrassMidRange,
"steepness": 8,
"terrainGround": tGrassMidRange,
"terrainSteep": tMidRangeCliffs
},
{
"maxHeight": heightGrassHighRange,
"steepness": 8,
"terrainGround": tGrassHighRange,
"terrainSteep": tMidRangeCliffs
},
{
"maxHeight": heightHighRocks,
"steepness": 8,
"terrainGround": tHighRocks,
"terrainSteep": tHighRangeCliffs
},
{
"maxHeight": heightSnowedRocks,
"steepness": 7,
"terrainGround": tSnowedRocks,
"terrainSteep": tHighRangeCliffs
},
{
"maxHeight": Infinity,
"steepness": 6,
"terrainGround": tTopSnowOnly,
"terrainSteep": tTopSnow
}
];
g_Map.log("Creating initial sinusoidal noise");
var baseHeights = [];
for (var ix = 0; ix < mapSize; ix++)
{
baseHeights.push([]);
for (var iz = 0; iz < mapSize; iz++)
{
let position = new Vector2D(ix, iz);
if (g_Map.inMapBounds(position))
{
let height = heightBase + randFloat(-1, 1) + scaleByMapSize(1, 3) * (Math.cos(ix / scaleByMapSize(5, 30)) + Math.sin(iz / scaleByMapSize(5, 30)));
g_Map.setHeight(position, height);
baseHeights[ix].push(height);
}
else
baseHeights[ix].push(heightInit);
}
}
var playerIDs = sortAllPlayers();
var playerPosition = playerPlacementArcs(
playerIDs,
mapCenter,
fractionToTiles(0.35),
oceanAngle,
0.1 * Math.PI,
0.9 * Math.PI);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
"outerTerrain": tRoadWild,
"innerTerrain": tRoad
},
"StartingAnimal": {
},
"Berries": {
"template": oBerryBush
},
"Mines": {
"types": [
{ "template": oMetalLarge },
{ "template": oStoneLarge }
]
},
"Trees": {
"template": oPine
},
"Decoratives": {
"template": aGrassShort
}
});
Engine.SetProgress(30);
g_Map.log("Creating the pyreneans");
var mountainVec = new Vector2D(mountainLength, 0).rotate(-startAngle);
var mountainStart = Vector2D.sub(mapCenter, Vector2D.div(mountainVec, 2));
var mountainDirection = mountainVec.clone().normalize();
createPyreneans();
paintTileClassBasedOnHeight(heightPyreneans, Infinity, Elevation_ExcludeMin_ExcludeMax, clPyrenneans);
Engine.SetProgress(40);
/**
* Generates the mountain peak noise.
*
* @param {number} x - between 0 and 1
* @returns {number} between 0 and 1
*/
function sigmoid(x, peakPosition)
{
return 1 / (1 + Math.exp(x)) *
// If we're too far from the border, we flatten
(0.2 - Math.max(0, Math.abs(0.5 - peakPosition) - 0.3)) * 5;
}
function createPyreneans()
{
for (let peak = 0; peak < mountainPeaks; ++peak)
{
let peakPosition = peak / mountainPeaks;
let peakHeight = randFloat(0, 10);
for (let distance = 0; distance < mountainWidth; distance += 1/3)
{
let rest = 2 * (1 - distance / mountainWidth);
let sigmoidX =
- 1 * (rest - 1.9) +
- 4 *
(rest - randFloat(0.9, 1.1)) *
(rest - randFloat(0.9, 1.1)) *
(rest - randFloat(0.9, 1.1));
for (let direction of [-1, 1])
{
let pos = Vector2D.sum([
Vector2D.add(mountainStart, Vector2D.mult(mountainDirection, peakPosition * mountainLength)),
new Vector2D(mountainOffset, 0).rotate(-peakPosition * Math.PI * 4),
new Vector2D(distance, 0).rotate(-startAngle - direction * Math.PI / 2)
]).round();
g_Map.setHeight(pos, baseHeights[pos.x][pos.y] + (heightMountain + peakHeight + randFloat(-9, 9)) * sigmoid(sigmoidX, peakPosition));
}
}
}
}
g_Map.log("Creating passages");
var passageLocation = 0.35;
var passageVec = mountainDirection.perpendicular().mult(passageLength);
for (let passLoc of [passageLocation, 1 - passageLocation])
for (let direction of [1, -1])
{
let passageStart = Vector2D.add(mountainStart, Vector2D.mult(mountainVec, passLoc));
let passageEnd = Vector2D.add(passageStart, Vector2D.mult(passageVec, direction));
createPassage({
"start": passageStart,
"end": passageEnd,
"startHeight": heightPassage,
"startWidth": 7,
"endWidth": 7,
"smoothWidth": 2,
"tileClass": clPass
});
}
Engine.SetProgress(50);
g_Map.log("Smoothing the pyreneans");
createArea(
new MapBoundsPlacer(),
new SmoothingPainter(1, 0.3, 1),
new NearTileClassConstraint(clPyrenneans, 1));
g_Map.log("Creating oceans");
for (let ocean of distributePointsOnCircle(2, oceanAngle, fractionToTiles(0.48), mapCenter)[0])
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.9, 0.05, Infinity, ocean),
[
new ElevationPainter(heightOcean),
new TileClassPainter(clWater)
]);
g_Map.log("Smoothing around the water");
createArea(
new MapBoundsPlacer(),
new SmoothingPainter(5, 0.9, 1),
new NearTileClassConstraint(clWater, 5));
Engine.SetProgress(55);
g_Map.log("Creating hills");
createAreas(
new ClumpPlacer(scaleByMapSize(60, 120), 0.3, 0.06, Infinity),
[
new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetHill, 4, heightOffsetHillRandom),
new TerrainPainter(tGrassSpecific),
new TileClassPainter(clHill)
],
avoidClasses(clWater, 5, clPlayer, 20, clBaseResource, 6, clPyrenneans, 2), scaleByMapSize(5, 35));
g_Map.log("Creating forests");
var types = [[tForestTransition, pForestLandVeryLight, pForestLandLight, pForestLand]];
var size = scaleByMapSize(40, 115) * Math.PI;
var num = Math.floor(scaleByMapSize(8,40) / types.length);
for (let type of types)
createAreas(
new ClumpPlacer(size, 0.2, 0.1, Infinity),
[
new LayeredPainter(type, [scaleByMapSize(1, 2), scaleByMapSize(3, 6), scaleByMapSize(3, 6)]),
new TileClassPainter(clForest)
],
avoidClasses(clPlayer, 20, clPyrenneans,0, clForest, 7, clWater, 2),
num);
Engine.SetProgress(60);
g_Map.log("Creating lone trees");
var num = scaleByMapSize(80,400);
var group = new SimpleGroup([new SimpleObject(oPine, 1,2, 1,3),new SimpleObject(oBeech, 1,2, 1,3)], true, clForest);
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 1, clPlayer, 8,clPyrenneans, 1), num, 20 );
g_Map.log("Painting terrain by height and slope");
for (let i = 0; i < terrainPerHeight.length; ++i)
for (let steep of [false, true])
createArea(
new MapBoundsPlacer(),
new TerrainPainter(steep ? terrainPerHeight[i].terrainSteep : terrainPerHeight[i].terrainGround),
[
new NearTileClassConstraint(clPyrenneans, 2),
new HeightConstraint(terrainPerHeight[i - 1] ? terrainPerHeight[i - 1].maxHeight : -Infinity, terrainPerHeight[i].maxHeight),
steep ?
new SlopeConstraint(terrainPerHeight[i].steepness, Infinity) :
new SlopeConstraint(-Infinity, terrainPerHeight[i].steepness),
]);
for (let x = 0; x < mapSize; ++x)
for (let z = 0; z < mapSize; ++z)
{
let position = new Vector2D(x, z);
let height = g_Map.getHeight(position);
let heightDiff = g_Map.getSlope(position);
let terrainShore = getShoreTerrain(position, height, heightDiff);
if (terrainShore)
createTerrain(terrainShore).place(position);
}
function getShoreTerrain(position, height, heightDiff)
{
if (height <= heightWaterTerrain)
return tWater;
if (height <= heightSand && new NearTileClassConstraint(clWater, 2).allows(position))
return heightDiff < 2.5 ? tSand : tMidRangeCliffs;
// Notice the sand transition is also be painted below height -2
if (height <= heightSandTransition && new NearTileClassConstraint(clWater, 3).allows(position))
return heightDiff < 2.5 ? tSandTransition : tMidRangeCliffs;
return undefined;
}
g_Map.log("Creating dirt patches");
for (let size of [scaleByMapSize(3, 20), scaleByMapSize(5, 40), scaleByMapSize(8, 60)])
createAreas(
new ClumpPlacer(size, 0.3, 0.06, 0.5),
[
new TerrainPainter(tDirtyGrass),
new TileClassPainter(clDirt)
],
avoidClasses(clWater, 3, clForest, 0, clPyrenneans,5, clHill, 0, clDirt, 5, clPlayer, 6),
scaleByMapSize(15, 45));
g_Map.log("Creating grass patches");
for (let size of [scaleByMapSize(2, 32), scaleByMapSize(3, 48), scaleByMapSize(5, 80)])
createAreas(
new ClumpPlacer(size, 0.3, 0.06, 0.5),
new TerrainPainter(tLushGrass),
avoidClasses(clWater, 3, clForest, 0, clPyrenneans,5, clHill, 0, clDirt, 5, clPlayer, 6),
scaleByMapSize(15, 45));
Engine.SetProgress(70);
// making more in dirt areas so as to appear different
g_Map.log("Creating small grass tufts");
var group = new SimpleGroup( [new SimpleObject(aGrassShort, 1,2, 0,1, -Math.PI / 8, Math.PI / 8)] );
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clHill, 2, clPlayer, 5, clDirt, 0, clPyrenneans,2), scaleByMapSize(13, 200) );
createObjectGroupsDeprecated(group, 0, stayClasses(clDirt,1), scaleByMapSize(13, 200),10);
g_Map.log("Creating large grass tufts");
group = new SimpleGroup( [new SimpleObject(aGrass, 2,4, 0,1.8, -Math.PI / 8, Math.PI / 8), new SimpleObject(aGrassShort, 3,6, 1.2,2.5, -Math.PI / 8, Math.PI / 8)] );
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clHill, 2, clPlayer, 5, clDirt, 1, clForest, 0, clPyrenneans,2), scaleByMapSize(13, 200) );
createObjectGroupsDeprecated(group, 0, stayClasses(clDirt,1), scaleByMapSize(13, 200),10);
Engine.SetProgress(75);
g_Map.log("Creating bushes");
group = new SimpleGroup( [new SimpleObject(aBushMedium, 1,2, 0,2), new SimpleObject(aBushSmall, 2,4, 0,2)] );
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 2, clPlayer, 1, clPyrenneans, 1), scaleByMapSize(13, 200), 50 );
Engine.SetProgress(80);
g_Map.log("Creating metal mines");
createBalancedMetalMines(
oMetalSmall,
oMetalLarge,
clMetal,
avoidClasses(clWater, 2, clForest, 0, clPlayer, scaleByMapSize(15, 25), clPyrenneans, 3)
);
g_Map.log("Creating stone mines");
createBalancedStoneMines(
oStoneSmall,
oStoneLarge,
clRock,
avoidClasses(clWater, 2, clForest, 0, clPlayer, scaleByMapSize(15, 25), clPyrenneans, 3, clMetal, 10)
);
Engine.SetProgress(85);
g_Map.log("Creating small decorative rocks");
group = new SimpleGroup( [new SimpleObject(aRockMedium, 1,3, 0,1)], true );
createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0), scaleByMapSize(16, 262), 50 );
g_Map.log("Creating large decorative rocks");
group = new SimpleGroup( [new SimpleObject(aRockLarge, 1,2, 0,1), new SimpleObject(aRockMedium, 1,3, 0,2)], true );
createObjectGroupsDeprecated( group, 0, avoidClasses(clWater, 0, clForest, 0, clPlayer, 0), scaleByMapSize(8, 131), 50 );
Engine.SetProgress(90);
g_Map.log("Creating deer");
group = new SimpleGroup( [new SimpleObject(oDeer, 5,7, 0,4)], true, clFood );
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clPyrenneans, 1, clFood, 15), 3 * numPlayers, 50 );
g_Map.log("Creating rabbit");
group = new SimpleGroup( [new SimpleObject(oRabbit, 2,3, 0,2)], true, clFood );
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clPyrenneans, 1, clFood,15), 3 * numPlayers, 50 );
g_Map.log("Creating berry bush");
group = new SimpleGroup( [new SimpleObject(oBerryBush, 5,7, 0,4)],true, clFood );
createObjectGroupsDeprecated(group, 0, avoidClasses(clWater, 3, clForest, 0, clPlayer, 20, clPyrenneans, 1, clFood, 10), randIntInclusive(1, 4) * numPlayers + 2, 50);
g_Map.log("Creating fish");
group = new SimpleGroup( [new SimpleObject(oFish, 2,3, 0,2)], true, clFood );
createObjectGroupsDeprecated(group, 0, [avoidClasses(clFood, 15), stayClasses(clWater, 6)], 20 * numPlayers, 60 );
placePlayersNomad(clPlayer, avoidClasses(clWater, 4, clPyrenneans, 4, clForest, 1, clMetal, 4, clRock, 4, clFood, 2));
setSunElevation(Math.PI * randFloat(1/5, 1/3));
setSunRotation(randomAngle());
setSkySet("cumulus");
setSunColor(0.73,0.73,0.65);
setAmbientColor(0.45,0.45,0.50);
setWaterColor(0.263, 0.353, 0.616);
setWaterTint(0.104, 0.172, 0.563);
setWaterWaviness(5.0);
setWaterType("ocean");
setWaterMurkiness(0.83);
setWaterHeight(heightWaterLevel);
g_Map.ExportMap();