Engine.LoadLibrary("rmgen");
Engine.LoadLibrary("rmgen-common");
Engine.LoadLibrary("rmbiome");
TILE_CENTERED_HEIGHT_MAP = true;
setSelectedBiome();
const tMainTerrain = g_Terrains.mainTerrain;
const tForestFloor1 = g_Terrains.forestFloor1;
const tForestFloor2 = g_Terrains.forestFloor2;
const tCliff = g_Terrains.cliff;
const tTier1Terrain = g_Terrains.tier1Terrain;
const tTier2Terrain = g_Terrains.tier2Terrain;
const tTier3Terrain = g_Terrains.tier3Terrain;
const tHill = g_Terrains.hill;
const tRoad = g_Terrains.road;
const tRoadWild = g_Terrains.roadWild;
const tTier4Terrain = g_Terrains.tier4Terrain;
const tShore = g_Terrains.shore;
const tWater = g_Terrains.water;
const oTree1 = g_Gaia.tree1;
const oTree2 = g_Gaia.tree2;
const oTree4 = g_Gaia.tree4;
const oTree5 = g_Gaia.tree5;
const oFruitBush = g_Gaia.fruitBush;
const oMainHuntableAnimal = g_Gaia.mainHuntableAnimal;
const oSecondaryHuntableAnimal = g_Gaia.secondaryHuntableAnimal;
const oFish = g_Gaia.fish;
const oStoneLarge = g_Gaia.stoneLarge;
const oStoneSmall = g_Gaia.stoneSmall;
const oMetalLarge = g_Gaia.metalLarge;
const oWoodTreasure = "gaia/treasure/wood";
const aGrass = g_Decoratives.grass;
const aGrassShort = g_Decoratives.grassShort;
const aReeds = g_Decoratives.reeds;
const aLillies = g_Decoratives.lillies;
const aRockLarge = g_Decoratives.rockLarge;
const aRockMedium = g_Decoratives.rockMedium;
const aBushMedium = g_Decoratives.bushMedium;
const aBushSmall = g_Decoratives.bushSmall;
const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2];
const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1];
const heightSeaGround = -5;
const heightLand = 3;
const heightCliff = 3.12;
const heightHill = 18;
const heightOffsetBump = 2;
var g_Map = new RandomMap(heightSeaGround, tWater);
const numPlayers = getNumPlayers();
const mapSize = g_Map.getSize();
const mapCenter = g_Map.getCenter();
const mapBounds = g_Map.getBounds();
var clPlayer = g_Map.createTileClass();
var clPlayerTerritory = g_Map.createTileClass();
var clHill = g_Map.createTileClass();
var clForest = g_Map.createTileClass();
var clWater = g_Map.createTileClass();
var clDirt = g_Map.createTileClass();
var clRock = g_Map.createTileClass();
var clMetal = g_Map.createTileClass();
var clFood = g_Map.createTileClass();
var clPeninsulaSteam = g_Map.createTileClass();
var clBaseResource = g_Map.createTileClass();
var clLand = g_Map.createTileClass();
var clShallow = g_Map.createTileClass();
var landElevationPainter = new SmoothElevationPainter(ELEVATION_SET, heightLand, 4);
var unknownMapFunctions = {
"land": [
"Continent",
"Isthmus",
"CentralRiverLand",
"EdgeSeas",
"Gulf",
"Lakes",
"Passes",
"Lowlands",
"Mainland"
],
"naval": [
"CentralSea",
"CentralRiverNaval",
"Archipelago",
"RiversAndLake"
]
};
/**
* The player IDs and locations shall only be determined by the landscape functions if it's not a nomad game,
* because nomad maps randomize the locations after the terrain generation.
* The locations should only determined by the landscape functions to avoid placing bodies of water and resources into civic centers and the starting resources.
*/
var playerIDs = sortAllPlayers();
var playerPosition = [];
var g_StartingTreasures = false;
var g_StartingWalls = true;
function createUnknownMap()
{
const landscape = g_MapSettings.Landscape || pickRandom([...unknownMapFunctions.land, ...unknownMapFunctions.naval]);
global["unknown" + landscape]();
paintUnknownMapBasedOnHeight();
createUnknownPlayerBases();
createUnknownObjects();
placePlayersNomad(clPlayer, avoidClasses(clForest, 1, clMetal, 4, clRock, 4, clHill, 4, clFood, 2, clWater, 10));
}
/**
* Chain of islands or many disconnected islands.
*/
function unknownArchipelago()
{
g_StartingWalls = "towers";
g_StartingTreasures = true;
let [pIDs, islandPosition] = playerPlacementCircle(fractionToTiles(0.35));
if (!isNomad())
{
[playerIDs, playerPosition] = [pIDs, islandPosition];
markPlayerArea("large");
}
g_Map.log("Creating islands");
let islandSize = diskArea(scaleByMapSize(17, 29));
for (let i = 0; i < numPlayers; ++i)
createArea(
new ClumpPlacer(islandSize, 0.8, 0.1, Infinity, islandPosition[i]),
landElevationPainter);
let type = isNomad() ? randIntInclusive(1, 2) : randIntInclusive(1, 3);
if (type == 1)
{
g_Map.log("Creating archipelago");
createAreas(
new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, Infinity),
[
landElevationPainter,
new TileClassPainter(clLand)
],
null,
scaleByMapSize(2, 5) * randIntInclusive(8, 14));
g_Map.log("Creating shore jaggedness with small puddles");
createAreas(
new ClumpPlacer(scaleByMapSize(15, 80), 0.2, 0.1, Infinity),
[
new SmoothElevationPainter(ELEVATION_SET, heightLand, 4),
new TileClassPainter(clLand)
],
borderClasses(clLand, 6, 3),
scaleByMapSize(12, 130) * 2,
150);
}
else if (type == 2)
{
g_Map.log("Creating islands");
createAreas(
new ClumpPlacer(islandSize * randFloat(0.6, 1.4), 0.8, 0.1, randFloat(0.0, 0.2)),
[
landElevationPainter,
new TileClassPainter(clLand)
],
avoidClasses(clLand, 3, clPlayerTerritory, 3),
scaleByMapSize(6, 10) * randIntInclusive(8, 14));
g_Map.log("Creating small islands");
createAreas(
new ClumpPlacer(islandSize * randFloat(0.3, 0.7), 0.8, 0.1, 0.07),
[
new SmoothElevationPainter(ELEVATION_SET, heightLand, 6),
new TileClassPainter(clLand)
],
avoidClasses(clLand, 3, clPlayerTerritory, 3),
scaleByMapSize(2, 6) * randIntInclusive(6, 15),
25);
}
else if (type == 3)
{
g_Map.log("Creating tight islands");
createAreas(
new ClumpPlacer(islandSize * randFloat(0.8, 1.2), 0.8, 0.1, Infinity),
[
landElevationPainter,
new TileClassPainter(clLand)
],
avoidClasses(clLand, randIntInclusive(8, 16), clPlayerTerritory, 3),
scaleByMapSize(2, 5) * randIntInclusive(8, 14));
}
}
/**
* Disk shaped mainland with water on the edge.
*/
function unknownContinent()
{
let waterHeight = -5;
if (!isNomad())
{
g_Map.log("Ensuring player area");
[playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25));
markPlayerArea("small");
for (let i = 0; i < numPlayers; ++i)
createArea(
new ChainPlacer(
2,
Math.floor(scaleByMapSize(5, 9)),
Math.floor(scaleByMapSize(5, 20)),
Infinity,
playerPosition[i],
0,
[Math.floor(scaleByMapSize(23, 50))]),
[
landElevationPainter,
new TileClassPainter(clLand)
]);
}
g_Map.log("Creating continent");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, Infinity, mapCenter),
[
landElevationPainter,
new TileClassPainter(clLand)
]);
if (randBool(1/3))
{
g_Map.log("Creating peninsula (i.e. half the map not being surrounded by water)");
let angle = randomAngle();
let peninsulaPosition1 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.25), 0).rotate(-angle));
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.38)), 0.9, 0.09, Infinity, peninsulaPosition1),
[
landElevationPainter,
new TileClassPainter(clLand)
]);
g_Map.log("Remembering to not paint shorelines into the peninsula");
let peninsulaPosition2 = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(0.35), 0).rotate(-angle));
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.33)), 0.9, 0.01, Infinity, peninsulaPosition2),
new TileClassPainter(clPeninsulaSteam));
}
createShoreJaggedness(waterHeight, clLand, 7);
}
function unknownCentralSea()
{
unknownCentralSeaOrIsthmus(false);
}
function unknownIsthmus()
{
unknownCentralSeaOrIsthmus(true);
}
/**
* Creates a huge central river, possibly connecting the riversides with a narrow piece of land.
*/
function unknownCentralSeaOrIsthmus(isthmus)
{
let waterHeight = -3;
let startAngle = randomAngle();
let [riverStart, riverEnd] = centralRiverCoordinates(startAngle);
paintRiver({
"parallel": false,
"start": riverStart,
"end": riverEnd,
"width": fractionToTiles(scaleByMapSize(0.27, 0.42) + randFloat(0, 0.08)),
"fadeDist": scaleByMapSize(3, 12),
"deviation": 0,
"heightRiverbed": waterHeight,
"heightLand": heightLand,
"meanderShort": 20,
"meanderLong": 0,
"waterFunc": (position, height, riverFraction) => {
if (height < 0)
clWater.add(position);
},
"landFunc": (position, shoreDist1, shoreDist2) => {
g_Map.setHeight(position, 3.1);
clLand.add(position);
}
});
if (!isNomad())
{
[playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.6));
markPlayerArea("small");
}
if (isthmus)
{
g_Map.log("Creating isthmus (i.e. connecting the two riversides with a big land passage)");
let [isthmusStart, isthmusEnd] = centralRiverCoordinates(startAngle + Math.PI / 2);
createArea(
new PathPlacer(
isthmusStart,
isthmusEnd,
scaleByMapSize(randIntInclusive(16, 24), randIntInclusive(100, 140)),
0.5,
3 * scaleByMapSize(1, 4),
0.1,
0.01),
[
landElevationPainter,
new TileClassPainter(clLand),
new TileClassUnPainter(clWater)
]);
}
createExtensionsOrIslands();
// Don't createShoreJaggedness since it doesn't fit artistically here
}
function unknownCentralRiverLand()
{
unknownCentralRiver(true);
}
function unknownCentralRiverNaval()
{
unknownCentralRiver(false);
}
/**
* Creates a very small central river.
*/
function unknownCentralRiver(shallows)
{
let waterHeight = -4;
let heightShallow = -2;
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightLand));
let startAngle = randomAngle();
if (!isNomad())
{
[playerIDs, playerPosition] = playerPlacementRiver(startAngle + Math.PI / 2, fractionToTiles(0.5));
markPlayerArea("large");
}
g_Map.log("Creating the main river");
let [coord1, coord2] = centralRiverCoordinates(startAngle);
createArea(
new PathPlacer(coord1, coord2, scaleByMapSize(14, 24), 0.5, scaleByMapSize(3, 12), 0.1, 0.01),
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
avoidClasses(clPlayerTerritory, 4));
g_Map.log("Creating small water spots at the map border to ensure separation of players");
for (let coord of [coord1, coord2])
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(5, 10)), 0.95, 0.6, Infinity, coord),
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 2),
avoidClasses(clPlayerTerritory, 8));
if (shallows)
{
g_Map.log("Creating the shallows of the main river");
for (let i = 0; i <= randIntInclusive(1, scaleByMapSize(4, 8)); ++i)
{
let location = fractionToTiles(randFloat(0.15, 0.85));
createPassage({
"start": new Vector2D(location, mapBounds.top).rotateAround(startAngle, mapCenter),
"end": new Vector2D(location, mapBounds.bottom).rotateAround(startAngle, mapCenter),
"startWidth": scaleByMapSize(8, 12),
"endWidth": scaleByMapSize(8, 12),
"smoothWidth": 2,
"startHeight": heightShallow,
"endHeight": heightShallow,
"constraints": new HeightConstraint(-Infinity, heightShallow),
"tileClass": clShallow
});
}
}
if (randBool(2/3))
createTributaryRivers(
startAngle,
randIntInclusive(8, scaleByMapSize(12, 16)),
scaleByMapSize(10, 20),
-4,
[-6, -1.5],
Math.PI / 5,
clWater,
clShallow,
avoidClasses(clPlayerTerritory, 3));
}
/**
* Creates a circular lake in the middle and possibly a river between each player ("pizza slices").
*/
function unknownRiversAndLake()
{
let waterHeight = -4;
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightLand));
let startAngle;
if (!isNomad())
{
let playerAngle;
[playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
let lake = randBool(3/4);
if (lake)
{
g_Map.log("Creating lake");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.17)), 0.7, 0.1, Infinity, mapCenter),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
new TileClassPainter(clWater)
]);
createShoreJaggedness(waterHeight, clWater, 3);
}
// TODO: On nomad because the resource imbalances per island are too drastic
{
g_Map.log("Creating small rivers separating players");
for (let river of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0])
{
createArea(
new PathPlacer(mapCenter, river, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
new TileClassPainter(clWater)
],
avoidClasses(clPlayer, 5));
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, Infinity, river),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 0),
new TileClassPainter(clWater)
],
avoidClasses(clPlayer, 5));
}
g_Map.log("Creating small lake");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.04)), 0.7, 0.1, Infinity, mapCenter),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
new TileClassPainter(clWater)
]);
}
if (!isNomad && lake && randBool(2/3))
{
g_Map.log("Creating small central island");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, Infinity, mapCenter),
[
landElevationPainter,
new TileClassPainter(clWater)
]);
}
}
/**
* Align players on a land strip with seas bordering on one or both sides that can hold islands.
*/
function unknownEdgeSeas()
{
let waterHeight = -4;
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightLand));
let startAngle = randomAngle();
if (!isNomad())
{
playerIDs = sortAllPlayers();
playerPosition = playerPlacementLine(startAngle + Math.PI / 2, mapCenter, fractionToTiles(0.2));
// Don't place the shoreline inside the CC, but possibly into the players territory
markPlayerArea("small");
}
for (let side of pickRandom([[0], [Math.PI], [0, Math.PI]]))
paintRiver({
"parallel": true,
"start": new Vector2D(mapBounds.left, mapBounds.top).rotateAround(side + startAngle, mapCenter),
"end": new Vector2D(mapBounds.left, mapBounds.bottom).rotateAround(side + startAngle, mapCenter),
"width": scaleByMapSize(80, randFloat(270, 320)),
"fadeDist": scaleByMapSize(2, 8),
"deviation": 0,
"heightRiverbed": waterHeight,
"heightLand": heightLand,
"meanderShort": 20,
"meanderLong": 0
});
createExtensionsOrIslands();
paintTileClassBasedOnHeight(0, heightCliff, 1, clLand);
createShoreJaggedness(waterHeight, clLand, 7, false);
}
/**
* Land shaped like a concrescent moon around a central lake.
*/
function unknownGulf()
{
let waterHeight = -3;
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightLand));
let startAngle = randomAngle();
if (!isNomad())
{
g_Map.log("Determining player locations");
playerPosition = playerPlacementCustomAngle(
fractionToTiles(0.35),
mapCenter,
i => startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))))[0];
markPlayerArea("large");
}
let gulfParts = [
{ "radius": fractionToTiles(0.16), "distance": fractionToTiles(0) },
{ "radius": fractionToTiles(0.2), "distance": fractionToTiles(0.2) },
{ "radius": fractionToTiles(0.22), "distance": fractionToTiles(0.49) }
];
for (let gulfPart of gulfParts)
{
let position = Vector2D.sub(mapCenter, new Vector2D(gulfPart.distance, 0).rotate(-startAngle)).round();
createArea(
new ClumpPlacer(diskArea(gulfPart.radius), 0.7, 0.05, Infinity, position),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
new TileClassPainter(clWater)
],
avoidClasses(clPlayerTerritory, defaultPlayerBaseRadius()));
}
}
/**
* Mainland style with some small random lakes.
*/
function unknownLakes()
{
let waterHeight = -5;
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightLand));
if (!isNomad())
{
[playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("large");
}
g_Map.log("Creating lakes");
createAreas(
new ClumpPlacer(scaleByMapSize(160, 700), 0.2, 0.1, Infinity),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 5),
new TileClassPainter(clWater)
],
[avoidClasses(clPlayerTerritory, 12), randBool() ? avoidClasses(clWater, 8) : new NullConstraint()],
scaleByMapSize(5, 16));
}
/**
* A large hill leaving players only a small passage to each of the the two neighboring players.
*/
function unknownPasses()
{
let heightMountain = 24;
let waterHeight = -4;
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightLand));
let playerAngle;
let startAngle;
if (!isNomad())
{
[playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
else
startAngle = randomAngle();
g_Map.log("Creating a mountain range between neighboring players");
for (let mountain of distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0])
{
createArea(
new PathPlacer(mapCenter, mountain, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
[
// More smoothing than this often results in the mountainrange becoming passable to one player.
new SmoothElevationPainter(ELEVATION_SET, heightMountain, 1),
new TileClassPainter(clWater)
],
avoidClasses(clPlayer, 5));
// Small mountain at the map border between the players to ensure separation of players
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(4, 22)), 0.95, 0.6, Infinity, mountain),
new SmoothElevationPainter(ELEVATION_SET, heightMountain, 0),
avoidClasses(clPlayer, 5));
}
g_Map.log("Creating passages between neighboring players");
const passes = numPlayers == 2 && distributePointsOnCircle(numPlayers * 3, startAngle, fractionToTiles(0.35), mapCenter)[0];
for (let i = 0; i < numPlayers && numPlayers > 1; ++i)
{
// For numPlayers > 2 use the playerPosition to not end up inside the mountains.
createArea(
new PathPlacer(
numPlayers == 2 ? passes[3 * i + 1] : playerPosition[i],
numPlayers == 2 ? passes[3 * i + 2] : playerPosition[(i + 1) % numPlayers],
scaleByMapSize(14, 24),
0.4,
3 * scaleByMapSize(1, 3),
0.2,
0.05),
new SmoothElevationPainter(ELEVATION_SET, heightLand, 2));
}
if (randBool(2/5))
{
g_Map.log("Create central lake");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.1)), 0.7, 0.1, Infinity, mapCenter),
[
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 3),
new TileClassPainter(clWater)
]);
}
else
{
g_Map.log("Fill area between the paths");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.05)), 0.7, 0.1, Infinity, mapCenter),
[
new SmoothElevationPainter(ELEVATION_SET, heightMountain, 4),
new TileClassPainter(clWater)
]);
}
}
/**
* Land enclosed by a hill that leaves small areas for civic centers and large central place.
*/
function unknownLowlands()
{
let heightMountain = 30;
g_Map.log("Creating mountain that is going to separate players");
createArea(
new MapBoundsPlacer(),
new ElevationPainter(heightMountain));
let playerAngle;
let startAngle;
if (!isNomad())
{
[playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
else
startAngle = randomAngle();
g_Map.log("Creating valleys enclosed by the mountain");
let valleys = numPlayers;
if (mapSize >= 128 && numPlayers <= 2 ||
mapSize >= 192 && numPlayers <= 3 ||
mapSize >= 320 && numPlayers <= 4 ||
mapSize >= 384 && numPlayers <= 5 ||
mapSize >= 448 && numPlayers <= 6)
valleys *= 2;
g_Map.log("Creating player valley");
for (let valley of distributePointsOnCircle(valleys, startAngle, fractionToTiles(0.35), mapCenter)[0])
{
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(18, 32)), 0.65, 0.1, Infinity, valley),
[
new SmoothElevationPainter(ELEVATION_SET, heightLand, 2),
new TileClassPainter(clLand)
]);
// Passage from player to center
createArea(
new PathPlacer(mapCenter, valley, scaleByMapSize(14, 24), 0.4, 3 * scaleByMapSize(1, 3), 0.2, 0.05),
[
landElevationPainter,
new TileClassPainter(clWater)
]);
}
g_Map.log("Creating the big central area");
createArea(
new ClumpPlacer(diskArea(fractionToTiles(0.18)), 0.7, 0.1, Infinity, mapCenter),
[
landElevationPainter,
new TileClassPainter(clWater)
]);
}
/**
* No water, no hills.
*/
function unknownMainland()
{
createArea(
new MapBoundsPlacer(),
new ElevationPainter(3));
if (!isNomad())
{
[playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
}
function centralRiverCoordinates(angle)
{
return [
new Vector2D(mapBounds.left + 1, mapCenter.y),
new Vector2D(mapBounds.right - 1, mapCenter.y)
].map(v => v.rotateAround(angle, mapCenter));
}
function createShoreJaggedness(waterHeight, borderClass, shoreDist, inwards = true)
{
g_Map.log("Creating shore jaggedness");
for (let i = 0; i < 2; ++i)
if (i || inwards)
createAreas(
new ChainPlacer(2, Math.floor(scaleByMapSize(4, 6)), 15, Infinity),
[
new SmoothElevationPainter(ELEVATION_SET, i ? heightLand : waterHeight, 4),
i ? new TileClassPainter(clLand) : new TileClassUnPainter(clLand)
],
[
avoidClasses(clPlayer, 20, clPeninsulaSteam, 20),
borderClasses(borderClass, shoreDist, shoreDist)
],
scaleByMapSize(7, 130) * 2,
150);
}
function createExtensionsOrIslands()
{
let rnd = randIntInclusive(1, 3);
if (rnd == 1)
{
g_Map.log("Creating islands");
createAreas(
new ClumpPlacer(Math.square(randIntInclusive(scaleByMapSize(8, 15), scaleByMapSize(15, 23))), 0.8, 0.1, randFloat(0, 0.2)),
[
landElevationPainter,
new TileClassPainter(clLand)
],
avoidClasses(clLand, 3, clPlayer, 3),
scaleByMapSize(2, 5) * randIntInclusive(8, 14));
}
else if (rnd == 2)
{
g_Map.log("Creating extentions");
createAreas(
new ChainPlacer(Math.floor(scaleByMapSize(4, 7)), Math.floor(scaleByMapSize(7, 10)), Math.floor(scaleByMapSize(16, 40)), 0.07),
[
landElevationPainter,
new TileClassPainter(clLand)
],
null,
scaleByMapSize(2, 5) * randIntInclusive(8, 14));
}
}
/**
* Prevent impassable terrain and resource collisions at the the civic center and starting resources.
*/
function markPlayerArea(size)
{
for (let i = 0; i < numPlayers; ++i)
{
addCivicCenterAreaToClass(playerPosition[i], clPlayer);
if (size == "large")
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, Infinity, playerPosition[i]),
new TileClassPainter(clPlayerTerritory));
}
}
function paintUnknownMapBasedOnHeight()
{
paintTerrainBasedOnHeight(heightCliff, 40, 1, tCliff);
paintTerrainBasedOnHeight(3, heightCliff, 1, tMainTerrain);
paintTerrainBasedOnHeight(1, 3, 1, tShore);
paintTerrainBasedOnHeight(-8, 1, 2, tWater);
unPaintTileClassBasedOnHeight(0, heightCliff, 1, clWater);
unPaintTileClassBasedOnHeight(-6, 0, 1, clLand);
paintTileClassBasedOnHeight(-6, 0, 1, clWater);
paintTileClassBasedOnHeight(0, heightCliff, 1, clLand);
paintTileClassBasedOnHeight(heightCliff, 40, 1, clHill);
}
/**
* Place resources and decoratives after the player territory was marked.
*/
function createUnknownObjects()
{
g_Map.log("Creating bumps");
createAreas(
new ClumpPlacer(scaleByMapSize(20, 50), 0.3, 0.06, Infinity),
new SmoothElevationPainter(ELEVATION_MODIFY, heightOffsetBump, 2),
[avoidClasses(clWater, 2, clPlayer, 10), stayClasses(clLand, 3)],
randIntInclusive(0, scaleByMapSize(1, 2) * 200));
g_Map.log("Creating hills");
createAreas(
new ClumpPlacer(scaleByMapSize(20, 150), 0.2, 0.1, Infinity),
[
new LayeredPainter([tCliff, tHill], [2]),
new SmoothElevationPainter(ELEVATION_SET, heightHill, 2),
new TileClassPainter(clHill)
],
[avoidClasses(clPlayer, 15, clHill, randIntInclusive(6, 18)), stayClasses(clLand, 0)],
randIntInclusive(0, scaleByMapSize(4, 8))*randIntInclusive(1, scaleByMapSize(4, 9))
);
Engine.SetProgress(30);
g_Map.log("Creating forests");
let [numForest, numStragglers] = getTreeCounts(...rBiomeTreeCount(1));
let types = [
[[tForestFloor2, tMainTerrain, pForest1], [tForestFloor2, pForest1]],
[[tForestFloor1, tMainTerrain, pForest2], [tForestFloor1, pForest2]]
];
let size = numForest / (scaleByMapSize(2, 8) * numPlayers);
let num = Math.floor(size / types.length);
for (let type of types)
createAreas(
new ClumpPlacer(numForest / num, 0.1, 0.1, Infinity),
[
new LayeredPainter(type, [2]),
new TileClassPainter(clForest)
],
[avoidClasses(clPlayer, 20, clForest, randIntInclusive(5, 15), clHill, 2), stayClasses(clLand, 4)],
num);
Engine.SetProgress(50);
g_Map.log("Creating dirt patches");
let patchCount = (currentBiome() == "generic/savanna" ? 3 : 1) * scaleByMapSize(15, 45);
for (let patchSize of [scaleByMapSize(3, 48), scaleByMapSize(5, 84), scaleByMapSize(8, 128)])
createAreas(
new ClumpPlacer(patchSize, 0.3, 0.06, 0.5),
[
new LayeredPainter([[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], [1, 1]),
new TileClassPainter(clDirt)
],
[avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
patchCount);
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(tTier4Terrain),
[avoidClasses(clForest, 0, clHill, 2, clDirt, 5, clPlayer, 7), stayClasses(clLand, 4)],
patchCount);
Engine.SetProgress(55);
g_Map.log("Creating stone mines");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oStoneSmall, 0, 2, 0, 4, 0, 2 * Math.PI, 1), new SimpleObject(oStoneLarge, 1, 1, 0, 4, 0, 2 * Math.PI, 4)], true, clRock),
0,
[avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)],
randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
100);
g_Map.log("Creating small stone quarries");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oStoneSmall, 2, 5, 1, 3)], true, clRock),
0,
[avoidClasses(clForest, 1, clPlayer, 10, clRock, 10, clHill, 2), stayClasses(clLand, 3)],
randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
100);
g_Map.log("Creating metal mines");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oMetalLarge, 1, 1, 0, 4)], true, clMetal),
0,
[avoidClasses(clForest, 1, clPlayer, 10, clMetal, 10, clRock, 5, clHill, 2), stayClasses(clLand, 3)],
randIntInclusive(scaleByMapSize(2, 9), scaleByMapSize(9, 40)),
100);
Engine.SetProgress(65);
g_Map.log("Creating small decorative rocks");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(aRockMedium, 1, 3, 0, 1)], true),
0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)],
scaleByMapSize(16, 262),
50);
g_Map.log("Creating large decorative rocks");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)], true),
0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 0, clHill, 2), stayClasses(clLand, 3)],
scaleByMapSize(8, 131),
50);
Engine.SetProgress(70);
g_Map.log("Creating deer");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)], true, clFood),
0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)],
randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
50);
g_Map.log("Creating berry bush");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oFruitBush, 5, 7, 0, 4)], true, clFood),
0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)],
randIntInclusive(1, 4) * numPlayers + 2,
50);
Engine.SetProgress(75);
g_Map.log("Creating sheep");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)], true, clFood),
0,
[avoidClasses(clWater, 0, clForest, 0, clPlayer, 8, clHill, 2, clFood, 20), stayClasses(clLand, 2)],
randIntInclusive(numPlayers + 3, 5 * numPlayers + 4),
50);
g_Map.log("Creating fish");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(oFish, 2, 3, 0, 2)], true, clFood),
0,
avoidClasses(clLand, 4, clForest, 0, clPlayer, 0, clHill, 2, clFood, 20),
randIntInclusive(15, 40) * numPlayers,
60);
Engine.SetProgress(85);
g_Map.log("Creating straggler trees");
types = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
num = Math.floor(numStragglers / types.length);
for (let type of types)
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(type, 1, 1, 0, 3)], true, clForest),
0,
[avoidClasses(clWater, 1, clForest, 1, clHill, 2, clPlayer, 0, clMetal, 6, clRock, 6, clBaseResource, 6), stayClasses(clLand, 4)],
num);
let planetm = currentBiome() == "generic/india" ? 8 : 1;
g_Map.log("Creating small grass tufts");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(aGrassShort, 1, 2, 0, 1, -Math.PI / 8, Math.PI / 8)]),
0,
[avoidClasses(clWater, 2, clHill, 2, clPlayer, 2, clDirt, 0), stayClasses(clLand, 3)],
planetm * scaleByMapSize(13, 200));
Engine.SetProgress(90);
g_Map.log("Creating large grass tufts");
createObjectGroupsDeprecated(
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)]),
0,
[avoidClasses(clWater, 3, clHill, 2, clPlayer, 2, clDirt, 1, clForest, 0), stayClasses(clLand, 3)],
planetm * scaleByMapSize(13, 200));
Engine.SetProgress(95);
g_Map.log("Creating shallow flora");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(aLillies, 1, 2, 0, 2), new SimpleObject(aReeds, 2, 4, 0, 2)]),
0,
stayClasses(clShallow, 1),
60 * scaleByMapSize(13, 200),
80);
g_Map.log("Creating bushes");
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(aBushMedium, 1, 2, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]),
0,
[avoidClasses(clWater, 1, clHill, 2, clPlayer, 1, clDirt, 1), stayClasses(clLand, 3)],
planetm * scaleByMapSize(13, 200),
50);
setSkySet(pickRandom(["cirrus", "cumulus", "sunny", "sunny 1", "mountainous", "stratus"]));
setSunRotation(randomAngle());
setSunElevation(Math.PI * randFloat(1/5, 1/3));
}
function createUnknownPlayerBases()
{
placePlayerBases({
"PlayerPlacement": [playerIDs, playerPosition],
"BaseResourceClass": clBaseResource,
"Walls": g_StartingWalls,
"CityPatch": {
"outerTerrain": tRoadWild,
"innerTerrain": tRoad,
"painters": [
new TileClassPainter(clPlayer)
]
},
"StartingAnimal": {
},
"Berries": {
"template": oFruitBush
},
"Mines": {
"types": [
{ "template": oMetalLarge },
{ "template": oStoneLarge }
]
},
"Treasures": {
"types": [
{
"template": oWoodTreasure,
"count": g_StartingTreasures ? 14 : 0
}
]
},
"Trees": {
"template": oTree1
},
"Decoratives": {
"template": aGrassShort
}
});
}
createUnknownMap();
g_Map.ExportMap();