var g_Props = {
"barrels": "actor|props/special/eyecandy/barrels_buried.xml",
"crate": "actor|props/special/eyecandy/crate_a.xml",
"cart": "actor|props/special/eyecandy/handcart_1_broken.xml",
"well": "actor|props/special/eyecandy/well_1_c.xml",
"skeleton": "actor|props/special/eyecandy/skeleton.xml",
};
/**
* Prevent circular patterns around the CC by marking a random chain of circles there to be ignored by bluffs.
*/
function markPlayerAvoidanceArea(playerPosition, radius)
{
for (let position of playerPosition)
createArea(
new ChainPlacer(3, 6, scaleByMapSize(25, 60), Infinity, position, radius),
new TileClassPainter(g_TileClasses.bluffIgnore),
undefined,
scaleByMapSize(7, 14));
createArea(
new MapBoundsPlacer(),
new TileClassPainter(g_TileClasses.bluffIgnore),
new NearTileClassConstraint(g_TileClasses.baseResource, 5));
}
/**
* Paints a ramp from the given positions to t
* Bluffs might surround playerbases either entirely or unfairly.
*/
function createBluffsPassages(playerPosition)
{
g_Map.log("Creating passages towards the center");
for (let position of playerPosition)
{
let successful = true;
for (let tryCount = 0; tryCount < 80; ++tryCount)
{
let angle = position.angleTo(g_Map.getCenter()) + randFloat(-1, 1) * Math.PI / 2;
let start = Vector2D.add(position, new Vector2D(defaultPlayerBaseRadius() * 0.7, 0).rotate(angle).perpendicular()).round();
let end = Vector2D.add(position, new Vector2D(defaultPlayerBaseRadius() * randFloat(1.7, 2), 0).rotate(angle).perpendicular()).round();
if (g_TileClasses.forest.has(end) || !stayClasses(g_TileClasses.bluff, 12).allows(end))
continue;
if ((g_Map.getHeight(end.clone().floor()) - g_Map.getHeight(start.clone().floor())) / start.distanceTo(end) > 1.5)
continue;
let area = createPassage({
"start": start,
"end": end,
"startWidth": scaleByMapSize(10, 20),
"endWidth": scaleByMapSize(10, 14),
"smoothWidth": 3,
"terrain": g_Terrains.mainTerrain,
"tileClass": g_TileClasses.bluffsPassage
});
for (let point of area.getPoints())
g_Map.deleteTerrainEntity(point);
createArea(
new MapBoundsPlacer(),
new TerrainPainter(g_Terrains.cliff),
[
new StayAreasConstraint([area]),
new SlopeConstraint(2, Infinity)
]);
break;
}
}
}
/**
* Create bluffs, i.e. a slope hill reachable from ground level.
* Fill it with wood, mines, animals and decoratives.
*
* @param {Array} constraint - where to place them
* @param {number} size - size of the bluffs (1.2 would be 120% of normal)
* @param {number} deviation - degree of deviation from the defined size (0.2 would be 20% plus/minus)
* @param {number} fill - size of map to fill (1.5 would be 150% of normal)
* @param {number} baseHeight - elevation of the floor, making the bluff reachable
*/
function addBluffs(constraint, size, deviation, fill, baseHeight)
{
g_Map.log("Creating bluffs");
let elevation = 30;
// Percent of the length of the bluff determining the entrance area
let margin = 0.08;
let constrastTerrain = g_Terrains.tier2Terrain;
if (currentBiome() == "generic/india")
constrastTerrain = g_Terrains.dirt;
if (currentBiome() == "generic/autumn")
constrastTerrain = g_Terrains.tier3Terrain;
for (let i = 0; i < fill * 15; ++i)
{
let bluffDeviation = getRandomDeviation(size, deviation);
// Pick a random bluff location and shape
let areasBluff = createAreas(
new ChainPlacer(5 * bluffDeviation, 7 * bluffDeviation, 100 * bluffDeviation, 0.5),
undefined,
constraint,
1);
if (!areasBluff.length || !areasBluff[0].getPoints().length)
continue;
// Get a random starting position for the baseline and the endline
let angle = randIntInclusive(0, 3);
let opposingAngle = (angle + 2) % 4;
// Find the edges of the bluff
let baseLine;
let endLine;
// If we can't access the bluff, try different angles
let retries = 0;
let bluffPassable = false;
while (!bluffPassable && retries++ < 4)
{
baseLine = findClearLine(areasBluff[0], angle);
endLine = findClearLine(areasBluff[0], opposingAngle);
bluffPassable = isBluffPassable(areasBluff[0], baseLine, endLine);
angle = (angle + 1) % 4;
opposingAngle = (angle + 2) % 4;
}
if (!bluffPassable)
continue;
// Paint bluff texture and elevation
createArea(
new MapBoundsPlacer(),
[
new LayeredPainter([g_Terrains.mainTerrain, constrastTerrain], [5]),
new SmoothElevationPainter(ELEVATION_MODIFY, elevation * bluffDeviation, 2),
new TileClassPainter(g_TileClasses.bluff)
],
new StayAreasConstraint(areasBluff));
let slopeLength = (1 - margin) * Vector2D.average([baseLine.start, baseLine.end]).distanceTo(Vector2D.average([endLine.start, endLine.end]));
// Adjust the height of each point in the bluff
for (let point of areasBluff[0].getPoints())
{
let dist = Math.abs(distanceOfPointFromLine(baseLine.start, baseLine.end, point));
g_Map.setHeight(point, Math.max(g_Map.getHeight(point) * (1 - dist / slopeLength) - 2, baseHeight));
}
// Flatten all points adjacent to but not on the bluff
createArea(
new MapBoundsPlacer(),
[
new SmoothingPainter(1, 1, 1),
new TerrainPainter(g_Terrains.mainTerrain)
],
new AdjacentToAreaConstraint(areasBluff));
// Paint cliffs
createArea(
new MapBoundsPlacer(),
new TerrainPainter(g_Terrains.cliff),
[
new StayAreasConstraint(areasBluff),
new SlopeConstraint(2, Infinity)
]);
// Performance improvement
createArea(
new MapBoundsPlacer(),
new TileClassPainter(g_TileClasses.bluffIgnore),
new NearTileClassConstraint(g_TileClasses.bluff, 8));
}
addElements([
{
"func": addHills,
"avoid": [
g_TileClasses.hill, 3,
g_TileClasses.player, 20,
g_TileClasses.valley, 2,
g_TileClasses.water, 2
],
"stay": [g_TileClasses.bluff, 3],
"sizes": g_AllSizes,
"mixes": g_AllMixes,
"amounts": g_AllAmounts
}
]);
addElements([
{
"func": addLayeredPatches,
"avoid": [
g_TileClasses.dirt, 5,
g_TileClasses.forest, 2,
g_TileClasses.mountain, 2,
g_TileClasses.player, 12,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.bluff, 5],
"sizes": ["normal"],
"mixes": ["normal"],
"amounts": ["normal"]
}
]);
addElements([
{
"func": addDecoration,
"avoid": [
g_TileClasses.forest, 2,
g_TileClasses.player, 12,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.bluff, 5],
"sizes": ["normal"],
"mixes": ["normal"],
"amounts": ["normal"]
}
]);
addElements([
{
"func": addProps,
"avoid": [
g_TileClasses.forest, 2,
g_TileClasses.player, 12,
g_TileClasses.prop, 40,
g_TileClasses.water, 3
],
"stay": [
g_TileClasses.bluff, 7,
g_TileClasses.mountain, 7
],
"sizes": ["normal"],
"mixes": ["normal"],
"amounts": ["scarce"]
}
]);
addElements(shuffleArray([
{
"func": addForests,
"avoid": [
g_TileClasses.berries, 5,
g_TileClasses.forest, 18,
g_TileClasses.metal, 5,
g_TileClasses.mountain, 5,
g_TileClasses.player, 20,
g_TileClasses.rock, 5,
g_TileClasses.water, 2
],
"stay": [g_TileClasses.bluff, 6],
"sizes": g_AllSizes,
"mixes": g_AllMixes,
"amounts": ["normal", "many", "tons"]
},
{
"func": addMetal,
"avoid": [
g_TileClasses.berries, 5,
g_TileClasses.forest, 5,
g_TileClasses.mountain, 2,
g_TileClasses.player, 50,
g_TileClasses.rock, 15,
g_TileClasses.metal, 40,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.bluff, 6],
"sizes": ["normal"],
"mixes": ["same"],
"amounts": ["normal"]
},
{
"func": addStone,
"avoid": [
g_TileClasses.berries, 5,
g_TileClasses.forest, 5,
g_TileClasses.mountain, 2,
g_TileClasses.player, 50,
g_TileClasses.rock, 40,
g_TileClasses.metal, 15,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.bluff, 6],
"sizes": ["normal"],
"mixes": ["same"],
"amounts": ["normal"]
}
]));
let savanna = currentBiome() == "generic/savanna";
addElements(shuffleArray([
{
"func": addStragglerTrees,
"avoid": [
g_TileClasses.berries, 5,
g_TileClasses.forest, 10,
g_TileClasses.metal, 5,
g_TileClasses.mountain, 1,
g_TileClasses.player, 12,
g_TileClasses.rock, 5,
g_TileClasses.water, 5
],
"stay": [g_TileClasses.bluff, 6],
"sizes": savanna ? ["big"] : g_AllSizes,
"mixes": savanna ? ["varied"] : g_AllMixes,
"amounts": savanna ? ["tons"] : ["normal", "many", "tons"]
},
{
"func": addAnimals,
"avoid": [
g_TileClasses.animals, 20,
g_TileClasses.forest, 5,
g_TileClasses.mountain, 1,
g_TileClasses.player, 20,
g_TileClasses.rock, 5,
g_TileClasses.metal, 5,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.bluff, 6],
"sizes": g_AllSizes,
"mixes": g_AllMixes,
"amounts": ["normal", "many", "tons"]
},
{
"func": addBerries,
"avoid": [
g_TileClasses.berries, 50,
g_TileClasses.forest, 5,
g_TileClasses.metal, 10,
g_TileClasses.mountain, 2,
g_TileClasses.player, 20,
g_TileClasses.rock, 10,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.bluff, 6],
"sizes": g_AllSizes,
"mixes": g_AllMixes,
"amounts": ["normal", "many", "tons"]
}
]));
}
/**
* Add grass, rocks and bushes.
*/
function addDecoration(constraint, size, deviation, fill)
{
g_Map.log("Creating decoration");
var offset = getRandomDeviation(size, deviation);
var decorations = [
[
new SimpleObject(g_Decoratives.rockMedium, offset, 3 * offset, 0, offset)
],
[
new SimpleObject(g_Decoratives.rockLarge, offset, 2 * offset, 0, offset),
new SimpleObject(g_Decoratives.rockMedium, offset, 3 * offset, 0, 2 * offset)
],
[
new SimpleObject(g_Decoratives.grassShort, offset, 2 * offset, 0, offset)
],
[
new SimpleObject(g_Decoratives.grass, 2 * offset, 4 * offset, 0, 1.8 * offset),
new SimpleObject(g_Decoratives.grassShort, 3 * offset, 6 * offset, 1.2 * offset, 2.5 * offset)
],
[
new SimpleObject(g_Decoratives.bushMedium, offset, 2 * offset, 0, 2 * offset),
new SimpleObject(g_Decoratives.bushSmall, 2 * offset, 4 * offset, 0, 2 * offset)
]
];
var baseCount = 1;
if (currentBiome() == "generic/india")
baseCount = 8;
var counts = [
scaleByMapSize(16, 262),
scaleByMapSize(8, 131),
baseCount * scaleByMapSize(13, 200),
baseCount * scaleByMapSize(13, 200),
baseCount * scaleByMapSize(13, 200)
];
for (var i = 0; i < decorations.length; ++i)
{
var decorCount = Math.floor(counts[i] * fill);
var group = new SimpleGroup(decorations[i], true);
createObjectGroupsDeprecated(group, 0, constraint, decorCount, 5);
}
}
/**
* Create varying elevations.
*
* @param {Array} constraint - avoid/stay-classes
*
* @param {Object} el - the element to be rendered, for example:
* "class": g_TileClasses.hill,
* "painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
* "size": 1,
* "deviation": 0.2,
* "fill": 1,
* "count": scaleByMapSize(4, 8),
* "minSize": Math.floor(scaleByMapSize(3, 8)),
* "maxSize": Math.floor(scaleByMapSize(5, 10)),
* "spread": Math.floor(scaleByMapSize(10, 20)),
* "minElevation": 6,
* "maxElevation": 12,
* "steepness": 1.5
*/
function addElevation(constraint, el)
{
var count = el.fill * el.count;
var minSize = el.minSize;
var maxSize = el.maxSize;
var spread = el.spread;
var elType = ELEVATION_MODIFY;
if (el.class == g_TileClasses.water)
elType = ELEVATION_SET;
var widths = [];
// Allow for shore and cliff rendering
for (var s = el.painter.length; s > 2; --s)
widths.push(1);
for (var i = 0; i < count; ++i)
{
var elevation = randIntExclusive(el.minElevation, el.maxElevation);
var smooth = Math.floor(elevation / el.steepness);
var offset = getRandomDeviation(el.size, el.deviation);
var pMinSize = Math.floor(minSize * offset);
var pMaxSize = Math.floor(maxSize * offset);
var pSpread = Math.floor(spread * offset);
var pSmooth = Math.abs(Math.floor(smooth * offset));
var pElevation = Math.floor(elevation * offset);
pElevation = Math.max(el.minElevation, Math.min(pElevation, el.maxElevation));
pMinSize = Math.min(pMinSize, pMaxSize);
pMaxSize = Math.min(pMaxSize, el.maxSize);
pMinSize = Math.max(pMaxSize, el.minSize);
pSmooth = Math.max(pSmooth, 1);
createAreas(
new ChainPlacer(pMinSize, pMaxSize, pSpread, 0.5),
[
new LayeredPainter(el.painter, [widths.concat(pSmooth)]),
new SmoothElevationPainter(elType, pElevation, pSmooth),
new TileClassPainter(el.class)
],
constraint,
1);
}
}
/**
* Create rolling hills.
*/
function addHills(constraint, size, deviation, fill)
{
g_Map.log("Creating hills");
addElevation(constraint, {
"class": g_TileClasses.hill,
"painter": [g_Terrains.mainTerrain, g_Terrains.mainTerrain],
"size": size,
"deviation": deviation,
"fill": fill,
"count": 8,
"minSize": 5,
"maxSize": 8,
"spread": 20,
"minElevation": 6,
"maxElevation": 12,
"steepness": 1.5
});
createArea(
new MapBoundsPlacer(),
new TileClassPainter(g_TileClasses.bluffIgnore),
new NearTileClassConstraint(g_TileClasses.hill, 6));
}
/**
* Create random lakes with fish in it.
*/
function addLakes(constraint, size, deviation, fill)
{
g_Map.log("Creating lakes");
var lakeTile = g_Terrains.water;
if (currentBiome() == "generic/temperate" || currentBiome() == "generic/india")
lakeTile = g_Terrains.dirt;
if (currentBiome() == "generic/aegean")
lakeTile = g_Terrains.tier2Terrain;
if (currentBiome() == "generic/autumn")
lakeTile = g_Terrains.shore;
addElevation(constraint, {
"class": g_TileClasses.water,
"painter": [lakeTile, lakeTile],
"size": size,
"deviation": deviation,
"fill": fill,
"count": 6,
"minSize": 7,
"maxSize": 9,
"spread": 70,
"minElevation": -15,
"maxElevation": -2,
"steepness": 1.5
});
addElements([
{
"func": addFish,
"avoid": [
g_TileClasses.fish, 12,
g_TileClasses.hill, 8,
g_TileClasses.mountain, 8,
g_TileClasses.player, 8
],
"stay": [g_TileClasses.water, 7],
"sizes": g_AllSizes,
"mixes": g_AllMixes,
"amounts": ["normal", "many", "tons"]
}
]);
var group = new SimpleGroup([new SimpleObject(g_Decoratives.rockMedium, 1, 3, 1, 3)], true, g_TileClasses.dirt);
createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 1), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
group = new SimpleGroup([new SimpleObject(g_Decoratives.reeds, 10, 15, 1, 3), new SimpleObject(g_Decoratives.rockMedium, 1, 3, 1, 3)], true, g_TileClasses.dirt);
createObjectGroupsDeprecated(group, 0, [stayClasses(g_TileClasses.water, 2), borderClasses(g_TileClasses.water, 4, 3)], 1000, 100);
}
/**
* Universal function to create layered patches.
*/
function addLayeredPatches(constraint, size, deviation, fill)
{
g_Map.log("Creating layered patches");
var minRadius = 1;
var maxRadius = Math.floor(scaleByMapSize(3, 5));
var count = fill * scaleByMapSize(15, 45);
var patchSizes = [
scaleByMapSize(3, 6),
scaleByMapSize(5, 10),
scaleByMapSize(8, 21)
];
for (let patchSize of patchSizes)
{
var offset = getRandomDeviation(size, deviation);
var patchMinRadius = Math.floor(minRadius * offset);
var patchMaxRadius = Math.floor(maxRadius * offset);
createAreas(
new ChainPlacer(Math.min(patchMinRadius, patchMaxRadius), patchMaxRadius, Math.floor(patchSize * offset), 0.5),
[
new LayeredPainter(
[
[g_Terrains.mainTerrain, g_Terrains.tier1Terrain],
[g_Terrains.tier1Terrain, g_Terrains.tier2Terrain],
[g_Terrains.tier2Terrain, g_Terrains.tier3Terrain],
[g_Terrains.tier4Terrain]
],
[1, 1]),
new TileClassPainter(g_TileClasses.dirt)
],
constraint,
count * offset);
}
}
/**
* Create steep mountains.
*/
function addMountains(constraint, size, deviation, fill)
{
g_Map.log("Creating mountains");
addElevation(constraint, {
"class": g_TileClasses.mountain,
"painter": [g_Terrains.cliff, g_Terrains.hill],
"size": size,
"deviation": deviation,
"fill": fill,
"count": 8,
"minSize": 2,
"maxSize": 4,
"spread": 100,
"minElevation": 100,
"maxElevation": 120,
"steepness": 4
});
}
/**
* Create plateaus.
*/
function addPlateaus(constraint, size, deviation, fill)
{
g_Map.log("Creating plateaus");
var plateauTile = g_Terrains.dirt;
if (currentBiome() == "generic/arctic")
plateauTile = g_Terrains.tier1Terrain;
if (currentBiome() == "generic/alpine" || currentBiome() == "generic/savanna")
plateauTile = g_Terrains.tier2Terrain;
if (currentBiome() == "generic/autumn")
plateauTile = g_Terrains.tier4Terrain;
addElevation(constraint, {
"class": g_TileClasses.plateau,
"painter": [g_Terrains.cliff, plateauTile],
"size": size,
"deviation": deviation,
"fill": fill,
"count": 15,
"minSize": 2,
"maxSize": 4,
"spread": 200,
"minElevation": 20,
"maxElevation": 30,
"steepness": 8
});
for (var i = 0; i < 40; ++i)
{
var hillElevation = randIntInclusive(4, 18);
createAreas(
new ChainPlacer(3, 15, 1, 0.5),
[
new LayeredPainter([plateauTile, plateauTile], [3]),
new SmoothElevationPainter(ELEVATION_MODIFY, hillElevation, hillElevation - 2),
new TileClassPainter(g_TileClasses.hill)
],
[
avoidClasses(g_TileClasses.hill, 7),
stayClasses(g_TileClasses.plateau, 7)
],
1);
}
addElements([
{
"func": addDecoration,
"avoid": [
g_TileClasses.dirt, 15,
g_TileClasses.forest, 2,
g_TileClasses.player, 12,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.plateau, 8],
"sizes": ["normal"],
"mixes": ["normal"],
"amounts": ["tons"]
},
{
"func": addProps,
"avoid": [
g_TileClasses.forest, 2,
g_TileClasses.player, 12,
g_TileClasses.prop, 40,
g_TileClasses.water, 3
],
"stay": [g_TileClasses.plateau, 8],
"sizes": ["normal"],
"mixes": ["normal"],
"amounts": ["scarce"]
}
]);
}
/**
* Place less usual decoratives like barrels or crates.
*/
function addProps(constraint, size, deviation, fill)
{
g_Map.log("Creating rare actors");
var offset = getRandomDeviation(size, deviation);
var props = [
[
new SimpleObject(g_Props.skeleton, offset, 5 * offset, 0, 3 * offset + 2),
],
[
new SimpleObject(g_Props.barrels, offset, 2 * offset, 2, 3 * offset + 2),
new SimpleObject(g_Props.cart, 0, offset, 5, 2.5 * offset + 5),
new SimpleObject(g_Props.crate, offset, 2 * offset, 2, 2 * offset + 2),
new SimpleObject(g_Props.well, 0, 1, 2, 2 * offset + 2)
]
];
var baseCount = 1;
var counts = [
scaleByMapSize(16, 262),
scaleByMapSize(8, 131),
baseCount * scaleByMapSize(13, 200),
baseCount * scaleByMapSize(13, 200),
baseCount * scaleByMapSize(13, 200)
];
// Add small props
for (var i = 0; i < props.length; ++i)
{
var propCount = Math.floor(counts[i] * fill);
var group = new SimpleGroup(props[i], true);
createObjectGroupsDeprecated(group, 0, constraint, propCount, 5);
}
// Add decorative trees
var trees = new SimpleObject(g_Decoratives.tree, 5 * offset, 30 * offset, 2, 3 * offset + 10);
createObjectGroupsDeprecated(new SimpleGroup([trees], true), 0, constraint, counts[0] * 5 * fill, 5);
}
function addValleys(constraint, size, deviation, fill, baseHeight)
{
if (baseHeight < 6)
return;
g_Map.log("Creating valleys");
let minElevation = Math.max(-baseHeight, 1 - baseHeight / (size * (deviation + 1)));
var valleySlope = g_Terrains.tier1Terrain;
var valleyFloor = g_Terrains.tier4Terrain;
if (currentBiome() == "generic/sahara")
{
valleySlope = g_Terrains.tier3Terrain;
valleyFloor = g_Terrains.dirt;
}
if (currentBiome() == "generic/aegean")
{
valleySlope = g_Terrains.tier2Terrain;
valleyFloor = g_Terrains.dirt;
}
if (currentBiome() == "generic/alpine" || currentBiome() == "generic/savanna")
valleyFloor = g_Terrains.tier2Terrain;
if (currentBiome() == "generic/india")
valleySlope = g_Terrains.dirt;
if (currentBiome() == "generic/autumn")
valleyFloor = g_Terrains.tier3Terrain;
addElevation(constraint, {
"class": g_TileClasses.valley,
"painter": [valleySlope, valleyFloor],
"size": size,
"deviation": deviation,
"fill": fill,
"count": 8,
"minSize": 5,
"maxSize": 8,
"spread": 30,
"minElevation": minElevation,
"maxElevation": -2,
"steepness": 4
});
}
/**
* Create huntable animals.
*/
function addAnimals(constraint, size, deviation, fill)
{
g_Map.log("Creating animals");
var groupOffset = getRandomDeviation(size, deviation);
var animals = [
[new SimpleObject(g_Gaia.mainHuntableAnimal, 5 * groupOffset, 7 * groupOffset, 0, 4 * groupOffset)],
[new SimpleObject(g_Gaia.secondaryHuntableAnimal, 2 * groupOffset, 3 * groupOffset, 0, 2 * groupOffset)]
];
for (let animal of animals)
createObjectGroupsDeprecated(
new SimpleGroup(animal, true, g_TileClasses.animals),
0,
constraint,
Math.floor(30 * fill),
50);
}
function addBerries(constraint, size, deviation, fill)
{
g_Map.log("Creating berries");
let groupOffset = getRandomDeviation(size, deviation);
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(g_Gaia.fruitBush, 5 * groupOffset, 5 * groupOffset, 0, 3 * groupOffset)], true, g_TileClasses.berries),
0,
constraint,
Math.floor(50 * fill),
40);
}
function addFish(constraint, size, deviation, fill)
{
g_Map.log("Creating fish");
var groupOffset = getRandomDeviation(size, deviation);
var fishes = [
[new SimpleObject(g_Gaia.fish, groupOffset, 2 * groupOffset, 0, 2 * groupOffset)],
[new SimpleObject(g_Gaia.fish, 2 * groupOffset, 4 * groupOffset, 10 * groupOffset, 20 * groupOffset)]
];
for (let fish of fishes)
createObjectGroupsDeprecated(
new SimpleGroup(fish, true, g_TileClasses.fish),
0,
constraint,
Math.floor(40 * fill),
50);
}
function addForests(constraint, size, deviation, fill)
{
if (currentBiome() == "generic/savanna")
return;
g_Map.log("Creating forests");
let treeTypes = [
[
g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree1,
g_Terrains.forestFloor2 + TERRAIN_SEPARATOR + g_Gaia.tree2,
g_Terrains.forestFloor2
],
[
g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree4,
g_Terrains.forestFloor1 + TERRAIN_SEPARATOR + g_Gaia.tree5,
g_Terrains.forestFloor1
]
];
let forestTypes = [
[
[g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[0]],
[g_Terrains.forestFloor2, treeTypes[0]]
],
[
[g_Terrains.forestFloor2, g_Terrains.mainTerrain, treeTypes[1]],
[g_Terrains.forestFloor1, treeTypes[1]]],
[
[g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[0]],
[g_Terrains.forestFloor2, treeTypes[0]]],
[
[g_Terrains.forestFloor1, g_Terrains.mainTerrain, treeTypes[1]],
[g_Terrains.forestFloor1, treeTypes[1]]
]
];
for (let forestType of forestTypes)
{
let offset = getRandomDeviation(size, deviation);
createAreas(
new ChainPlacer(1, Math.floor(scaleByMapSize(3, 5) * offset), Math.floor(50 * offset), 0.5),
[
new LayeredPainter(forestType, [2]),
new TileClassPainter(g_TileClasses.forest)
],
constraint,
10 * fill);
}
}
function addMetal(constraint, size, deviation, fill)
{
g_Map.log("Creating metal mines");
var offset = getRandomDeviation(size, deviation);
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(g_Gaia.metalLarge, offset, offset, 0, 4 * offset)], true, g_TileClasses.metal),
0,
constraint,
1 + 20 * fill,
100);
}
function addSmallMetal(constraint, size, mixes, amounts)
{
g_Map.log("Creating small metal mines");
let deviation = getRandomDeviation(size, mixes);
createObjectGroupsDeprecated(
new SimpleGroup([new SimpleObject(g_Gaia.metalSmall, 2 * deviation, 5 * deviation, deviation, 3 * deviation)], true, g_TileClasses.metal),
0,
constraint,
1 + 20 * amounts,
100);
}
/**
* Create stone mines.
*/
function addStone(constraint, size, deviation, fill)
{
g_Map.log("Creating stone mines");
var offset = getRandomDeviation(size, deviation);
var mines = [
[
new SimpleObject(g_Gaia.stoneSmall, 0, 2 * offset, 0, 4 * offset),
new SimpleObject(g_Gaia.stoneLarge, offset, offset, 0, 4 * offset)
],
[
new SimpleObject(g_Gaia.stoneSmall, 2 * offset, 5 * offset, offset, 3 * offset)
]
];
for (let mine of mines)
createObjectGroupsDeprecated(
new SimpleGroup(mine, true, g_TileClasses.rock),
0,
constraint,
1 + 20 * fill,
100);
}
/**
* Create straggler trees.
*/
function addStragglerTrees(constraint, size, deviation, fill)
{
g_Map.log("Creating straggler trees");
// Ensure minimum distribution on african biome
if (currentBiome() == "generic/savanna")
{
fill = Math.max(fill, 2);
size = Math.max(size, 1);
}
var trees = [g_Gaia.tree1, g_Gaia.tree2, g_Gaia.tree3, g_Gaia.tree4];
var treesPerPlayer = 40;
var playerBonus = Math.max(1, (getNumPlayers() - 3) / 2);
var offset = getRandomDeviation(size, deviation);
var treeCount = treesPerPlayer * playerBonus * fill;
var totalTrees = scaleByMapSize(treeCount, treeCount);
var count = Math.floor(totalTrees / trees.length) * fill;
var min = offset;
var max = 4 * offset;
var minDist = offset;
var maxDist = 5 * offset;
// More trees for the african biome
if (currentBiome() == "generic/savanna")
{
min = 3 * offset;
max = 5 * offset;
minDist = 2 * offset + 1;
maxDist = 3 * offset + 2;
}
for (var i = 0; i < trees.length; ++i)
{
var treesMax = max;
// Don't clump fruit trees
if (i == 2 && (currentBiome() == "generic/sahara" || currentBiome() == "generic/aegean"))
treesMax = 1;
min = Math.min(min, treesMax);
var group = new SimpleGroup([new SimpleObject(trees[i], min, treesMax, minDist, maxDist)], true, g_TileClasses.forest);
createObjectGroupsDeprecated(group, 0, constraint, count);
}
}
/**
* Determine if the endline of the bluff is within the tilemap.
*/
function isBluffPassable(bluffArea, baseLine, endLine)
{
if (!baseLine ||
!endLine ||
!g_Map.validTilePassable(endLine.start) &&
!g_Map.validTilePassable(endLine.end))
return false;
let minTilesInGroup = 2;
let insideBluff = false;
let outsideBluff = false;
// If there aren't enough points in each row
let corners = getBoundingBox(bluffArea.getPoints());
for (let x = corners.min.x; x <= corners.max.x; ++x)
{
let count = 0;
for (let y = corners.min.y; y <= corners.max.y; ++y)
{
let pos = new Vector2D(x, y);
if (!bluffArea.contains(pos))
continue;
let valid = g_Map.validTilePassable(pos);
if (valid)
++count;
if (valid)
insideBluff = true;
if (outsideBluff && valid)
return false;
}
// We're expecting the end of the bluff
if (insideBluff && count < minTilesInGroup)
outsideBluff = true;
}
insideBluff = false;
outsideBluff = false;
// If there aren't enough points in each column
for (let y = corners.min.y; y <= corners.max.y; ++y)
{
let count = 0;
for (let x = corners.min.x; x <= corners.max.x; ++x)
{
let pos = new Vector2D(x, y);
if (!bluffArea.contains(pos))
continue;
let valid = g_Map.validTilePassable(pos.add(corners.min));
if (valid)
++count;
if (valid)
insideBluff = true;
if (outsideBluff && valid)
return false;
}
// We're expecting the end of the bluff
if (insideBluff && count < minTilesInGroup)
outsideBluff = true;
}
return true;
}
/**
* Find a 45 degree line that does not intersect with the bluff.
*/
function findClearLine(bluffArea, angle)
{
let corners = getBoundingBox(bluffArea.getPoints());
// Angle - 0: northwest; 1: northeast; 2: southeast; 3: southwest
let offset;
let y;
switch (angle)
{
case 0:
offset = new Vector2D(-1, -1);
y = corners.max.y;
break;
case 1:
offset = new Vector2D(1, -1);
y = corners.max.y;
break;
case 2:
offset = new Vector2D(1, 1);
y = corners.min.y;
break;
case 3:
offset = new Vector2D(-1, 1);
y = corners.min.y;
break;
default:
throw new Error("Unknown angle " + angle);
}
let clearLine;
for (let x = corners.min.x; x <= corners.max.x; ++x)
{
let start = new Vector2D(x, y);
let intersectsBluff = false;
let end = start.clone();
while (end.x >= corners.min.x && end.x <= corners.max.x && end.y >= corners.min.y && end.y <= corners.max.y)
{
if (bluffArea.contains(end) && g_Map.validTilePassable(end))
{
intersectsBluff = true;
break;
}
end.add(offset);
}
if (!intersectsBluff)
clearLine = {
"start": start,
"end": end.sub(offset)
};
if (intersectsBluff ? (angle == 0 || angle == 3) : (angle == 1 || angle == 2))
break;
}
return clearLine;
}
/**
* Returns a number within a random deviation of a base number.
*/
function getRandomDeviation(base, deviation)
{
return base + randFloat(-1, 1) * Math.min(base, deviation);
}