1
0
forked from mirrors/0ad

Merge all playerX and playerZ arrays into one array of Vector2D items.

This way random map scripts can do vector math and pass the result to
library functions that do further vector math on them and leave shorter
code, refs #4845.
Pass tilegrid coordinates rather than percent numbers to the playerbase
functions too, refs #4939.
Some more mapCenter calls, refs #4854.

This was SVN commit r20882.
This commit is contained in:
elexis
2018-01-16 04:39:31 +00:00
parent f8eae16390
commit 442c9848b8
67 changed files with 322 additions and 407 deletions
@@ -18,14 +18,15 @@ function shuffleArray(source)
/**
* Generates each permutation of the given array and runs the callback function on it.
* Uses the given clone function on each item of the array.
* Creating arrays with all permutations of the given array has a bad memory footprint.
* Algorithm by B. R. Heap. Changes the input array.
*/
function heapsPermute(array, callback)
function heapsPermute(array, cloneFunc, callback)
{
let c = new Array(array.length).fill(0);
callback(clone(array));
callback(array.map(cloneFunc));
let i = 0;
while (i < array.length)
@@ -33,11 +34,11 @@ function heapsPermute(array, callback)
if (c[i] < i)
{
let swapIndex = i % 2 ? c[i] : 0;
let swapValue = clone(array[swapIndex]);
let swapValue = cloneFunc(array[swapIndex]);
array[swapIndex] = array[i];
array[i] = swapValue;
callback(clone(array));
callback(array.map(cloneFunc));
++c[i];
i = 0;
@@ -69,7 +69,7 @@ var shoreHeight = 1;
var landHeight = 2;
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(0, 0.6),
"PlayerPlacement": playerPlacementRiver(0, fractionToTiles(0.6)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -54,7 +54,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -98,7 +98,7 @@ var clBaseResource = createTileClass();
initTerrain(tPrimary);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -302,10 +302,10 @@ var mountainHeight = 30;
initTerrain(tPrimary);
var [playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -42,7 +42,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -62,7 +62,7 @@ var shoreHeight = 1;
var islandRadius = scaleByMapSize(22, 31);
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
log("Creating player islands...");
for (let i = 0; i < numPlayers; ++i)
@@ -72,8 +72,8 @@ for (let i = 0; i < numPlayers; ++i)
Math.floor(scaleByMapSize(5, 10)),
Math.floor(scaleByMapSize(25, 60)),
1,
Math.floor(fractionToTiles(playerX[i])),
Math.floor(fractionToTiles(playerZ[i])),
playerPosition[i].x,
playerPosition[i].y,
0,
[Math.floor(islandRadius)]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 4));
@@ -100,7 +100,7 @@ paintTerrainBasedOnHeight(shoreHeight, landHeight, 0, tShore);
paintTerrainBasedOnHeight(getMapBaseHeight(), shoreHeight, 2, tWater);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass marked below
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -54,7 +54,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -89,15 +89,15 @@ for (var ix = 0; ix < mapSize; ix++)
}
}
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.3);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.3));
function distanceToPlayers(x, z)
{
var r = 10000;
for (var i = 0; i < numPlayers; i++)
{
var dx = x - playerX[i];
var dz = z - playerZ[i];
var dx = x - playerPosition[i].x;
var dz = z - playerPosition[i].y;
r = Math.min(r, Math.square(dx) + Math.square(dz));
}
return Math.sqrt(r);
@@ -119,7 +119,7 @@ function playerNearness(x, z)
Engine.SetProgress(10);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"BaseResourceClass": clBaseResource,
// Playerclass marked below
"CityPatch": {
@@ -153,7 +153,7 @@ placePlayerBases({
log("Marking player territory larger than the city patch...");
for (let i = 0; i < numPlayers; ++i)
createArea(
new ClumpPlacer(250, 0.95, 0.3, 0.1, Math.floor(fractionToTiles(playerX[i])), Math.floor(fractionToTiles(playerZ[i]))),
new ClumpPlacer(250, 0.95, 0.3, 0.1, playerPosition[i].x, playerPosition[i].y),
paintClass(clPlayer));
Engine.SetProgress(30);
@@ -48,7 +48,7 @@ var clBaseResource = createTileClass();
var clTreasure = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -383,7 +383,7 @@ else
for (let i = 0; i < numPlayers; ++i)
{
let playerPos = new Vector2D(possibleStartPositions[bestDerivation[i]][0], possibleStartPositions[bestDerivation[i]][1]);
placeCivDefaultStartingEntities(playerPos.x, playerPos.y, playerIDs[i], false);
placeCivDefaultStartingEntities(playerPos, playerIDs[i], false);
for (let j = 1; j <= 4; ++j)
{
@@ -57,7 +57,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -428,7 +428,7 @@ else
for (let p = 0; p < playerIDs.length; ++p)
{
let point = startLocations[p];
placeCivDefaultStartingEntities(point.x, point.y, playerIDs[p], true);
placeCivDefaultStartingEntities(point, playerIDs[p], true);
placeStartLocationResources(startLocations[p]);
}
@@ -59,14 +59,13 @@ var clBaseResource = createTileClass();
var playerHillRadius = defaultPlayerBaseRadius() / (isNomad() ? 1.5 : 1);
var playerHillElevation = 20;
var [playerIDs, playerX, playerZ, playerAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle] = playerPlacementCircle(fractionToTiles(0.35));
log("Creating player hills and ramps...");
for (let i = 0; i < numPlayers; ++i)
{
let playerPos = new Vector2D(playerX[i], playerZ[i]).mult(mapSize);
createArea(
new ClumpPlacer(diskArea(playerHillRadius), 0.95, 0.6, 10, Math.round(playerPos.x), Math.round(playerPos.y)),
new ClumpPlacer(diskArea(playerHillRadius), 0.95, 0.6, 10, playerPosition[i].x, playerPosition[i].y),
[
new LayeredPainter([tCliff, tHill], [2]),
new SmoothElevationPainter(ELEVATION_SET, playerHillElevation, 2),
@@ -75,8 +74,8 @@ for (let i = 0; i < numPlayers; ++i)
let angle = playerAngle[i] + Math.PI * (1 + randFloat(-1, 1) / 8);
createPassage({
"start": Vector2D.add(playerPos, new Vector2D(playerHillRadius + 15, 0).rotate(-angle)),
"end": Vector2D.add(playerPos, new Vector2D(playerHillRadius - 3, 0).rotate(-angle)),
"start": Vector2D.add(playerPosition[i], new Vector2D(playerHillRadius + 15, 0).rotate(-angle)),
"end": Vector2D.add(playerPosition[i], new Vector2D(playerHillRadius - 3, 0).rotate(-angle)),
"startWidth": 10,
"endWidth": 10,
"smoothWidth": 2,
@@ -87,7 +86,7 @@ for (let i = 0; i < numPlayers; ++i)
}
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": false,
+14 -13
View File
@@ -44,7 +44,8 @@ InitMap();
var numPlayers = getNumPlayers();
var mapSize = getMapSize();
var mapArea = mapSize*mapSize;
var mapArea = getMapArea();
var mapCenter = getMapCenter();
var clPlayer = createTileClass();
var clHill = createTileClass();
@@ -64,7 +65,7 @@ var playerCanyonRadius = scaleByMapSize(18, 32);
initTerrain(tMainTerrain);
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
log("Reserving space for the players, their initial forests and some less space therein without trees...");
for (let i = 0; i < numPlayers; ++i)
@@ -75,8 +76,8 @@ for (let i = 0; i < numPlayers; ++i)
0.65,
0.1,
10,
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i]))),
playerPosition[i].x,
playerPosition[i].y),
[
new LayeredPainter([tMainTerrain, tMainTerrain], [2]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 2),
@@ -143,7 +144,7 @@ for (let g = 0; g < scaleByMapSize(5, 30); ++g)
var p2 = 0;
for (let i = 0; i < numPlayers; ++i)
distances.push(Math.euclidDistance2D(tx, tz, fractionToTiles(playerX[i]), fractionToTiles(playerZ[i])));
distances.push(Math.euclidDistance2D(tx, tz, playerPosition[i].x, playerPosition[i].y));
for (let a = 0; a < numPlayers; ++a)
{
@@ -162,7 +163,7 @@ for (let g = 0; g < scaleByMapSize(5, 30); ++g)
}
createArea(
new PathPlacer(tx, tz, mapSize * playerX[p1], mapSize * playerZ[p1], scaleByMapSize(11, 17), 0.4, 3 * scaleByMapSize(1, 4), 0.1, 0.1),
new PathPlacer(tx, tz, playerPosition[p1].x, playerPosition[p1].y, scaleByMapSize(11, 17), 0.4, 3 * scaleByMapSize(1, 4), 0.1, 0.1),
[
new LayeredPainter([tMainTerrain, tMainTerrain], [3]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 3),
@@ -172,7 +173,7 @@ for (let g = 0; g < scaleByMapSize(5, 30); ++g)
if (numPlayers > 1)
createArea(
new PathPlacer(tx, tz, mapSize * playerX[p2], mapSize * playerZ[p2], scaleByMapSize(11, 17), 0.4, 3 * scaleByMapSize(1, 4), 0.1, 0.1),
new PathPlacer(tx, tz, playerPosition[p2].x, playerPosition[p2].y, scaleByMapSize(11, 17), 0.4, 3 * scaleByMapSize(1, 4), 0.1, 0.1),
[
new LayeredPainter([tMainTerrain, tMainTerrain], [3]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 3),
@@ -186,13 +187,13 @@ for (let i = 0; i < numPlayers; ++i)
{
log("Creating path from player to center and to neighbor...");
let neighbor = i + 1 < numPlayers ? i + 1 : 0;
for (let [x, z] of [[playerX[neighbor], playerZ[neighbor]], [0.5, 0.5]])
for (let position of [playerPosition[neighbor], mapCenter])
createArea(
new PathPlacer(
fractionToTiles(playerX[i]),
fractionToTiles(playerZ[i]),
fractionToTiles(x),
fractionToTiles(z),
playerPosition[i].x,
playerPosition[i].y,
position.x,
position.y,
scaleByMapSize(8, 13),
0.4,
3 * scaleByMapSize(1, 4),
@@ -214,7 +215,7 @@ createArea(
null);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass already marked above
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -72,7 +72,7 @@ createArea(
],
null);
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.25);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25));
log("Ensuring initial player land...");
for (let i = 0; i < numPlayers; ++i)
@@ -82,8 +82,8 @@ for (let i = 0; i < numPlayers; ++i)
Math.floor(scaleByMapSize(5, 9)),
Math.floor(scaleByMapSize(5, 20)),
1,
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i])),
playerPosition[i].x,
playerPosition[i].y,
0,
[Math.floor(scaleByMapSize(23, 50))]),
[
@@ -98,7 +98,7 @@ paintTerrainBasedOnHeight(1, 3, 0, tShore);
paintTerrainBasedOnHeight(-8, 1, 2, tWater);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -103,7 +103,7 @@ paintTerrainBasedOnHeight(2, 5, 1, tGrass);
paintTileClassBasedOnHeight(-6, 0.5, 1, clWater);
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(riverAngle, 0.6),
"PlayerPlacement": playerPlacementRiver(riverAngle, fractionToTiles(0.6)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -198,8 +198,7 @@ Engine.SetProgress(30);
log("Determining player locations...");
var playerIDs = sortAllPlayers();
var playerX = [];
var playerZ = [];
var playerPosition = [];
var playerAngle = [];
var p = 0;
for (let island = 0; island < 2; ++island)
@@ -209,14 +208,13 @@ for (let island = 0; island < 2; ++island)
for (let i = 0; i < playersPerIsland; ++i)
{
playerAngle[p] = Math.PI * ((i + 0.5) / (2 * playersPerIsland) + island) + swapAngle;
let pos = Vector2D.add(islandLocations[island], new Vector2D(radiusPlayer).rotate(-playerAngle[p]));
[playerX[p], playerZ[p]] = [tilesToFraction(pos.x), tilesToFraction(pos.y)];
playerPosition[p] = Vector2D.add(islandLocations[island], new Vector2D(radiusPlayer).rotate(-playerAngle[p]));
++p;
}
}
placePlayerBases({
"PlayerPlacement": [sortAllPlayers(), playerX, playerZ],
"PlayerPlacement": [sortAllPlayers(), playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": false,
@@ -46,6 +46,7 @@ InitMap();
const numPlayers = getNumPlayers();
const mapSize = getMapSize();
const mapCenter = getMapCenter();
var clCoral = createTileClass();
var clPlayer = createTileClass();
@@ -64,21 +65,27 @@ var clBaseResource = createTileClass();
var startingPlaces=[[0],[0,3],[0,2,4],[0,1,3,4],[0,1,2,3,4],[0,1,2,3,4,5]];
var startAngle = randomAngle();
var numIslands = Math.max(6, numPlayers);
var islandX = [];
var islandZ = [];
var islandRadius = scaleByMapSize(15, 40);
var islandCount = Math.max(6, numPlayers);
var islandPosition = distributePointsOnCircle(islandCount, startAngle, fractionToTiles(0.39), mapCenter)[0].map(position => position.round());
var centralIslandRadius = scaleByMapSize(15, 30);
var centralIslandCount = Math.floor(scaleByMapSize(1, 4));
var centralIslandPosition = new Array(numPlayers).fill(0).map((v, i) =>
Vector2D.add(mapCenter, new Vector2D(fractionToTiles(randFloat(0.1, 0.16)), 0).rotate(
-startAngle - Math.PI * (i * 2 / centralIslandCount + randFloat(-1, 1) / 8)).round()));
var areas = [];
var nPlayer = 0;
var playerX = [];
var playerZ = [];
var playerPosition = [];
function createCycladicArchipelagoIsland(ix, iz, tileClass, radius, coralRadius)
function createCycladicArchipelagoIsland(position, tileClass, radius, coralRadius)
{
log("Creating deep ocean rocks...");
createArea(
new ClumpPlacer(diskArea(radius + coralRadius), 0.7, 0.1, 10, ix, iz),
new ClumpPlacer(diskArea(radius + coralRadius), 0.7, 0.1, 10, position.x, position.y),
[
new LayeredPainter([tOceanRockDeep, tOceanCoral], [5]),
paintClass(clCoral)
@@ -88,7 +95,7 @@ function createCycladicArchipelagoIsland(ix, iz, tileClass, radius, coralRadius)
log("Creating island...");
areas.push(
createArea(
new ClumpPlacer(diskArea(radius), 0.7, 0.1, 10, ix, iz),
new ClumpPlacer(diskArea(radius), 0.7, 0.1, 10, position.x, position.y),
[
new LayeredPainter([tOceanCoral, tBeachWet, tBeachDry, tBeach, tBeachBlend, tGrass], [1, 3, 1, 1, 2]),
new SmoothElevationPainter(ELEVATION_SET, 3, 5),
@@ -98,45 +105,23 @@ function createCycladicArchipelagoIsland(ix, iz, tileClass, radius, coralRadius)
}
log("Creating player islands...");
for (let i = 0; i < numIslands; ++i)
for (let i = 0; i < islandCount; ++i)
{
let angle = startAngle + i * 2 * Math.PI / numIslands;
islandX[i] = 0.5 + 0.39 * Math.cos(angle);
islandZ[i] = 0.5 + 0.39 * Math.sin(angle);
// Determine which player resides on this island
let isPlayerIsland = numPlayers >= 6 || i == startingPlaces[numPlayers - 1][nPlayer];
if (isPlayerIsland)
{
[playerX[nPlayer], playerZ[nPlayer]] = [islandX[i], islandZ[i]];
playerPosition[nPlayer] = islandPosition[i];
++nPlayer;
}
createCycladicArchipelagoIsland(
Math.round(fractionToTiles(islandX[i])),
Math.round(fractionToTiles(islandZ[i])),
isPlayerIsland ? clPlayer : clIsland,
scaleByMapSize(15, 40),
scaleByMapSize(1, 5));
createCycladicArchipelagoIsland(islandPosition[i], isPlayerIsland ? clPlayer : clIsland, islandRadius, scaleByMapSize(1, 5));
}
log("Creating central islands...");
var nCenter = Math.floor(scaleByMapSize(1, 4));
for (let i = 0; i < nCenter; ++i)
{
let radius = randFloat(0.1, 0.16);
let angle = startAngle + Math.PI * (i * 2 / nCenter + randFloat(-1, 1) / 8);
createCycladicArchipelagoIsland(
Math.round(fractionToTiles(0.5 + radius * Math.cos(angle))),
Math.round(fractionToTiles(0.5 + radius * Math.sin(angle))),
clIsland,
scaleByMapSize(15, 30),
2);
}
for (let position of centralIslandPosition)
createCycladicArchipelagoIsland(position, clIsland, centralIslandRadius, 2);
placePlayerBases({
"PlayerPlacement": [sortAllPlayers(), playerX, playerZ],
"PlayerPlacement": [sortAllPlayers(), playerPosition],
// PlayerTileClass is clCity here and painted below
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -320,7 +320,7 @@ if (gallicCC)
Engine.SetProgress(10);
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(0, 0.6),
"PlayerPlacement": playerPlacementRiver(0, fractionToTiles(0.6)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": false,
@@ -67,7 +67,7 @@ for (var i=0; i < numPlayers; i++)
Engine.SetProgress(10);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerPosition.map(p => tilesToFraction(p.x)), playerPosition.map(p => tilesToFraction(p.y))],
"PlayerPlacement": [playerIDs, playerPosition],
"BaseResourceClass": clBaseResource,
// player class painted below
"CityPatch": {
@@ -12,7 +12,7 @@ Engine.SetProgress(10);
const teamsArray = getTeamsArray();
const startAngle = randomAngle();
addBases("stronghold", 0.37, 0.04, startAngle);
addBases("stronghold", fractionToTiles(0.37), fractionToTiles(0.04), startAngle);
Engine.SetProgress(20);
// Change the starting angle and add the players again
@@ -24,7 +24,7 @@ if (teamsArray.length == 2)
if (teamsArray.length == 4)
rotation = 5/4 * Math.PI;
addBases("stronghold", 0.15, 0.04, startAngle + rotation);
addBases("stronghold", fractionToTiles(0.15), fractionToTiles(0.04), startAngle + rotation);
Engine.SetProgress(40);
addElements(shuffleArray([
@@ -59,7 +59,7 @@ var landHeight = 3;
var waterHeight = -4;
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(Math.PI / 2, 0.6),
"PlayerPlacement": playerPlacementRiver(Math.PI / 2, fractionToTiles(0.6)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -71,15 +71,12 @@ var clRain = createTileClass();
var ccMountainHeight = 25;
var ccMountainSize = defaultPlayerBaseRadius();
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
log("Creating CC mountains...");
if (!isNomad())
for (let i = 0; i < numPlayers; ++i)
{
let ix = Math.round(fractionToTiles(playerX[i]));
let iz = Math.round(fractionToTiles(playerZ[i]));
// This one consists of many bumps, creating an omnidirectional ramp
createMountain(
ccMountainHeight,
@@ -87,15 +84,15 @@ if (!isNomad())
ccMountainSize,
Math.floor(scaleByMapSize(4, 10)),
undefined,
ix,
iz,
playerPosition[i].x,
playerPosition[i].y,
tHillDark,
clPlayer,
14);
// Flatten the initial CC area
createArea(
new ClumpPlacer(diskArea(ccMountainSize), 0.95, 0.6, 10, ix, iz),
new ClumpPlacer(diskArea(ccMountainSize), 0.95, 0.6, 10, playerPosition[i].x,playerPosition[i].y),
[
new LayeredPainter([tHillVeryDark, tHillMedium1], [ccMountainSize]),
new SmoothElevationPainter(ELEVATION_SET, ccMountainHeight, ccMountainSize),
@@ -106,7 +103,7 @@ if (!isNomad())
Engine.SetProgress(8);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass already marked above
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -78,7 +78,7 @@ createArea(
avoidClasses(clPlayer, 5));
log("Creating player islands...")
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.38);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.38));
for (let i = 0; i < numPlayers; ++i)
createArea(
@@ -87,17 +87,16 @@ for (let i = 0; i < numPlayers; ++i)
0.8,
0.1,
10,
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i]))),
playerPosition[i].x,
playerPosition[i].y),
[
new LayeredPainter([tShore, tMainTerrain], [shoreRadius]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, shoreRadius),
paintClass(clHill)
],
null);
]);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": false,
@@ -55,17 +55,21 @@ var clMetal = createTileClass();
var clFood = createTileClass();
var clBaseResource = createTileClass();
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var treasures = [
{ "template": oFoodTreasure, "distance": 5 },
{ "template": oWoodTreasure, "distance": 5 },
{ "template": oMetalTreasure, "distance": 3 },
{ "template": oMetalTreasure, "distance": 2 },
];
for (var i=0; i < numPlayers; i++)
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
for (let i = 0; i < numPlayers; ++i)
{
if (isNomad())
break;
log("Creating base for player " + playerIDs[i] + "...");
playerX[i] *= mapSize;
playerZ[i] *= mapSize;
for (let dist of [6, 8])
{
let ents = getStartingEntities(playerIDs[i]);
@@ -73,68 +77,41 @@ for (var i=0; i < numPlayers; i++)
if (dist == 8)
ents = ents.filter(ent => ent.Template.indexOf("female") != -1 || ent.Template.indexOf("infantry") != -1);
placeStartingEntities(playerX[i], playerZ[i], playerIDs[i], ents, dist);
placeStartingEntities(playerPosition[i], playerIDs[i], ents, dist);
}
// Create treasure
var bbAngle = BUILDING_ORIENTATION;
var bbDist = 10;
var bbX = Math.round(playerX[i] + bbDist * Math.cos(bbAngle));
var bbZ = Math.round(playerZ[i] + bbDist * Math.sin(bbAngle));
var group = new SimpleGroup(
[new SimpleObject(oFoodTreasure, 5,5, 0,2)],
true, clBaseResource, bbX, bbZ
);
createObjectGroup(group, 0);
log("Creating treasure for player " + playerIDs[i] + "...");
let bbAngle = BUILDING_ORIENTATION;
for (let treasure of treasures)
{
let position = Vector2D.add(playerPosition[i], new Vector2D(10, 0).rotate(-bbAngle))
createObjectGroup(new SimpleGroup([new SimpleObject(oFoodTreasure, 5, 5, 0, 2)], true, clBaseResource, position.x, position.y), 0);
bbAngle += Math.PI / 2;
}
bbAngle += Math.PI / 2;
var bbX = Math.round(playerX[i] + bbDist * Math.cos(bbAngle));
var bbZ = Math.round(playerZ[i] + bbDist * Math.sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oWoodTreasure, 5,5, 0,2)],
true, clBaseResource, bbX, bbZ
);
createObjectGroup(group, 0);
bbAngle += Math.PI / 2;
var bbX = Math.round(playerX[i] + bbDist * Math.cos(bbAngle));
var bbZ = Math.round(playerZ[i] + bbDist * Math.sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oMetalTreasure, 3,3, 0,2)],
true, clBaseResource, bbX, bbZ
);
createObjectGroup(group, 0);
bbAngle += Math.PI / 2;
var bbX = Math.round(playerX[i] + bbDist * Math.cos(bbAngle));
var bbZ = Math.round(playerZ[i] + bbDist * Math.sin(bbAngle));
group = new SimpleGroup(
[new SimpleObject(oStoneTreasure, 2,2, 0,2)],
true, clBaseResource, bbX, bbZ
);
createObjectGroup(group, 0);
// Base texture
log("Painting ground texture for player " + playerIDs[i] + "...");
var civ = getCivCode(playerIDs[i]);
var tilesSize = civ == "cart" ? 24 : 22;
const minBoundX = (playerX[i] > tilesSize ? playerX[i] - tilesSize : 0);
const minBoundY = (playerZ[i] > tilesSize ? playerZ[i] - tilesSize : 0);
const maxBoundX = (playerX[i] < mapSize - tilesSize ? playerX[i] + tilesSize : mapSize);
const maxBoundY = (playerZ[i] < mapSize - tilesSize ? playerZ[i] + tilesSize : mapSize);
const minBoundX = Math.max(0, playerPosition[i].x - tilesSize);
const minBoundY = Math.max(0, playerPosition[i].y - tilesSize);
const maxBoundX = Math.min(mapSize, playerPosition[i].x + tilesSize);
const maxBoundY = Math.min(mapSize, playerPosition[i].y + tilesSize);
for (var tx = minBoundX; tx < maxBoundX; ++tx)
for (var ty = minBoundY; ty < maxBoundY; ++ty)
{
var unboundSumOfXY = tx + ty - minBoundX - minBoundY;
if ((unboundSumOfXY > tilesSize) && (unboundSumOfXY < 3 * tilesSize) && (tx - ty + minBoundY - minBoundX < tilesSize) && (ty - tx - minBoundY + minBoundX < tilesSize))
if (unboundSumOfXY > tilesSize &&
unboundSumOfXY < tilesSize * 3 &&
tx - ty + minBoundY - minBoundX < tilesSize &&
ty - tx - minBoundY + minBoundX < tilesSize)
{
placeTerrain(Math.floor(tx), Math.floor(ty), tRoad);
addToClass(Math.floor(tx), Math.floor(ty), clPlayer);
placeTerrain(tx, ty, tRoad);
addToClass(tx, ty, clPlayer);
}
}
// Place custom fortress
log("Placing fortress for player " + playerIDs[i] + "...");
if (civ == "brit" || civ == "gaul" || civ == "iber")
{
var wall = ["gate", "tower", "long",
@@ -151,7 +128,7 @@ for (var i=0; i < numPlayers; i++)
"cornerIn", "long", "house", "tower", "long", "tower", "long",
"cornerIn", "long", "house", "tower"];
}
placeCustomFortress(playerX[i], playerZ[i], new Fortress("Spahbod", wall), civ, playerIDs[i], -Math.PI/4);
placeCustomFortress(playerPosition[i].x, playerPosition[i].y, new Fortress("Spahbod", wall), civ, playerIDs[i], -Math.PI/4);
}
log("Creating lakes...");
@@ -60,7 +60,7 @@ var clBaseResource = createTileClass();
initTerrain(tMainTerrain);
var [playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
log("Determining number of rivers between players...");
var split = 1;
@@ -176,7 +176,7 @@ paintTerrainBasedOnHeight(2, 21, 1, tMainTerrain);
paintTileClassBasedOnHeight(-6, 0.5, 1, clWater);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -59,17 +59,17 @@ var shallowHeight = -1.5;
var waterHeight = -3;
log("Create the continent body");
var continentCenter = new Vector2D(0.5, 0.7);
var continentCenter = new Vector2D(fractionToTiles(0.5), fractionToTiles(0.7)).round();
createArea(
new ChainPlacer(
2,
Math.floor(scaleByMapSize(5, 12)),
Math.floor(scaleByMapSize(60, 700)),
1,
Math.floor(fractionToTiles(continentCenter.x)),
Math.floor(fractionToTiles(continentCenter.y)),
continentCenter.x,
continentCenter.y,
0,
[Math.floor(mapSize * 0.49)]),
[Math.floor(fractionToTiles(0.49))]),
[
new LayeredPainter([tGrass, tGrass, tGrass], [4, 2]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 4),
@@ -79,9 +79,8 @@ createArea(
placePlayerBases({
"PlayerPlacement": [primeSortAllPlayers(), ...playerPlacementCustomAngle(
0.35,
continentCenter.x,
continentCenter.y,
fractionToTiles(0.35),
continentCenter,
i => Math.PI * (-0.46 / numPlayers * (i + i % 2) - (i % 2) / 2))],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
@@ -132,9 +132,8 @@ var startAngle = -Math.PI / 6;
placePlayerBases({
"PlayerPlacement": [sortAllPlayers(), ...playerPlacementCustomAngle(
0.35,
tilesToFraction(mapCenter.x),
tilesToFraction(mapCenter.y),
fractionToTiles(0.35),
mapCenter,
i => startAngle + 2/3 * Math.PI * (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1)))],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
@@ -14,7 +14,7 @@ Engine.SetProgress(10);
const mapSize = getMapSize();
const startAngle = randomAngle();
const players = addBases("radial", 0.38, 0.05, startAngle);
const players = addBases("radial", fractionToTiles(0.38), fractionToTiles(0.05), startAngle);
Engine.SetProgress(20);
addCenterLake();
@@ -12,7 +12,7 @@ Engine.SetProgress(10);
const teamsArray = getTeamsArray();
const startAngle = randomAngle();
addBases("line", 0.2, 0.08, startAngle);
addBases("line", fractionToTiles(0.2), fractionToTiles(0.08), startAngle);
Engine.SetProgress(20);
placeBarriers();
@@ -57,7 +57,7 @@ var clHighlands = createTileClass();
var highlandsPosition = fractionToTiles(0.25);
placePlayerBases({
"PlayerPlacement": playerPlacementLine(true, 0.5, 0.2),
"PlayerPlacement": playerPlacementLine(true, mapCenter, fractionToTiles(0.2)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -36,7 +36,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
// No city patch
@@ -101,19 +101,19 @@ for (let i = 0; i < teams.length; ++i)
for (let p = 0; p < teams[i].length; ++p)
{
let [playerAngle, fx, fz, ix, iz] = getPlayerTileCoordinates(p, i, fractionX, fractionZ);
let playerPosition = new Vector2D(ix, iz);
addCivicCenterAreaToClass(ix, iz, clPlayer);
addCivicCenterAreaToClass(playerPosition, clPlayer);
createArea(
new ChainPlacer(2, Math.floor(scaleByMapSize(5, 11)), Math.floor(scaleByMapSize(60, 250)), 1, ix, iz, 0, [Math.floor(mapSize * 0.01)]),
new ChainPlacer(2, Math.floor(scaleByMapSize(5, 11)), Math.floor(scaleByMapSize(60, 250)), 1, ix, iz, 0, [Math.floor(fractionToTiles(0.01))]),
[
new LayeredPainter([tMainTerrain, tMainTerrain, tMainTerrain], [1, shoreRadius]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, shoreRadius),
paintClass(clLand)
],
null);
]);
placeCivDefaultStartingEntities(fx, fz, teams[i][p], false);
placeCivDefaultStartingEntities(playerPosition, teams[i][p], false);
}
log("Create initial mines for team " + i);
@@ -148,8 +148,7 @@ for (let i = 0; i < teams.length; ++i)
placePlayerBaseChicken({
"playerID": teams[i][p],
"playerX": tilesToFraction(fx),
"playerZ": tilesToFraction(fz),
"playerPosition": new Vector2D(fx, fz),
"BaseResourceClass": clBaseResource,
"baseResourceConstraint": stayClasses(clLand, 5)
});
@@ -63,16 +63,15 @@ var clLand = createTileClass();
var landHeight = 3;
var playerIslandRadius = scaleByMapSize(20, 29);
var [playerIDs, playerX, playerZ, playerAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle] = playerPlacementCircle(fractionToTiles(0.35));
if (!isNomad())
{
log("Creating player islands and docks...");
for (let i = 0; i < numPlayers; i++)
{
let playerPos = new Vector2D(playerX[i], playerZ[i]).mult(mapSize);
createArea(
new ClumpPlacer(diskArea(playerIslandRadius), 0.8, 0.1, 10, playerPos.x, playerPos.y),
new ClumpPlacer(diskArea(playerIslandRadius), 0.8, 0.1, 10, playerPosition[i].x, playerPosition[i].y),
[
new LayeredPainter([tMainTerrain , tMainTerrain, tMainTerrain], [1, 6]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 6),
@@ -80,7 +79,7 @@ if (!isNomad())
paintClass(clPlayer)
]);
let dockLocation = findLocationInDirectionBasedOnHeight(playerPos, mapCenter, -3 , landHeight - 0.5, landHeight);
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , landHeight - 0.5, landHeight);
placeObject(dockLocation.x, dockLocation.y, oDock, playerIDs[i], playerAngle[i] + Math.PI);
}
}
@@ -119,7 +118,7 @@ paintTerrainBasedOnHeight(1, 3, 0, tShore);
paintTerrainBasedOnHeight(-8, 1, 2, tWater);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass marked above
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -54,7 +54,7 @@ var waterPosition = fractionToTiles(0.31);
var mountainPosition = fractionToTiles(0.69);
placePlayerBases({
"PlayerPlacement": playerPlacementLine(false, 0.55, 0.2),
"PlayerPlacement": playerPlacementLine(false, new Vector2D(fractionToTiles(0.55), mapCenter.y), fractionToTiles(0.2)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -57,14 +57,11 @@ var clBaseResource = createTileClass();
initTerrain(tMainTerrain);
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
log("Preventing water in player territory...");
for (let i = 0; i < numPlayers; ++i)
addCivicCenterAreaToClass(
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i])),
clPlayer);
addCivicCenterAreaToClass(playerPosition[i], clPlayer);
log("Creating the lake...")
createArea(
@@ -100,7 +97,7 @@ paintTerrainBasedOnHeight(-8, 1, 2, tWater);
paintTileClassBasedOnHeight(-6, 0, 1, clWater);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass marked above
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -55,8 +55,8 @@ InitMap();
const numPlayers = getNumPlayers();
const mapSize = getMapSize();
const mapCenter = getMapCenter();
// Create classes
var clWater = createTileClass();
var clCliff = createTileClass();
var clForest = createTileClass();
@@ -67,16 +67,15 @@ var clPlayer = createTileClass();
var clBaseResource = createTileClass();
log("Creating players...");
var [playerIDs, playerX, playerZ] = playerPlacementLine(false, 0.5, randFloat(0.42, 0.46));
var [playerIDs, playerPosition] = playerPlacementLine(false, mapCenter, fractionToTiles(randFloat(0.42, 0.46)));
function distanceToPlayers(x, z)
{
var r = 10000;
for (let i = 0; i < numPlayers; ++i)
{
var dx = x - playerX[i];
var dz = z - playerZ[i];
var dx = x - playerPosition[i].x;
var dz = z - playerPosition[i].y;
r = Math.min(r, Math.square(dx) + Math.square(dz));
}
return Math.sqrt(r);
@@ -306,7 +305,7 @@ for (var ix = 0; ix < mapSize; ix++)
Engine.SetProgress(30);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"baseResourceConstraint": avoidClasses(clCliff, 4),
@@ -21,7 +21,7 @@ const startAngle = randomAngle();
resetTerrain(topTerrain, g_TileClasses.land, hillHeight);
Engine.SetProgress(10);
addBases("radial", 0.4, randFloat(0.05, 0.1), startAngle);
addBases("radial", fractionToTiles(0.4), fractionToTiles(randFloat(0.05, 0.1)), startAngle);
Engine.SetProgress(20);
createSunkenTerrain();
@@ -56,7 +56,7 @@ var shallowHeight = -2;
var shallowWidth = scaleByMapSize(8, 12);
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(Math.PI / 2, 0.5),
"PlayerPlacement": playerPlacementRiver(Math.PI / 2, fractionToTiles(0.5)),
// PlayerTileClass marked below
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -53,7 +53,7 @@ var clBaseResource = createTileClass();
initTerrain(tMainTerrain);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -64,19 +64,16 @@ var landHeight = 3;
var startAngle = 4/7 * Math.PI;
var playerIDs = sortAllPlayers();
var [playerX, playerZ, playerAngle] = playerPlacementCustomAngle(
0.35,
tilesToFraction(mapCenter.x),
tilesToFraction(mapCenter.y),
var [playerPosition, playerAngle] = playerPlacementCustomAngle(
fractionToTiles(0.35),
mapCenter,
i => startAngle - 8/7 * Math.PI * (i + 1) / (numPlayers + 1));
log("Creating player islands and docks...");
for (let i = 0; i < numPlayers; ++i)
{
let playerPosition = new Vector2D(playerX[i], playerZ[i]).mult(mapSize).round();
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(15, 25)), 0.8, 0.1, 10, playerPosition.x, playerPosition.y),
new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.8, 0.1, 10, playerPosition[i].x, playerPosition[i].y),
[
new LayeredPainter([tWater, tShore, tMainTerrain], [1, 4]),
new SmoothElevationPainter(ELEVATION_SET, landHeight, 4),
@@ -87,13 +84,13 @@ for (let i = 0; i < numPlayers; ++i)
if (isNomad())
continue;
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition, mapCenter, -3 , 2.6, 3);
let dockLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 2.6, 3);
placeObject(dockLocation.x, dockLocation.y, oDock, playerIDs[i], playerAngle[i] + Math.PI);
}
Engine.SetProgress(10);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": false,
@@ -59,7 +59,7 @@ var hillHeight1 = 16;
var hillHeight2 = 16;
var hillHeight3 = 16;
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
if (!isNomad())
for (let i = 0; i < numPlayers; ++i)
@@ -69,12 +69,12 @@ if (!isNomad())
0.9,
0.5,
10,
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i]))),
playerPosition[i].x,
playerPosition[i].y),
paintClass(clPlayer));
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -6,7 +6,7 @@ if (isNomad())
placePlayersNomad(createTileClass());
else
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.39)
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.39))
});
ExportMap();
@@ -47,7 +47,7 @@ var clBaseResource = createTileClass();
var landHeight = getMapBaseHeight();
placePlayerBases({
"PlayerPlacement": playerPlacementLine(true, 0.45, 0.2),
"PlayerPlacement": playerPlacementLine(true, new Vector2D(fractionToTiles(0.45), mapCenter.y), fractionToTiles(0.2)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -54,15 +54,12 @@ var forestDistance = scaleByMapSize(6, 20);
initTerrain(tSand);
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
log("Creating small oasis near the players...")
var forestDist = 1.2 * defaultPlayerBaseRadius();
for (let i = 0; i < numPlayers; ++i)
{
let fx = fractionToTiles(playerX[i]);
let fz = fractionToTiles(playerZ[i]);
// Create starting batches of wood
let forestX = 0;
let forestY = 0;
@@ -70,8 +67,8 @@ for (let i = 0; i < numPlayers; ++i)
do {
forestAngle = Math.PI / 3 * randFloat(1, 2);
forestX = Math.round(fx + forestDist * Math.cos(forestAngle));
forestY = Math.round(fz + forestDist * Math.sin(forestAngle));
forestX = playerPosition[i].x + Math.round(forestDist * Math.cos(forestAngle));
forestY = playerPosition[i].y + Math.round(forestDist * Math.sin(forestAngle));
} while (
!createArea(
new ClumpPlacer(70, 1, 0.5, 10, forestX, forestY),
@@ -117,7 +114,7 @@ for (let i = 0; i < numPlayers; ++i)
Engine.SetProgress(20);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -65,7 +65,7 @@ var clCP = createTileClass();
initTerrain(tDirtMain);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"BaseResourceClass": clBaseResource,
"CityPatch": {
"outerTerrain": tCity,
@@ -43,6 +43,7 @@ InitMap();
const numPlayers = getNumPlayers();
const mapSize = getMapSize();
const mapCenter = getMapCenter();
const mapBounds = getMapBounds();
var clPlayer = createTileClass();
@@ -62,7 +63,7 @@ var shoreHeight = -1.5;
var landHeight = getMapBaseHeight();
placePlayerBases({
"PlayerPlacement": playerPlacementLine(false, 0.76, 0.2),
"PlayerPlacement": playerPlacementLine(false, new Vector2D(fractionToTiles(0.76), mapCenter.y), fractionToTiles(0.2)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -43,7 +43,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
var clArcticWolf = createTileClass();
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
var treasures = [{
"template": oWoodTreasure,
@@ -54,13 +54,13 @@ log("Creating player markets...");
if (!isNomad())
for (let i = 0; i < numPlayers; ++i)
{
let marketPos = Vector2D.add(new Vector2D(playerX[i], playerZ[i]).mult(mapSize), new Vector2D(12, 0).rotate(randomAngle())).round();
let marketPos = Vector2D.add(playerPosition[i], new Vector2D(12, 0).rotate(randomAngle())).round();
placeObject(marketPos.x, marketPos.y, oMarket, playerIDs[i], BUILDING_ORIENTATION);
addCivicCenterAreaToClass(marketPos.x, marketPos.y, clBaseResource);
addCivicCenterAreaToClass(marketPos, clBaseResource);
}
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -278,13 +278,12 @@ else
if (isNomad())
{
let constraint = avoidClasses(clWater, 4, clMetal, 4, clRock, 4, clHill, 4, clFood, 2);
[playerIDs, playerX, playerZ] = placePlayersNomad(clPlayer, constraint);
[playerIDs, playerPosition] = placePlayersNomad(clPlayer, constraint);
for (let i = 0; i < numPlayers; ++i)
placePlayerBaseTreasures({
"playerID": playerIDs[i],
"playerX": tilesToFraction(playerX[i]),
"playerZ": tilesToFraction(playerZ[i]),
"playerPosition": playerPosition[i],
"BaseResourceClass": clBaseResource,
"baseResourceConstraint": constraint,
"types": treasures
@@ -106,10 +106,9 @@ for (var ix = 0; ix < mapSize; ix++)
placePlayerBases({
"PlayerPlacement": [primeSortAllPlayers(), ...playerPlacementCustomAngle(
0.35,
tilesToFraction(mapCenter.x),
tilesToFraction(mapCenter.y),
i => oceanAngle + Math.PI * (i % 2 ? 1 : -1) * ((1/2 + 1/3 * (2/numPlayers * (i + 1 - i % 2) - 1))))],
fractionToTiles(0.35),
mapCenter,
i => oceanAngle + Math.PI * (i % 2 ? 1 : -1) * ((1/2 + 1/3 * (2/numPlayers * (i + 1 - i % 2) - 1))))],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -48,7 +48,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -42,7 +42,7 @@ const pForestP2 = [tForestFloor + TERRAIN_SEPARATOR + oPalm2, tForestFloor];
InitMap();
const numPlayers = getNumPlayers();
const mapSize = getMapSize();
const mapCenter = getMapCenter();
var clPlayer = createTileClass();
var clPlayerTerritory = createTileClass();
@@ -84,8 +84,8 @@ for (let i = 0; i < stripWidths.length; ++i)
Math.floor(scaleByMapSize(3, connectPlayers && isPlayerStrip ? 8 : 7)),
Math.floor(scaleByMapSize(30, 60)),
1,
Math.floor(randFloat(...stripWidths[i]) * mapSize),
Math.floor(randFloat(0, 1) * mapSize)),
Math.floor(fractionToTiles(randFloat(...stripWidths[i]))),
Math.floor(fractionToTiles(randFloat(0, 1)))),
[
new LayeredPainter([tGrass, tGrass], [2]),
new SmoothElevationPainter(ELEVATION_SET, 3, 3),
@@ -95,7 +95,7 @@ for (let i = 0; i < stripWidths.length; ++i)
}
Engine.SetProgress(20);
var [playerIDs, playerX, playerZ] = playerPlacementLine(false, 0.5, 1 - stripWidthsLeft[2][0] - stripWidthsLeft[2][1]);
var [playerIDs, playerPosition] = playerPlacementLine(false, mapCenter, fractionToTiles(1 - stripWidthsLeft[2][0] - stripWidthsLeft[2][1]));
// Either left vs right or top vs bottom
playerIDs = randBool() ? sortAllPlayers() : primeSortAllPlayers();
@@ -104,7 +104,7 @@ log("Ensuring player territory...");
var playerRadius = scaleByMapSize(12, 20);
for (let i = 0; i < numPlayers; ++i)
createArea(
new ChainPlacer(1, 6, 40, 1, Math.round(fractionToTiles(playerX[i])), Math.round(fractionToTiles(playerZ[i])), 0, [Math.floor(playerRadius)]),
new ChainPlacer(1, 6, 40, 1, playerPosition[i].x, playerPosition[i].y, 0, [Math.floor(playerRadius)]),
[
new LayeredPainter([tGrass, tGrass, tGrass], [1, 4]),
new SmoothElevationPainter(ELEVATION_SET, 3, 4),
@@ -112,7 +112,7 @@ for (let i = 0; i < numPlayers; ++i)
]);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"Walls": "towers",
@@ -69,10 +69,10 @@ var shallowHeight = -1;
initTerrain(tMainTerrain);
var [playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -36,17 +36,18 @@ function getStartingEntities(playerID)
/**
* Places the given entities at the given location (typically a civic center and starting units).
* @param location - A Vector2D specifying tile coordinates.
* @param civEntities - An array of objects with the Template property and optionally a Count property.
* The first entity is placed in the center, the other ones surround it.
*/
function placeStartingEntities(fx, fz, playerID, civEntities, dist = 6, orientation = BUILDING_ORIENTATION)
function placeStartingEntities(location, playerID, civEntities, dist = 6, orientation = BUILDING_ORIENTATION)
{
// Place the central structure
let i = 0;
let firstTemplate = civEntities[i].Template;
if (firstTemplate.startsWith("structures/"))
{
placeObject(fx, fz, firstTemplate, playerID, orientation);
placeObject(location.x, location.y, firstTemplate, playerID, orientation);
++i;
}
@@ -59,8 +60,8 @@ function placeStartingEntities(fx, fz, playerID, civEntities, dist = 6, orientat
for (let num = 0; num < count; ++num)
placeObject(
fx + dist * Math.cos(angle) + space * (-num + 0.75 * Math.floor(count / 2)) * Math.sin(angle),
fz + dist * Math.sin(angle) + space * (num - 0.75 * Math.floor(count / 2)) * Math.cos(angle),
location.x + dist * Math.cos(angle) + space * (-num + 0.75 * Math.floor(count / 2)) * Math.sin(angle),
location.y + dist * Math.sin(angle) + space * (num - 0.75 * Math.floor(count / 2)) * Math.cos(angle),
civEntities[j].Template,
playerID,
angle);
@@ -70,10 +71,10 @@ function placeStartingEntities(fx, fz, playerID, civEntities, dist = 6, orientat
/**
* Places the default starting entities as defined by the civilization definition, optionally including city walls.
*/
function placeCivDefaultStartingEntities(x, z, playerID, wallType, dist = 6, orientation = BUILDING_ORIENTATION)
function placeCivDefaultStartingEntities(location, playerID, wallType, dist = 6, orientation = BUILDING_ORIENTATION)
{
placeStartingEntities(x, z, playerID, getStartingEntities(playerID), dist, orientation);
placeStartingWalls(x, z, playerID, wallType, orientation);
placeStartingEntities(location, playerID, getStartingEntities(playerID), dist, orientation);
placeStartingWalls(location.x, location.y, playerID, wallType, orientation);
}
/**
@@ -97,13 +98,12 @@ function placeStartingWalls(x, z, playerID, wallType, orientation = BUILDING_ORI
*/
function placePlayerBases(playerBaseArgs)
{
let [playerIDs, playerX, playerZ] = playerBaseArgs.PlayerPlacement;
let [playerIDs, playerPosition] = playerBaseArgs.PlayerPlacement;
for (let i = 0; i < getNumPlayers(); ++i)
{
playerBaseArgs.playerID = playerIDs[i];
playerBaseArgs.playerX = playerX[i];
playerBaseArgs.playerZ = playerZ[i];
playerBaseArgs.playerPosition = playerPosition[i];
placePlayerBase(playerBaseArgs);
}
}
@@ -118,13 +118,10 @@ function placePlayerBase(playerBaseArgs)
log("Creating base for player " + playerBaseArgs.playerID + "...");
let fx = fractionToTiles(playerBaseArgs.playerX);
let fz = fractionToTiles(playerBaseArgs.playerZ);
placeCivDefaultStartingEntities(fx, fz, playerBaseArgs.playerID, playerBaseArgs.Walls !== undefined ? playerBaseArgs.Walls : true);
placeCivDefaultStartingEntities(playerBaseArgs.playerPosition, playerBaseArgs.playerID, playerBaseArgs.Walls !== undefined ? playerBaseArgs.Walls : true);
if (playerBaseArgs.PlayerTileClass !== undefined)
addCivicCenterAreaToClass(Math.round(fx), Math.round(fz), playerBaseArgs.PlayerTileClass);
addCivicCenterAreaToClass(playerBaseArgs.playerPosition, playerBaseArgs.PlayerTileClass);
for (let functionID of g_PlayerBaseFunctions)
{
@@ -139,7 +136,7 @@ function placePlayerBase(playerBaseArgs)
let args = playerBaseArgs[functionID];
// Copy some global arguments to the arguments for each function
for (let prop of ["playerID", "playerX", "playerZ", "BaseResourceClass", "baseResourceConstraint"])
for (let prop of ["playerID", "playerPosition", "BaseResourceClass", "baseResourceConstraint"])
args[prop] = playerBaseArgs[prop];
func(args);
@@ -155,15 +152,17 @@ function defaultPlayerBaseRadius()
* Marks the corner and center tiles of an area that is about the size of a Civic Center with the given TileClass.
* Used to prevent resource collisions with the Civic Center.
*/
function addCivicCenterAreaToClass(ix, iz, tileClass)
function addCivicCenterAreaToClass(position, tileClass)
{
addToClass(ix, iz, tileClass);
let pos = position.clone().round();
addToClass(ix, iz + 5, tileClass);
addToClass(ix, iz - 5, tileClass);
addToClass(pos.x, pos.y, tileClass);
addToClass(ix + 5, iz, tileClass);
addToClass(ix - 5, iz, tileClass);
addToClass(pos.x, pos.y + 5, tileClass);
addToClass(pos.x, pos.y - 5, tileClass);
addToClass(pos.x + 5, pos.y, tileClass);
addToClass(pos.x - 5, pos.y, tileClass);
}
/**
@@ -178,7 +177,7 @@ function getPlayerBaseArgs(playerBaseArgs)
return [
(property, defaultVal) => playerBaseArgs[property] === undefined ? defaultVal : playerBaseArgs[property],
new Vector2D(playerBaseArgs.playerX, playerBaseArgs.playerZ).mult(getMapSize()),
playerBaseArgs.playerPosition,
baseResourceConstraint
];
}
@@ -420,8 +419,7 @@ function placePlayersNomad(playerClass, constraints)
let numPlayers = getNumPlayers();
let playerIDs = shuffleArray(sortAllPlayers());
let playerX = [];
let playerZ = [];
let playerPosition = [];
for (let i = 0; i < numPlayers; ++i)
{
@@ -450,8 +448,7 @@ function placePlayersNomad(playerClass, constraints)
if (createObjectGroups(group, playerIDs[i], new AndConstraint([constraint, avoidClasses(playerClass, distance * distanceFactor)]), 1, 200, false))
{
success = true;
playerX[i] = group.x;
playerZ[i] = group.z;
playerPosition[i] = new Vector2D(group.x, group.z);
break;
}
}
@@ -459,7 +456,7 @@ function placePlayersNomad(playerClass, constraints)
throw new Error("Could not place starting units for player " + playerIDs[i] + "!");
}
return [playerIDs, playerX, playerZ];
return [playerIDs, playerPosition];
}
/**
@@ -511,21 +508,20 @@ function primeSortAllPlayers()
/**
* Determine player starting positions on a circular pattern.
*/
function playerPlacementCircle(radius, startingAngle = undefined, centerX = 0.5, centerZ = 0.5)
function playerPlacementCircle(radius, startingAngle = undefined, center = undefined)
{
let startAngle = startingAngle !== undefined ? startingAngle : randomAngle();
let [locations, playerAngle] = distributePointsOnCircle(getNumPlayers(), startAngle, radius, new Vector2D(centerX, centerZ));
return [sortAllPlayers(), locations.map(l => l.x), locations.map(l => l.y), playerAngle, startAngle];
let [playerPosition, playerAngle] = distributePointsOnCircle(getNumPlayers(), startAngle, radius, center || getMapCenter());
return [sortAllPlayers(), playerPosition.map(p => p.round()), playerAngle, startAngle];
}
/**
* Determine player starting positions on a circular pattern, with a custom angle for each player.
* Commonly used for gulf terrains.
*/
function playerPlacementCustomAngle(radius, centerX, centerZ, playerAngleFunc)
function playerPlacementCustomAngle(radius, center, playerAngleFunc)
{
let playerX = [];
let playerZ = [];
let playerPosition = [];
let playerAngle = [];
let numPlayers = getNumPlayers();
@@ -533,11 +529,10 @@ function playerPlacementCustomAngle(radius, centerX, centerZ, playerAngleFunc)
for (let i = 0; i < numPlayers; ++i)
{
playerAngle[i] = playerAngleFunc(i);
playerX[i] = centerX + radius * Math.cos(playerAngle[i]);
playerZ[i] = centerZ + radius * Math.sin(playerAngle[i]);
playerPosition[i] = Vector2D.add(center, new Vector2D(radius, 0).rotate(-playerAngle[i])).round();
}
return [playerX, playerZ, playerAngle];
return [playerPosition, playerAngle];
}
/**
@@ -545,14 +540,13 @@ function playerPlacementCustomAngle(radius, centerX, centerZ, playerAngleFunc)
* If there are two teams with an equal number of players, each team will occupy exactly one line.
* Angle 0 means the players are placed in north to south direction, i.e. along the Z axis.
*/
function playerPlacementRiver(angle, width)
function playerPlacementRiver(angle, width, center = undefined)
{
let positions = [];
let numPlayers = getNumPlayers();
let numPlayersEven = numPlayers % 2 == 0;
let mapCenter = new Vector2D(0.5, 0.5);
let mapSize = getMapSize();
let centerPosition = center || getMapCenter();
let playerPosition = [];
for (let i = 0; i < numPlayers; ++i)
{
@@ -561,12 +555,13 @@ function playerPlacementRiver(angle, width)
let offsetDivident = numPlayersEven || currentPlayerEven ? (i + 1) % 2 : 0;
let offsetDivisor = numPlayersEven ? 0 : currentPlayerEven ? +1 : -1;
positions[i] = new Vector2D(
width * (i % 2) + (1 - width) / 2,
((i - 1 + offsetDivident) / 2 + 1) / ((numPlayers + offsetDivisor) / 2 + 1)).rotateAround(angle, mapCenter);
playerPosition[i] = new Vector2D(
width * (i % 2) + (mapSize - width) / 2,
fractionToTiles(((i - 1 + offsetDivident) / 2 + 1) / ((numPlayers + offsetDivisor) / 2 + 1))
).rotateAround(angle, centerPosition).round();
}
return [primeSortAllPlayers(), positions.map(p => p.x), positions.map(p => p.y)];
return [primeSortAllPlayers(), playerPosition];
}
/***
@@ -576,20 +571,17 @@ function playerPlacementRiver(angle, width)
*/
function playerPlacementLine(horizontal, center, width)
{
let playerX = [];
let playerZ = [];
let playerPosition = [];
let numPlayers = getNumPlayers();
for (let i = 0; i < numPlayers; ++i)
{
playerX[i] = (i + 1) / (numPlayers + 1);
playerZ[i] = center + width * (i % 2 - 1/2);
playerPosition[i] = Vector2D.add(
center,
new Vector2D(
fractionToTiles((i + 1) / (numPlayers + 1) - 0.5),
width * (i % 2 - 1/2))).rotateAround(horizontal ? 0 : Math.PI / 2, center).round();
if (!horizontal)
[playerX[i], playerZ[i]] = [playerZ[i], playerX[i]];
}
return [sortAllPlayers(), playerX, playerZ];
return [sortAllPlayers(), playerPosition];
}
/**
@@ -166,8 +166,7 @@ function createBase(player, walls = true)
{
placePlayerBase({
"playerID": player.id,
"playerX": player.x,
"playerZ": player.z,
"playerPosition": player.position,
"PlayerTileClass": g_TileClasses.player,
"BaseResourceClass": g_TileClasses.baseResource,
"Walls": getMapSize() > 192 && walls,
@@ -254,8 +253,8 @@ function randomStartingPositionPattern(teamsArray)
return {
"setup": pickRandom(formats),
"distance": randFloat(0.2, 0.35),
"separation": randFloat(0.05, 0.1)
"distance": fractionToTiles(randFloat(0.2, 0.35)),
"separation": fractionToTiles(randFloat(0.05, 0.1))
};
}
@@ -271,23 +270,24 @@ function randomStartingPositionPattern(teamsArray)
*/
function placeLine(teamsArray, distance, groupedDistance, startAngle)
{
var players = [];
let players = [];
let mapCenter = getMapCenter();
let dist = fractionToTiles(0.45);
for (let i = 0; i < teamsArray.length; ++i)
{
var safeDist = distance;
if (distance + teamsArray[i].length * groupedDistance > 0.45)
safeDist = 0.45 - teamsArray[i].length * groupedDistance;
if (distance + teamsArray[i].length * groupedDistance > dist)
safeDist = dist - teamsArray[i].length * groupedDistance;
var teamAngle = startAngle + (i + 1) * 2 * Math.PI / teamsArray.length;
// Create player base
for (var p = 0; p < teamsArray[i].length; ++p)
for (let p = 0; p < teamsArray[i].length; ++p)
{
players[teamsArray[i][p]] = {
"id": teamsArray[i][p],
"x": 0.5 + (safeDist + p * groupedDistance) * Math.cos(teamAngle),
"z": 0.5 + (safeDist + p * groupedDistance) * Math.sin(teamAngle)
"position": Vector2D.add(mapCenter, new Vector2D(safeDist + p * groupedDistance, 0).rotate(-teamAngle)).round()
};
createBase(players[teamsArray[i][p]], false);
}
@@ -305,6 +305,7 @@ function placeLine(teamsArray, distance, groupedDistance, startAngle)
*/
function placeRadial(playerIDs, distance, startAngle)
{
let mapCenter = getMapCenter();
let players = [];
let numPlayers = getNumPlayers();
@@ -313,8 +314,7 @@ function placeRadial(playerIDs, distance, startAngle)
let angle = startAngle + i * 2 * Math.PI / numPlayers;
players[i] = {
"id": playerIDs[i],
"x": 0.5 + distance * Math.cos(angle),
"z": 0.5 + distance * Math.sin(angle)
"position": Vector2D.add(mapCenter, new Vector2D(distance, 0).rotate(-angle)).round()
};
createBase(players[i]);
}
@@ -330,19 +330,14 @@ function placeRandom(playerIDs)
var locations = [];
var attempts = 0;
var resets = 0;
var mapCenter = getMapCenter();
for (let i = 0; i < getNumPlayers(); ++i)
{
var playerAngle = randomAngle();
// Distance from the center of the map in percent
// Mapsize being used as a diameter, so 0.5 is the edge of the map
var distance = randFloat(0, 0.42);
var x = 0.5 + distance * Math.cos(playerAngle);
var z = 0.5 + distance * Math.sin(playerAngle);
let position = Vector2D.add(mapCenter, new Vector2D(fractionToTiles(randFloat(0, 0.42)), 0).rotate(randomAngle())).round();
// Minimum distance between initial bases must be a quarter of the map diameter
if (locations.some(loc => Math.euclidDistance2D(x, z, loc.x, loc.z) < 0.25))
if (locations.some(loc => loc.distanceTo(position) < fractionToTiles(0.25)))
{
--i;
++attempts;
@@ -362,10 +357,7 @@ function placeRandom(playerIDs)
continue;
}
locations[i] = {
"x": x,
"z": z
};
locations[i] = position;
}
let players = groupPlayersByLocations(playerIDs, locations);
@@ -379,7 +371,7 @@ function placeRandom(playerIDs)
* Pick locations from the given set so that teams end up grouped.
*
* @param {Array} playerIDs - sorted by teams.
* @param {Array} locations - array of x/z pairs of possible starting locations.
* @param {Array} locations - array of Vector2D of possible starting locations.
*/
function groupPlayersByLocations(playerIDs, locations)
{
@@ -390,7 +382,7 @@ function groupPlayersByLocations(playerIDs, locations)
// Of all permutations of starting locations, find the one where
// the sum of the distances between allies is minimal, weighted by teamsize.
heapsPermute(shuffleArray(locations).slice(0, playerIDs.length), function(permutation)
heapsPermute(shuffleArray(locations).slice(0, playerIDs.length), v => v.clone(), permutation =>
{
let dist = 0;
let teamDist = 0;
@@ -401,9 +393,8 @@ function groupPlayersByLocations(playerIDs, locations)
let team1 = g_MapSettings.PlayerData[playerIDs[i - 1]].Team;
let team2 = g_MapSettings.PlayerData[playerIDs[i]].Team;
++teamSize;
if (team1 != -1 && team1 == team2)
teamDist += Math.euclidDistance2D(permutation[i - 1].x, permutation[i - 1].z, permutation[i].x, permutation[i].z);
teamDist += permutation[i - 1].distanceTo(permutation[i]);
else
{
dist += teamDist / teamSize;
@@ -424,11 +415,11 @@ function groupPlayersByLocations(playerIDs, locations)
let players = [];
for (let i = 0; i < playerIDs.length; ++i)
{
let player = minLocations[i];
player.id = playerIDs[i];
players.push(player);
}
players[i] = {
"id": playerIDs[i],
"position": minLocations[i]
};
return players;
}
@@ -443,21 +434,21 @@ function groupPlayersByLocations(playerIDs, locations)
function placeStronghold(teamsArray, distance, groupedDistance, startAngle)
{
var players = [];
var mapCenter = getMapCenter();
for (let i = 0; i < teamsArray.length; ++i)
{
var teamAngle = startAngle + (i + 1) * 2 * Math.PI / teamsArray.length;
var fractionX = 0.5 + distance * Math.cos(teamAngle);
var fractionZ = 0.5 + distance * Math.sin(teamAngle);
var teamPosition = Vector2D.add(mapCenter, new Vector2D(distance, 0).rotate(-teamAngle));
var teamGroupDistance = groupedDistance;
// If we have a team of above average size, make sure they're spread out
if (teamsArray[i].length > 4)
teamGroupDistance = Math.max(0.08, groupedDistance);
teamGroupDistance = Math.max(fractionToTiles(0.08), groupedDistance);
// If we have a solo player, place them on the center of the team's location
if (teamsArray[i].length == 1)
teamGroupDistance = 0;
teamGroupDistance = fractionToTiles(0);
// TODO: Ensure players are not placed outside of the map area, similar to placeLine
@@ -467,8 +458,7 @@ function placeStronghold(teamsArray, distance, groupedDistance, startAngle)
var angle = startAngle + (p + 1) * 2 * Math.PI / teamsArray[i].length;
players[teamsArray[i][p]] = {
"id": teamsArray[i][p],
"x": fractionX + teamGroupDistance * Math.cos(angle),
"z": fractionZ + teamGroupDistance * Math.sin(angle)
"position": Vector2D.add(teamPosition, new Vector2D(teamGroupDistance, 0).rotate(-angle)).round()
};
createBase(players[teamsArray[i][p]], false);
}
@@ -47,10 +47,10 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
var clTreasure = createTileClass();
var [playerIDs, playerX, playerZ, playerAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle] = playerPlacementCircle(fractionToTiles(0.35));
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -40,7 +40,7 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -55,10 +55,10 @@ var clShallows = createTileClass();
var waterHeight = -4;
var shallowHeight = -2;
var [playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -124,8 +124,8 @@ for (let i = 0; i < numPlayers; ++i)
log("Creating shallows between neighbors...");
createPassage({
"start": new Vector2D(playerX[i], playerZ[i]).mult(mapSize).round(),
"end": new Vector2D(playerX[neighborID], playerZ[neighborID]).mult(mapSize).round(),
"start": playerPosition[i],
"end": playerPosition[neighborID],
"startWidth": 10,
"endWidth": 10,
"smoothWidth": 4,
@@ -136,6 +136,7 @@ for (let i = 0; i < numPlayers; ++i)
});
log("Creating animals in shallows...");
let shallowPosition = Vector2D.average([playerPosition[i], playerPosition[neighbor]]).round();
let objects = [
new SimpleObject(oWildebeest, 5, 6, 0, 4),
new SimpleObject(oElephant, 2, 3, 0, 4)
@@ -146,8 +147,8 @@ for (let i = 0; i < numPlayers; ++i)
[object],
true,
clFood,
Math.round(fractionToTiles(playerX[i] + playerX[neighborID]) / 2),
Math.round(fractionToTiles(playerZ[i] + playerZ[neighborID]) / 2)),
shallowPosition.x,
shallowPosition.y),
0);
}
@@ -152,7 +152,7 @@ for (let i = 0; i < numPlayers; ++i)
}
placePlayerBases({
"PlayerPlacement": [sortAllPlayers(), playerPosition.map(p => tilesToFraction(p.x)), playerPosition.map(p => tilesToFraction(p.y))],
"PlayerPlacement": [sortAllPlayers(), playerPosition],
"BaseResourceClass": clBaseResource,
"Walls": false,
// player class painted below
@@ -60,7 +60,7 @@ const islandBetweenPlayerAndCenterDist = 0.16;
const islandBetweenPlayerAndCenterRadius = 0.81;
const centralIslandRadius = 0.36;
var [playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
var numIslands = 0;
var isConnected = [];
@@ -213,12 +213,12 @@ else
log("Creating player islands...");
for (let i = 0; i < numPlayers; ++i)
{
islandPos[i] = new Vector2D(playerX[i], playerZ[i]).mult(mapSize).round();
islandPos[i] = playerPosition[i];
createIsland(i, 1, isNomad() ? clLand : clPlayer);
}
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass already marked above
"BaseResourceClass": clBaseResource,
"baseResourceConstraint": stayClasses(clPlayer, 4),
@@ -10,7 +10,7 @@ initTileClasses();
resetTerrain(g_Terrains.mainTerrain, g_TileClasses.land, getMapBaseHeight());
Engine.SetProgress(20);
addBases("stronghold", randFloat(0.2, 0.35), randFloat(0.05, 0.1), randomAngle());
addBases("stronghold", fractionToTiles(randFloat(0.2, 0.35)), fractionToTiles(randFloat(0.05, 0.1)), randomAngle());
Engine.SetProgress(30);
addElements(shuffleArray([
@@ -66,7 +66,7 @@ createArea(
]);
Engine.SetProgress(10);
var [playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.3);
var [playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.3));
var halfway = distributePointsOnCircle(numPlayers, startAngle, fractionToTiles(0.375), mapCenter)[0].map(v => v.round());
var attacker = distributePointsOnCircle(numPlayers, startAngle, fractionToTiles(0.45), mapCenter)[0].map(v => v.round());
var passage = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPlayers, fractionToTiles(0.5), mapCenter)[0];
@@ -74,14 +74,11 @@ var passage = distributePointsOnCircle(numPlayers, startAngle + Math.PI / numPla
log("Creating player bases and attacker points...");
for (let i = 0; i < numPlayers; ++i)
{
let playerPos = new Vector2D(playerX[i], playerZ[i]).mult(mapSize).round();
placeStartingEntities(playerPos.x, playerPos.y, playerIDs[i], getStartingEntities(playerIDs[i]).filter(ent =>
placeStartingEntities(playerPosition[i], playerIDs[i], getStartingEntities(playerIDs[i]).filter(ent =>
ent.Template.indexOf("civil_centre") != -1 || ent.Template.indexOf("infantry") != -1));
placePlayerBaseDecoratives({
"playerX": playerX[i],
"playerZ": playerZ[i],
"playerPosition": playerPosition[i],
"template": aGrassShort,
"BaseResourceClass": clBaseResource
});
@@ -95,7 +92,7 @@ for (let i = 0; i < numPlayers; ++i)
]);
log("Placing treasure seeker woman...");
let femaleLocation = findLocationInDirectionBasedOnHeight(playerPos, mapCenter, -3 , 3.5, 3).round();
let femaleLocation = findLocationInDirectionBasedOnHeight(playerPosition[i], mapCenter, -3 , 3.5, 3).round();
addToClass(femaleLocation.x, femaleLocation.y, clWomen);
placeObject(femaleLocation.x, femaleLocation.y, oTreasureSeeker, playerIDs[i], playerAngle[i] + Math.PI);
@@ -104,7 +101,7 @@ for (let i = 0; i < numPlayers; ++i)
placeObject(attacker[i].x, attacker[i].y, triggerPointAttacker, playerIDs[i], Math.PI / 2);
log("Preventing mountains in the area between player and attackers...");
addCivicCenterAreaToClass(playerPos.x, playerPos.y, clPlayer);
addCivicCenterAreaToClass(playerPosition[i], clPlayer);
addToClass(attacker[i].x, attacker[i].y, clPlayer);
addToClass(halfway[i].x, halfway[i].y, clPlayer);
}
@@ -33,6 +33,7 @@ const pForestT = [tForestFloor1 + TERRAIN_SEPARATOR + oTamarix,tForestFloor2];
InitMap();
const mapCenter = getMapCenter();
const numPlayers = getNumPlayers();
var clPlayer = createTileClass();
@@ -44,17 +45,14 @@ var clFood = createTileClass();
var clBaseResource = createTileClass();
var clGrass = createTileClass();
var [playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
var [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
for (let i = 0; i < numPlayers; ++i)
{
let ix = Math.round(fractionToTiles(playerX[i]));
let iz = Math.round(fractionToTiles(playerZ[i]));
log("Marking player territory larger than the city patch...");
if (!isNomad())
createArea(
new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.9, 0.5, 10, ix, iz),
new ClumpPlacer(diskArea(defaultPlayerBaseRadius()), 0.9, 0.5, 10, playerPosition[i].x, playerPosition[i].y),
paintClass(clPlayer));
log("Creating big grass patches surrounding the city patches...");
@@ -64,8 +62,8 @@ for (let i = 0; i < numPlayers; ++i)
Math.floor(scaleByMapSize(5, 12)),
Math.floor(scaleByMapSize(25, 60)) / (isNomad() ? 2 : 1),
1,
ix,
iz,
playerPosition[i].x,
playerPosition[i].y,
0,
[Math.floor(scaleByMapSize(16, 30))]),
[
@@ -76,7 +74,7 @@ for (let i = 0; i < numPlayers; ++i)
Engine.SetProgress(10);
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
// PlayerTileClass marked above
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -67,7 +67,7 @@ var clTreasure = createTileClass();
var desertWidth = fractionToTiles(0.25);
placePlayerBases({
"PlayerPlacement": playerPlacementRiver(0, 0.4),
"PlayerPlacement": playerPlacementRiver(0, fractionToTiles(0.4)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -102,8 +102,7 @@ var unknownMapFunctions = {
* 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 playerX = [];
var playerZ = [];
var playerPosition = [];
var g_StartingTreasures = false;
var g_StartingWalls = true;
@@ -134,10 +133,10 @@ function unknownArchipelago()
g_StartingWalls = "towers";
g_StartingTreasures = true;
let [pIDs, islandX, islandZ] = playerPlacementCircle(0.35);
let [pIDs, islandPosition] = playerPlacementCircle(fractionToTiles(0.35));
if (!isNomad())
{
[playerIDs, playerX, playerZ] = [pIDs, islandX, islandZ];
[playerIDs, playerPosition] = [pIDs, islandPosition];
markPlayerArea("large");
}
@@ -145,7 +144,7 @@ function unknownArchipelago()
let islandSize = diskArea(scaleByMapSize(17, 29));
for (let i = 0; i < numPlayers; ++i)
createArea(
new ClumpPlacer(islandSize, 0.8, 0.1, 10, fractionToTiles(islandX[i]), fractionToTiles(islandZ[i])),
new ClumpPlacer(islandSize, 0.8, 0.1, 10, islandPosition[i].x, islandPosition[i].y),
landElevationPainter);
let type = isNomad() ? randIntInclusive(1, 2) : randIntInclusive(1, 3);
@@ -219,7 +218,7 @@ function unknownContinent()
if (!isNomad())
{
log("Ensuring player area...");
[playerIDs, playerX, playerZ] = playerPlacementCircle(0.25);
[playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25));
markPlayerArea("small");
for (let i = 0; i < numPlayers; ++i)
@@ -229,8 +228,8 @@ function unknownContinent()
Math.floor(scaleByMapSize(5, 9)),
Math.floor(scaleByMapSize(5, 20)),
1,
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i])),
playerPosition[i].x,
playerPosition[i].y,
0,
[Math.floor(scaleByMapSize(23, 50))]),
[
@@ -311,7 +310,7 @@ function unknownCentralSea()
if (!isNomad())
{
[playerIDs, playerX, playerZ] = playerPlacementRiver(horizontal ? Math.PI / 2 : 0, 0.6);
[playerIDs, playerPosition] = playerPlacementRiver(horizontal ? Math.PI / 2 : 0, fractionToTiles(0.6));
markPlayerArea("small");
}
@@ -355,7 +354,7 @@ function unknownCentralRiver()
if (!isNomad())
{
[playerIDs, playerX, playerZ] = playerPlacementRiver(horizontal ? Math.PI / 2 : 0, 0.5);
[playerIDs, playerPosition] = playerPlacementRiver(horizontal ? Math.PI / 2 : 0, fractionToTiles(0.5));
markPlayerArea("large");
}
@@ -418,7 +417,7 @@ function unknownRiversAndLake()
if (!isNomad())
{
let playerAngle;
[playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
[playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
@@ -491,7 +490,7 @@ function unknownEdgeSeas()
let horizontal = randBool();
if (!isNomad())
{
[playerIDs, playerX, playerZ] = playerPlacementLine(horizontal, 0.5, 0.2);
[playerIDs, playerPosition] = playerPlacementLine(horizontal, mapCenter, fractionToTiles(0.2));
// Don't place the shoreline inside the CC, but possibly into the players territory
markPlayerArea("small");
}
@@ -531,11 +530,10 @@ function unknownGulf()
{
log("Determining player locations...");
[playerX, playerZ] = playerPlacementCustomAngle(
0.35,
tilesToFraction(mapCenter.x),
tilesToFraction(mapCenter.y),
i => startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))));
playerPosition = playerPlacementCustomAngle(
fractionToTiles(0.35),
mapCenter,
i => startAngle + 2/3 * Math.PI * (-1 + (numPlayers == 1 ? 1 : 2 * i / (numPlayers - 1))))[0];
markPlayerArea("large");
}
@@ -553,7 +551,7 @@ function unknownGulf()
new SmoothElevationPainter(ELEVATION_SET, waterHeight, 4),
paintClass(clWater)
],
avoidClasses(clPlayerTerritory, scaleByMapSize(15, 25)));
avoidClasses(clPlayerTerritory, defaultPlayerBaseRadius()));
}
/**
@@ -567,7 +565,7 @@ function unknownLakes()
if (!isNomad())
{
[playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
[playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("large");
}
@@ -595,7 +593,7 @@ function unknownPasses()
let startAngle;
if (!isNomad())
{
[playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
[playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
else
@@ -674,7 +672,7 @@ function unknownLowlands()
let startAngle;
if (!isNomad())
{
[playerIDs, playerX, playerZ, playerAngle, startAngle] = playerPlacementCircle(0.35);
[playerIDs, playerPosition, playerAngle, startAngle] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
else
@@ -726,7 +724,7 @@ function unknownMainland()
if (!isNomad())
{
[playerIDs, playerX, playerZ] = playerPlacementCircle(0.35);
[playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
markPlayerArea("small");
}
}
@@ -795,14 +793,11 @@ function markPlayerArea(size)
{
for (let i = 0; i < numPlayers; ++i)
{
addCivicCenterAreaToClass(
Math.round(fractionToTiles(playerX[i])),
Math.round(fractionToTiles(playerZ[i])),
clPlayer);
addCivicCenterAreaToClass(playerPosition[i], clPlayer);
if (size == "large")
createArea(
new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, fractionToTiles(playerX[i]), fractionToTiles(playerZ[i])),
new ClumpPlacer(diskArea(scaleByMapSize(17, 29) / 3), 0.6, 0.3, 10, playerPosition[i].x, playerPosition[i].y),
paintClass(clPlayerTerritory));
}
}
@@ -1018,7 +1013,7 @@ function createUnknownObjects()
function createUnknownPlayerBases()
{
placePlayerBases({
"PlayerPlacement": [playerIDs, playerX, playerZ],
"PlayerPlacement": [playerIDs, playerPosition],
"BaseResourceClass": clBaseResource,
"Walls": g_StartingWalls,
"CityPatch": {
@@ -35,7 +35,7 @@ var clMetal = createTileClass();
var clBaseResource = createTileClass();
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(0.35),
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {
@@ -619,13 +619,13 @@ for (let i = 0; i < resourceSpots.length; ++i)
{
if (mercenaryCamps)
{
placeStartingEntities(resourceSpots[i].x, resourceSpots[i].y, 0, mercenaryCampGuards[currentBiome()]);
placeStartingEntities(resourceSpots[i], 0, mercenaryCampGuards[currentBiome()]);
rectangularSmoothToHeight(resourceSpots[i], 15, 15, g_Map.height[resourceSpots[i].x][resourceSpots[i].y], 0.5);
--mercenaryCamps;
}
else
{
placeCustomFortress(resourceSpots[i].x, resourceSpots[i].y, pickRandom(fences), "other", 0, randomAngle());
placeCustomFortress(resourceSpots[i], pickRandom(fences), "other", 0, randomAngle());
rectangularSmoothToHeight(resourceSpots[i], 10, 10, g_Map.height[resourceSpots[i].x][resourceSpots[i].y], 0.5);
}
}