Compare commits

...

7 Commits

51 changed files with 506 additions and 223 deletions

View File

@ -0,0 +1,78 @@
GameSettings.prototype.Attributes.PlayerPlacement = class PlayerPlacement extends GameSetting
{
init()
{
this.available = undefined;
this.value = undefined;
this.settings.map.watch(() => this.onMapChange(), ["map"]);
}
toInitAttributes(attribs)
{
if (this.value !== undefined)
attribs.settings.PlayerPlacement = this.value;
}
fromInitAttributes(attribs)
{
if (!!this.getLegacySetting(attribs, "PlayerPlacement"))
this.value = this.getLegacySetting(attribs, "PlayerPlacement");
}
onMapChange()
{
if (!this.getMapSetting("PlayerPlacements"))
{
this.value = undefined;
this.available = undefined;
return;
}
// TODO: should probably validate that they fit one of the known schemes.
this.available = this.getMapSetting("PlayerPlacements");
this.value = "random";
}
setValue(val)
{
this.value = val;
}
pickRandomItems()
{
// If the map is random, we need to wait until it is selected.
if (this.settings.map.map === "random" || this.value !== "random")
return false;
this.value = pickRandom(this.available);
return true;
}
};
GameSettings.prototype.Attributes.PlayerPlacement.prototype.StartingPositions = [
{
"Id": "circle",
"Name": translateWithContext("player placement", "Circle"),
"Description": translate("Players are placed in a circle spanning the map.")
},
{
"Id": "river",
"Name": translateWithContext("player placement", "River"),
"Description": translate("Allied players are placed on two parallel lines.")
},
{
"Id": "groupedLines",
"Name": translateWithContext("player placement", "Grouped lines"),
"Description": translate("Allied players are placed along opposing radial lines"),
},
{
"Id": "randomGroup",
"Name": translateWithContext("player placement", "Random Group"),
"Description": translate("Allied players are grouped, but otherwise placed randomly on the map."),
},
{
"Id": "stronghold",
"Name": translateWithContext("player placement", "Stronghold"),
"Description": translate("Allied players are grouped in one random place of the map."),
}
];

View File

@ -1,73 +0,0 @@
GameSettings.prototype.Attributes.TeamPlacement = class TeamPlacement extends GameSetting
{
init()
{
this.available = undefined;
this.value = undefined;
this.settings.map.watch(() => this.onMapChange(), ["map"]);
}
toInitAttributes(attribs)
{
if (this.value !== undefined)
attribs.settings.TeamPlacement = this.value;
}
fromInitAttributes(attribs)
{
if (!!this.getLegacySetting(attribs, "TeamPlacement"))
this.value = this.getLegacySetting(attribs, "TeamPlacement");
}
onMapChange()
{
if (!this.getMapSetting("TeamPlacements"))
{
this.value = undefined;
this.available = undefined;
return;
}
// TODO: should probably validate that they fit one of the known schemes.
this.available = this.getMapSetting("TeamPlacements");
this.value = "random";
}
setValue(val)
{
this.value = val;
}
pickRandomItems()
{
// If the map is random, we need to wait until it is selected.
if (this.settings.map.map === "random" || this.value !== "random")
return false;
this.value = pickRandom(this.available);
return true;
}
};
GameSettings.prototype.Attributes.TeamPlacement.prototype.StartingPositions = [
{
"Id": "radial",
"Name": translateWithContext("team placement", "Circle"),
"Description": translate("Allied players are grouped and placed with opposing players on one circle spanning the map.")
},
{
"Id": "line",
"Name": translateWithContext("team placement", "Line"),
"Description": translate("Allied players are placed in a linear pattern."),
},
{
"Id": "randomGroup",
"Name": translateWithContext("team placement", "Random Group"),
"Description": translate("Allied players are grouped, but otherwise placed randomly on the map."),
},
{
"Id": "stronghold",
"Name": translateWithContext("team placement", "Stronghold"),
"Description": translate("Allied players are grouped in one random place of the map."),
}
];

View File

