forked from 0ad/0ad
Compare commits
7 Commits
68de737d1d
...
214c51203f
Author | SHA1 | Date | |
---|---|---|---|
214c51203f | |||
398314be4c | |||
86dee81483 | |||
bec5b35d90 | |||
073286e73c | |||
a75a116333 | |||
fc17b7d55d |
@ -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."),
|
||||
}
|
||||
];
|
@ -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."),
|
||||
}
|
||||
];
|
@ -11,7 +11,7 @@ var g_GameSettingsLayout = [
|
||||
"MapSelection",
|
||||
"MapBrowser",
|
||||
"MapSize",
|
||||
"TeamPlacement",
|
||||
"PlayerPlacement",
|
||||
"Landscape",
|
||||
"Biome",
|
||||
"WaterLevel",
|
||||
|
@ -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;
|
@ -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;
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -6,6 +6,6 @@
|
||||
"Preview" : "ambush.png",
|
||||
"SupportedBiomes": "generic/",
|
||||
"CircularMap" : true,
|
||||
"TeamPlacements": ["radial", "line", "randomGroup", "stronghold"]
|
||||
"PlayerPlacements": ["circle", "river", "groupedLines", "randomGroup", "stronghold"]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -6,6 +6,13 @@
|
||||
"Keywords": ["naval"],
|
||||
"Preview" : "archipelago.png",
|
||||
"SupportedBiomes": "generic/",
|
||||
"CircularMap" : true
|
||||
"CircularMap" : true,
|
||||
"PlayerPlacements": [
|
||||
"circle",
|
||||
"river",
|
||||
"groupedLines",
|
||||
"randomGroup",
|
||||
"stronghold"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -6,6 +6,11 @@
|
||||
"Preview" : "continent.png",
|
||||
"Keywords": ["multiplayer"],
|
||||
"SupportedBiomes": "generic/",
|
||||
"CircularMap" : true
|
||||
"CircularMap" : true,
|
||||
"PlayerPlacements": [
|
||||
"circle",
|
||||
"randomGroup",
|
||||
"stronghold"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -6,6 +6,6 @@
|
||||
"Preview" : "frontier.png",
|
||||
"SupportedBiomes": "generic/",
|
||||
"CircularMap" : true,
|
||||
"TeamPlacements": ["radial", "line", "randomGroup", "stronghold"]
|
||||
"PlayerPlacements": ["circle", "river", "groupedLines", "randomGroup", "stronghold"]
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ function* GenerateMap()
|
||||
const [playerIDs, playerPosition] =
|
||||
createBases(
|
||||
...playerPlacementByPattern(
|
||||
"radial",
|
||||
"circle",
|
||||
fractionToTiles(0.38),
|
||||
fractionToTiles(0.05),
|
||||
startAngle,
|
||||
|
@ -26,7 +26,7 @@ function* GenerateMap()
|
||||
const startAngle = randomAngle();
|
||||
createBases(
|
||||
...playerPlacementByPattern(
|
||||
"line",
|
||||
"groupedLines",
|
||||
fractionToTiles(0.2),
|
||||
fractionToTiles(0.08),
|
||||
startAngle,
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -6,6 +6,10 @@
|
||||
"Keywords": ["multiplayer"],
|
||||
"Preview" : "lake.png",
|
||||
"SupportedBiomes": "generic/",
|
||||
"CircularMap" : true
|
||||
"CircularMap" : true,
|
||||
"PlayerPlacements": [
|
||||
"circle",
|
||||
"river"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ function* GenerateMap()
|
||||
|
||||
createBases(
|
||||
...playerPlacementByPattern(
|
||||
"radial",
|
||||
"circle",
|
||||
fractionToTiles(0.4),
|
||||
fractionToTiles(randFloat(0.05, 0.1)),
|
||||
startAngle,
|
||||
|
@ -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": {
|
||||
|
@ -6,6 +6,13 @@
|
||||
"Preview" : "mainland.png",
|
||||
"Keywords": ["multiplayer"],
|
||||
"SupportedBiomes": "generic/",
|
||||
"CircularMap" : true
|
||||
"CircularMap" : true,
|
||||
"PlayerPlacements": [
|
||||
"circle",
|
||||
"river",
|
||||
"groupedLines",
|
||||
"randomGroup",
|
||||
"stronghold"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -6,6 +6,9 @@
|
||||
"SupportedBiomes": ["generic/sahara", "generic/nubia", "generic/savanna"],
|
||||
"Keywords": ["multiplayer"],
|
||||
"Preview" : "neareastern_badlands.png",
|
||||
"CircularMap" : true
|
||||
"CircularMap" : true,
|
||||
"PlayerPlacements": [
|
||||
"circle"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user