@ -11,7 +11,7 @@ var g_GameSettingsLayout = [
"MapSelection",
"MapBrowser",
"MapSize",
"TeamPlacement",
"PlayerPlacement",
"Landscape",
"Biome",
"WaterLevel",

View File

@ -0,0 +1,64 @@
GameSettingControls.PlayerPlacement = class PlayerPlacement extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.values = undefined;
g_GameSettings.playerPlacement.watch(() => this.render(), ["value", "available"]);
this.render();
}
onHoverChange()
{
this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip;
}
render()
{
this.setHidden(!g_GameSettings.playerPlacement.value);
if (!g_GameSettings.playerPlacement.value)
return;
let randomItem = clone(this.RandomItem);
randomItem.Name = setStringTags(randomItem.Name, this.RandomItemTags);
let patterns = [randomItem];
for (let pattern of g_GameSettings.playerPlacement.available)
patterns.push(g_GameSettings.playerPlacement.StartingPositions
.find(pObj => pObj.Id == pattern));
this.values = prepareForDropdown(patterns);
this.dropdown.list = this.values.Name;
this.dropdown.list_data = this.values.Id;
this.setSelectedValue(g_GameSettings.playerPlacement.value);
}
getAutocompleteEntries()
{
return this.values && this.values.Name.slice(1);
}
onSelectionChange(itemIdx)
{
g_GameSettings.playerPlacement.setValue(this.values.Id[itemIdx]);
this.gameSettingsController.setNetworkInitAttributes();
}
};
GameSettingControls.PlayerPlacement.prototype.TitleCaption =
translate("Player Placement");
GameSettingControls.PlayerPlacement.prototype.Tooltip =
translate("Select one of the starting position patterns for this map.");
GameSettingControls.PlayerPlacement.prototype.RandomItem = {
"Id": "random",
"Name": translateWithContext("player placement", "Random"),
"Description": translateWithContext("player placement", "Select a random player placement pattern when starting the game.")
};
GameSettingControls.PlayerPlacement.prototype.AutocompleteOrder = 0;

View File

@ -1,64 +0,0 @@
GameSettingControls.TeamPlacement = class TeamPlacement extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.values = undefined;
g_GameSettings.teamPlacement.watch(() => this.render(), ["value", "available"]);
this.render();
}
onHoverChange()
{
this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip;
}
render()
{
this.setHidden(!g_GameSettings.teamPlacement.value);
if (!g_GameSettings.teamPlacement.value)
return;
let randomItem = clone(this.RandomItem);
randomItem.Name = setStringTags(randomItem.Name, this.RandomItemTags);
let patterns = [randomItem];
for (let pattern of g_GameSettings.teamPlacement.available)
patterns.push(g_GameSettings.teamPlacement.StartingPositions
.find(pObj => pObj.Id == pattern));
this.values = prepareForDropdown(patterns);
this.dropdown.list = this.values.Name;
this.dropdown.list_data = this.values.Id;
this.setSelectedValue(g_GameSettings.teamPlacement.value);
}
getAutocompleteEntries()
{
return this.values && this.values.Name.slice(1);
}
onSelectionChange(itemIdx)
{
g_GameSettings.teamPlacement.setValue(this.values.Id[itemIdx]);
this.gameSettingsController.setNetworkInitAttributes();
}
};
GameSettingControls.TeamPlacement.prototype.TitleCaption =
translate("Team Placement");
GameSettingControls.TeamPlacement.prototype.Tooltip =
translate("Select one of the starting position patterns of this map.");
GameSettingControls.TeamPlacement.prototype.RandomItem = {
"Id": "random",
"Name": translateWithContext("team placement", "Random"),
"Description": translateWithContext("team placement", "Select a random team placement pattern when starting the game.")
};
GameSettingControls.TeamPlacement.prototype.AutocompleteOrder = 0;

View File

@ -74,8 +74,17 @@ function* GenerateMap(mapSettings)
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -5,6 +5,13 @@
"Description" : "The central region of the vast continent of Africa, birthplace of humanity. Players start in a lush area teeming with vegetation and wildlife.",
"SupportedBiomes": ["generic/savanna", "generic/sahara", "generic/nubia"],
"Preview" : "african_plains.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -38,8 +38,17 @@ function* GenerateMap(mapSettings)
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -3,8 +3,15 @@
"Name" : "Alpine Lakes",
"Script" : "alpine_lakes.js",
"Description" : "High Alpine mountains surrounding deep valleys strung with mountain streams and finger-like lakes.",
"CircularMap" : true,
"Preview" : "alpine_lakes.png",
"SupportedBiomes": "alpine/"
"SupportedBiomes": "alpine/",
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -20,13 +20,13 @@ function* GenerateMap(mapSettings)
yield 10;
const pattern = mapSettings.TeamPlacement ||
pickRandom(["line", "radial", "randomGroup", "stronghold"]);
const pattern = mapSettings.PlayerPlacement ||
pickRandom(["circle", "river", "groupedLines", "randomGroup", "stronghold"]);
const [playerIDs, playerPosition] =
createBases(
...playerPlacementByPattern(
pattern,
fractionToTiles(randFloat(0.2, 0.35)),
fractionToTiles(randFloat(0.25, 0.35)),
fractionToTiles(randFloat(0.08, 0.1)),
randomAngle(),
undefined),

View File

@ -6,6 +6,6 @@
"Preview" : "ambush.png",
"SupportedBiomes": "generic/",
"CircularMap" : true,
"TeamPlacements": ["radial", "line", "randomGroup", "stronghold"]
"PlayerPlacements": ["circle", "river", "groupedLines", "randomGroup", "stronghold"]
}
}

View File

@ -52,8 +52,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -4,6 +4,13 @@
"Script" : "anatolian_plateau.js",
"Description" : "An indefensible open land with little wood and stone, representing the central basin of Asia Minor.",
"Preview" : "anatolian_plateau.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -76,7 +76,14 @@ function* GenerateMap()
const islandRadius = scaleByMapSize(22, 31);
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
g_Map.log("Creating player islands");
for (let i = 0; i < numPlayers; ++i)

View File

@ -6,6 +6,13 @@
"Keywords": ["naval"],
"Preview" : "archipelago.png",
"SupportedBiomes": "generic/",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -63,8 +63,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -5,6 +5,13 @@
"Description" : "Summer has arrived to the cold regions of the north and with it have many animals, profiting from its milder climate. Wolves, ever present, have shed their winter clothes; deer, hares and muskox populate the plains. The last traces of winter are rapidly disappearing, only to reappear very soon.",
"Keywords": [],
"Preview" : "arctic_summer.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -104,7 +104,14 @@ function* GenerateMap()
}
}
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.3));
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
function distanceToPlayers(x, z)
{

View File

@ -5,6 +5,12 @@
"Description" : "Each player starts deep in the forest.\n\nThe Ardennes is a region of extensive forests, rolling hills and ridges formed within the Givetian Ardennes mountain range, primarily in modern day Belgium and Luxembourg. The region took its name from the ancient Silva, a vast forest in Roman times called Arduenna Silva.",
"Keywords": ["multiplayer"],
"CircularMap" : true,
"Preview" : "ardennes_forest.png"
"Preview" : "ardennes_forest.png",
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup"
]
}
}

View File

@ -60,8 +60,17 @@ function* GenerateMap()
const clBaseResource = g_Map.createTileClass();
const clTreasure = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -4,6 +4,13 @@
"Script" : "atlas_mountains.js",
"Description" : "A rugged land with small room for buildings with scarce wood. Represents the mountain range in the Northwest Africa.",
"Preview" : "atlas_mountains.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -66,8 +66,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -3,8 +3,14 @@
"Name" : "Botswanan Haven",
"Script" : "botswanan_haven.js",
"Description" : "Botswanan Africa during the wet season, a land which was arid and inhospitable just weeks before has come to life totally transformed. Herds of zebras graze amid the tall, lush grasses in which lions lie waiting, while in the shallow pools lurk fearsome crocodiles.",
"Keywords": [],
"Preview" : "botswanan_haven.png",
"CircularMap" : true,
"Preview" : "botswanan_haven.png"
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -86,7 +86,14 @@ function* GenerateMap()
new TileClassPainter(clLand)
]);
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.25));
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.25),
fractionToTiles(0.1),
randomAngle(),
undefined);
g_Map.log("Ensuring initial player land");
for (let i = 0; i < numPlayers; ++i)

View File

@ -6,6 +6,11 @@
"Preview" : "continent.png",
"Keywords": ["multiplayer"],
"SupportedBiomes": "generic/",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"randomGroup",
"stronghold"
]
}
}

View File

@ -79,7 +79,14 @@ function* GenerateMap()
const playerMountainSize = defaultPlayerBaseRadius();
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
g_Map.log("Creating CC mountains");
if (!isNomad())

View File

@ -1,11 +1,16 @@
{
"settings" : {
"Name" : "Extinct Volcano",
"Script" : "extinct_volcano.js",
"Description" : "[color=\"red\"]IMPORTANT NOTE: AI PLAYERS DO NOT WORK WITH THIS MAP[/color]\n\nA once fertile valley, desolated by the eruption of the long-dormant volcano in the heart of the region. Following years of empty, scorched deadness, signs of life started reappearing and spreading. Now the land is half-way to the full lushness of its former era. Alas, it is not to be: following a long stretch of drought, interminable rains have set in in the higher regions to the north. Water levels are rising at drastic levels, slowly forcing players to seek the high ground of the lesser, extinct volcanoes or the now again dormant great cone.",
"Keywords": ["trigger"],
"CircularMap" : true,
"Preview" : "extinctvolcano.png",
"settings": {
"Name": "Extinct Volcano",
"Script": "extinct_volcano.js",
"Description": "[color=\"red\"]IMPORTANT NOTE: AI PLAYERS DO NOT WORK WITH THIS MAP[/color]\n\nA once fertile valley, desolated by the eruption of the long-dormant volcano in the heart of the region. Following years of empty, scorched deadness, signs of life started reappearing and spreading. Now the land is half-way to the full lushness of its former era. Alas, it is not to be: following a long stretch of drought, interminable rains have set in in the higher regions to the north. Water levels are rising at drastic levels, slowly forcing players to seek the high ground of the lesser, extinct volcanoes or the now again dormant great cone.",
"Keywords": [ "trigger" ],
"CircularMap": true,
"Preview": "extinctvolcano.png",
"PlayerPlacements": [
"circle",
"river",
"randomGroup"
],
"SeaLevelRise": {
"Min": 0,
"Max": 60,

View File

@ -24,8 +24,8 @@ function* GenerateMap(mapSettings)
if (!isNomad())
{
// Note: `|| pickRandom(...)` is needed for atlas.
const pattern = mapSettings.TeamPlacement ||
pickRandom(["line", "radial", "randomGroup", "stronghold"]);
const pattern = mapSettings.PlayerPlacement ||
pickRandom(["groupedLines", "river", "circle", "randomGroup", "stronghold"]);
createBases(
...playerPlacementByPattern(
pattern,

View File

@ -6,6 +6,6 @@
"Preview" : "frontier.png",
"SupportedBiomes": "generic/",
"CircularMap" : true,
"TeamPlacements": ["radial", "line", "randomGroup", "stronghold"]
"PlayerPlacements": ["circle", "river", "groupedLines", "randomGroup", "stronghold"]
}
}

View File

@ -30,7 +30,7 @@ function* GenerateMap()
const [playerIDs, playerPosition] =
createBases(
...playerPlacementByPattern(
"radial",
"circle",
fractionToTiles(0.38),
fractionToTiles(0.05),
startAngle,

View File

@ -26,7 +26,7 @@ function* GenerateMap()
const startAngle = randomAngle();
createBases(
...playerPlacementByPattern(
"line",
"groupedLines",
fractionToTiles(0.2),
fractionToTiles(0.08),
startAngle,

View File

@ -44,8 +44,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
// No city patch

View File

@ -5,6 +5,11 @@
"Description" : "Central India just before the monsoon season - a parched land awaits the life-bringing rain which is already two months late. Due to the extended dryness and scorching heat, only the largest lake remains. The hardy trees which have survived the climate are spread out, yet not too scarce.",
"Preview" : "india.png",
"Keywords": [],
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"stronghold"
]
}
}

View File

@ -71,7 +71,15 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
const pattern = g_MapSettings.PlayerPlacement;
const teamDist = (pattern == 'river') ? .55 : .35;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(teamDist),
fractionToTiles(0.1),
randomAngle(),
undefined);
g_Map.log("Preventing water in player territory");
for (let i = 0; i < numPlayers; ++i)

View File

@ -6,6 +6,10 @@
"Keywords": ["multiplayer"],
"Preview" : "lake.png",
"SupportedBiomes": "generic/",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river"
]
}
}

View File

@ -29,7 +29,7 @@ function* GenerateMap()
createBases(
...playerPlacementByPattern(
"radial",
"circle",
fractionToTiles(0.4),
fractionToTiles(randFloat(0.05, 0.1)),
startAngle,

View File

@ -64,8 +64,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -6,6 +6,13 @@
"Preview" : "mainland.png",
"Keywords": ["multiplayer"],
"SupportedBiomes": "generic/",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -83,7 +83,14 @@ function* GenerateMap(mapSettings)
const oasisRadius = scaleByMapSize(14, 40);
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
if (!isNomad())
for (let i = 0; i < numPlayers; ++i)

View File

@ -6,6 +6,9 @@
"SupportedBiomes": ["generic/sahara", "generic/nubia", "generic/savanna"],
"Keywords": ["multiplayer"],
"Preview" : "neareastern_badlands.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle"
]
}
}

View File

@ -63,8 +63,18 @@ function* GenerateMap(mapSettings)
const clBaseResource = g_Map.createTileClass();
const clCP = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const teamDist = (pattern == "river") ? .50 : .35;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(teamDist),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"BaseResourceClass": clBaseResource,
"CityPatch": {
"outerTerrain": tCity,

View File

@ -1,11 +1,16 @@
{
"settings" : {
"Name" : "Persian Highlands",
"Script" : "persian_highlands.js",
"Description" : "A dry central plateau rich in minerals surrounded by rocky hills\n\nThe southern parts of Zagros Mountains were the heart of the Persian empires and population. Although the altitude is high, the southern parts are drier that the northern Zagros, leading to a semi-arid climate. Still there are some sparse oak forests in the higher grounds.",
"Keywords": ["multiplayer"],
"Preview" : "persian_highlands_summer.png",
"CircularMap" : true,
"SupportedBiomes": "persian_highlands/"
"settings": {
"Name": "Persian Highlands",
"Script": "persian_highlands.js",
"Description": "A dry central plateau rich in minerals surrounded by rocky hills\n\nThe southern parts of Zagros Mountains were the heart of the Persian empires and population. Although the altitude is high, the southern parts are drier that the northern Zagros, leading to a semi-arid climate. Still there are some sparse oak forests in the higher grounds.",
"Keywords": [ "multiplayer" ],
"Preview": "persian_highlands_summer.png",
"CircularMap": true,
"SupportedBiomes": "persian_highlands/",
"PlayerPlacements": [
"circle",
"river",
"stronghold"
]
}
}

View File

@ -55,8 +55,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -4,6 +4,13 @@
"Script" : "rhine_marshlands.js",
"Description" : "Shallow, passable wetlands with little room for building. Represents the lowlands of the Rhine basin in Europe.",
"Preview" : "rhine_marshlands.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -461,7 +461,7 @@ function placePlayersNomad(playerClass, constraints)
const constraint = new StaticConstraint(constraints);
const numPlayers = getNumPlayers();
const playerIDs = shuffleArray(sortAllPlayers());
const playerIDs = shuffleArray(getPlayerIDs());
const playerPosition = [];
for (let i = 0; i < numPlayers; ++i)
@ -501,7 +501,16 @@ function placePlayersNomad(playerClass, constraints)
}
/**
* Sorts an array of player IDs by team index. Players without teams come first.
* Get the player IDs
*
* @returns {Array} - an array with sequential integers from 1 to the number of players
*/
function getPlayerIDs()
{
return Array.from(Array(getNumPlayers()), (_, index) => index + 1);
}
/*** Sorts an array of player IDs by team index. Players without teams come first.
* Randomize order for players of the same team.
*/
function sortPlayers(playerIDs)
@ -516,11 +525,7 @@ function sortPlayers(playerIDs)
*/
function sortAllPlayers()
{
const playerIDs = [];
for (let i = 0; i < getNumPlayers(); ++i)
playerIDs.push(i+1);
return sortPlayers(playerIDs);
return sortPlayers(getPlayerIDs());
}
/**
@ -578,7 +583,7 @@ function partitionPlayers(playerIDs)
*/
function getTeamsArray()
{
let playerIDs = sortAllPlayers();
const playerIDs = getPlayerIDs();
let numPlayers = getNumPlayers();
// Group players by team
@ -610,20 +615,20 @@ function getTeamsArray()
function playerPlacementByPattern(patternName, distance = undefined, groupedDistance = undefined, angle = undefined, center = undefined)
{
if (patternName === undefined)
patternName = g_MapSettings.TeamPlacement;
patternName = g_MapSettings.PlayerPlacement;
switch (patternName)
{
case "radial":
case "circle":
return playerPlacementCircle(distance, angle, center).slice(0, 2);
case "river":
return playerPlacementRiver(angle, distance, center);
case "line":
case "groupedLines":
return placeLine(getTeamsArray(), distance, groupedDistance, angle);
case "stronghold":
return placeStronghold(getTeamsArray(), distance, groupedDistance, angle);
case "randomGroup":
return playerPlacementRandom(sortAllPlayers(), undefined);
return playerPlacementRandom(getPlayerIDs(), undefined);
default:
throw new Error("Unknown placement pattern: " + patternName);
}
@ -636,7 +641,7 @@ function playerPlacementCircle(radius, startingAngle = undefined, center = undef
{
const startAngle = startingAngle !== undefined ? startingAngle : randomAngle();
const [playerPosition, playerAngle] = distributePointsOnCircle(getNumPlayers(), startAngle, radius, center || g_Map.getCenter());
return [sortAllPlayers(), playerPosition.map(p => p.round()), playerAngle, startAngle];
return [getPlayerIDs(), playerPosition.map(p => p.round()), playerAngle, startAngle];
}
/**
@ -752,8 +757,9 @@ function placeLine(teamsArray, distance, groupedDistance, startAngle)
const playerPosition = [];
const mapCenter = g_Map.getCenter();
const dist = fractionToTiles(0.45);
const numAcross = 2 * getNumPlayers() / teamsArray.length; // if its two teams, numAcross is the same as numPlayers.
const dist = fractionToTiles(numAcross == 2 ? 0.45 : 0.66 + (-0.01 * numAcross));
groupedDistance = groupedDistance * (3.00 + (-0.225 * numAcross));
for (let i = 0; i < teamsArray.length; ++i)
{
let safeDist = distance;
@ -790,8 +796,8 @@ function placeStronghold(teamsArray, distance, groupedDistance, startAngle)
for (let i = 0; i < teamsArray.length; ++i)
{
let teamAngle = startAngle + (i + 1) * 2 * Math.PI / teamsArray.length;
let teamPosition = Vector2D.add(mapCenter, new Vector2D(distance, 0).rotate(-teamAngle));
let teamGroupDistance = groupedDistance;
let teamPosition = Vector2D.add(mapCenter, new Vector2D(distance * 0.8, 0).rotate(-teamAngle));
let teamGroupDistance = groupedDistance * 1.2;
// If we have a team of above average size, make sure they're spread out
if (teamsArray[i].length > 4)

View File

@ -46,8 +46,17 @@ function* GenerateMap()
const clFood = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -4,6 +4,13 @@
"Script" : "sahel.js",
"Description" : "A somewhat open map with an abundance of food and mineral resources, while wood is somewhat scarce.",
"Preview" : "sahel.png",
"CircularMap" : true
"CircularMap" : true,
"PlayerPlacements": [
"circle",
"river",
"groupedLines",
"randomGroup",
"stronghold"
]
}
}

View File

@ -52,7 +52,14 @@ function* GenerateMap()
const clBaseResource = g_Map.createTileClass();
const clGrass = g_Map.createTileClass();
const [playerIDs, playerPosition] = playerPlacementCircle(fractionToTiles(0.35));
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
g_Map.log("Creating big grass patches around the playerbases");
for (let i = 0; i < numPlayers; ++i)

View File

@ -1,9 +1,15 @@
{
"settings" : {
"Name" : "Syria",
"Script" : "syria.js",
"Description" : "Players start in a plains with slightly rolling highlands.",
"Preview" : "syria.png",
"CircularMap" : true
"settings": {
"Name": "Syria",
"Script": "syria.js",
"Description": "Players start in a plains with slightly rolling highlands.",
"Preview": "syria.png",
"CircularMap": true,
"PlayerPlacements": [
"circle",
"river",
"randomGroup",
"stronghold"
]
}
}

View File

@ -41,8 +41,17 @@ function* GenerateMap()
const clMetal = g_Map.createTileClass();
const clBaseResource = g_Map.createTileClass();
const pattern = g_MapSettings.PlayerPlacement;
const [playerIDs, playerPosition] =
playerPlacementByPattern(
pattern,
fractionToTiles(0.35),
fractionToTiles(0.1),
randomAngle(),
undefined);
placePlayerBases({
"PlayerPlacement": playerPlacementCircle(fractionToTiles(0.35)),
"PlayerPlacement": [playerIDs, playerPosition],
"PlayerTileClass": clPlayer,
"BaseResourceClass": clBaseResource,
"CityPatch": {

View File

@ -4,6 +4,11 @@
"Script" : "volcanic_lands.js",
"Description" : "A charred dead land where players start around a smoking volcano.",
"CircularMap" : true,
"Preview" : "volcanic_lands.png"
"Preview" : "volcanic_lands.png",
"PlayerPlacements": [
"circle",
"river",
"stronghold"
]
}
}