mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-06-21 20:04:13 +00:00
Separate Game Settings from the GUI Gamesetup
Split the gamesetup in two: the 'GameAttributes' part into gamesettings/
and the GUI/Presentation part in gamesetup/. This makes it easier to
separate the presentation from the data.
The immediate benefit is that campaigns & autostart scripts don't need
to load the gamesetup folder at all. This also makes it much easier for
any modder that would want to to change the GameSetup itself.
Each 'game attribute' is given a unique class extending GameSetting
(with a few exceptions), in charge of:
- 'Serializing' to the JSON-compatible 'InitAttributes' format, which is
used for persisted settings, network synchronization, map script
settings, hotloading.
- Deserializing from the same format.
- Watching for settings it depends on (such that e.g. unexploring the
map also unreveals it).
The GUI controls remain in charge of presenting the state accurately,
however they now directly subscribe to changes of the GameSettings for
update. The updating logic in general has been lightened on the GUI
side, making it more straightforward to know when something will update,
and reducing un-necessary computations (in theory - in practice, I
believe the gamesetup was already fairly good about this).
The 'Controller' class of the gamesetup have also been lightened, since
more responsibility now lies with GameSettings. In particular, this
include code to actually launch a game.
In general the GameSettings class is permissive - the GUI gamesetup has
tighter restriction for what the player can/cannot modify. This is
intended to give flexibility for campaign maps, which may want to change
arbitrary settings.
Further work would be useful, non-exhaustively:
- the setting of default values remains messy. They currently exist
somethings in GameSettings, sometimes in the GUI gamesetup, and in the
simulation itself (in case attributes are not set).
- the availability and 'lockedness' of settings remains a
work-in-progress.
- some attributes, like disabled technologies, should probably be
removed and triggers used instead.
- the Handling of AI and player-specific data could be improved.
- settings Persistence should follow its own path - not all settings are
worth persisting.
- GAIA settings are added simulation-side but not in the GUI, which is
confusing.
Thanks langbart & Freagarach for testing.
Follows the gamesetup rewrite in 34138a7764.
Refs #3049
Differential Revision: https://code.wildfiregames.com/D3243
This was SVN commit r25077.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
function init(initData)
|
||||
{
|
||||
let settings = new GameSettings().init();
|
||||
settings.fromInitAttributes(initData);
|
||||
let assignments = {
|
||||
"local": {
|
||||
"player": 1,
|
||||
"name": Engine.ConfigDB_GetValue("user", "playername.singleplayer") || Engine.GetSystemUsername()
|
||||
}
|
||||
};
|
||||
settings.launchGame(assignments);
|
||||
|
||||
Engine.SwitchGuiPage("page_loading.xml", {
|
||||
"attribs": settings.toInitAttributes(),
|
||||
"playerAssignments": assignments
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<objects>
|
||||
<script file="gui/maps/MapCache.js"/>
|
||||
<script directory="gui/common/"/>
|
||||
<script directory="gui/gamesettings/"/>
|
||||
<script directory="gui/gamesettings/attributes/"/>
|
||||
<script directory="gui/autostart/"/>
|
||||
</objects>
|
||||
@@ -39,15 +39,37 @@ class CampaignMenu extends AutoWatcher
|
||||
let level = this.getSelectedLevelData();
|
||||
if (!meetsRequirements(this.run, level))
|
||||
return;
|
||||
Engine.SwitchGuiPage("page_gamesetup.xml", {
|
||||
|
||||
let settings = {
|
||||
"mapType": level.MapType,
|
||||
"map": "maps/" + level.Map,
|
||||
"autostart": true,
|
||||
"settings": {
|
||||
"CheatsEnabled": true
|
||||
},
|
||||
"campaignData": {
|
||||
"run": this.run.filename,
|
||||
"levelID": this.levelSelection.list_data[this.selectedLevel],
|
||||
"data": this.run.data
|
||||
}
|
||||
};
|
||||
let assignments = {
|
||||
"local": {
|
||||
"player": 1,
|
||||
"name": Engine.ConfigDB_GetValue("user", "playername.singleplayer") || Engine.GetSystemUsername()
|
||||
}
|
||||
};
|
||||
|
||||
let gameSettings = new GameSettings().init();
|
||||
gameSettings.fromInitAttributes(settings);
|
||||
if (level.Preview)
|
||||
gameSettings.mapPreview.setCustom("cropped:" + 400/512 + "," + 300/512 + ":" + level.Preview);
|
||||
gameSettings.mapName.set(this.getLevelName(level));
|
||||
// TODO: level description should also be passed, ideally.
|
||||
|
||||
gameSettings.launchGame(assignments);
|
||||
Engine.SwitchGuiPage("page_loading.xml", {
|
||||
"attribs": gameSettings.toInitAttributes(),
|
||||
"playerAssignments": assignments
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
<objects>
|
||||
<script file="gui/maps/MapCache.js"/>
|
||||
<script directory="gui/common/"/>
|
||||
<script directory="gui/gamesettings/"/>
|
||||
<script directory="gui/gamesettings/attributes/"/>
|
||||
<script directory="gui/campaigns/default_menu/"/>
|
||||
|
||||
<object type="image" style="ModernWindow" size="0 0 100% 100%" name="campaignMenuWindow">
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Observable is a self-proxy to enable opt-in reactive programming.
|
||||
* Properties of an Observable can be watched, and whenever they are changed
|
||||
* a callback will be fired.
|
||||
*/
|
||||
var ObservableMixin = (Parent) => class Observable extends (() => Parent || Object)()
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
|
||||
// Stores observers that are fired when the value is changed
|
||||
Object.defineProperty(this, "_changeObserver", {
|
||||
"value": {},
|
||||
"enumerable": false,
|
||||
});
|
||||
// Stores observers that are fired when the value is assigned to,
|
||||
// even if it isn't changed.
|
||||
Object.defineProperty(this, "_setObserver", {
|
||||
"value": {},
|
||||
"enumerable": false,
|
||||
});
|
||||
return new Proxy(this, {
|
||||
"set": (target, key, value) => {
|
||||
let old;
|
||||
let hasOld = false;
|
||||
if (Reflect.has(target, key))
|
||||
{
|
||||
hasOld = true;
|
||||
old = Reflect.get(target, key);
|
||||
}
|
||||
Reflect.set(target, key, value);
|
||||
this._trigger(this._setObserver, key, old);
|
||||
if (!hasOld || old !== value)
|
||||
this._trigger(this._changeObserver, key, old);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_trigger(dict, key, old)
|
||||
{
|
||||
if (dict[key])
|
||||
for (let watcher of dict[key])
|
||||
watcher(key, old);
|
||||
}
|
||||
|
||||
trigger(key, old)
|
||||
{
|
||||
this._trigger(this._setObserver, key, old);
|
||||
this._trigger(this._changeObserver, key, old);
|
||||
}
|
||||
|
||||
watch(watcher, props, onlyChange = true)
|
||||
{
|
||||
let dic = onlyChange ? this._changeObserver : this._setObserver;
|
||||
for (let prop of props)
|
||||
{
|
||||
if (!dic[prop])
|
||||
dic[prop] = [];
|
||||
dic[prop].push(watcher);
|
||||
}
|
||||
}
|
||||
};
|
||||
var Observable = ObservableMixin();
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* This is an auto-proxying class that adds profiling to all method.
|
||||
* Can be quite useful to track where time is spent in the GUI.
|
||||
* Usage: Just add "extens Profilable" to your class
|
||||
* and call super() in the constructor as appropriate.
|
||||
* Give your class a name if you want the ouput to be usable.
|
||||
*
|
||||
* It is recommended to only use this class when actually working on profiling,
|
||||
* or the profiler2 graph will be very cluttered.
|
||||
*/
|
||||
var ProfilableMixin = (Parent) => class Profilable extends (() => Parent || Object)()
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
return new Proxy(this, {
|
||||
"get": (target, prop, receiver) => {
|
||||
let ret = Reflect.get(target, prop);
|
||||
if (typeof ret !== 'function')
|
||||
return ret;
|
||||
{
|
||||
ret = ret.bind(receiver);
|
||||
return (...a) => {
|
||||
let ret2;
|
||||
Engine.ProfileStart(target.constructor.name + ":" + prop);
|
||||
ret2 = ret(...a);
|
||||
Engine.ProfileStop();
|
||||
return ret2;
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
var Profilable = ProfilableMixin();
|
||||
@@ -178,12 +178,15 @@ function formatPlayerInfo(playerDataArray, playerStates)
|
||||
/**
|
||||
* Sets an additional map label, map preview image and describes the chosen game settings more closely.
|
||||
*
|
||||
* Requires g_GameAttributes and g_VictoryConditions.
|
||||
* Requires g_VictoryConditions.
|
||||
*
|
||||
* @param gameSettings - Serialised-format/JSON game settings.
|
||||
* (this takes serialised data to avoid loadings the gamesettings in the session GUI page)
|
||||
*/
|
||||
function getGameDescription(mapCache)
|
||||
function getGameDescription(gameSettings, mapCache)
|
||||
{
|
||||
let titles = [];
|
||||
if (!g_GameAttributes.settings.VictoryConditions.length)
|
||||
if (!gameSettings.settings.VictoryConditions.length)
|
||||
titles.push({
|
||||
"label": translateWithContext("victory condition", "Endless Game"),
|
||||
"value": translate("No winner will be determined, even if everyone is defeated.")
|
||||
@@ -191,13 +194,13 @@ function getGameDescription(mapCache)
|
||||
|
||||
for (let victoryCondition of g_VictoryConditions)
|
||||
{
|
||||
if (g_GameAttributes.settings.VictoryConditions.indexOf(victoryCondition.Name) == -1)
|
||||
if (gameSettings.settings.VictoryConditions.indexOf(victoryCondition.Name) == -1)
|
||||
continue;
|
||||
|
||||
let title = translateVictoryCondition(victoryCondition.Name);
|
||||
if (victoryCondition.Name == "wonder")
|
||||
{
|
||||
let wonderDuration = Math.round(g_GameAttributes.settings.WonderDuration);
|
||||
let wonderDuration = Math.round(gameSettings.settings.WonderDuration);
|
||||
title = sprintf(
|
||||
translatePluralWithContext(
|
||||
"victory condition",
|
||||
@@ -211,7 +214,7 @@ function getGameDescription(mapCache)
|
||||
let isCaptureTheRelic = victoryCondition.Name == "capture_the_relic";
|
||||
if (isCaptureTheRelic)
|
||||
{
|
||||
let relicDuration = Math.round(g_GameAttributes.settings.RelicDuration);
|
||||
let relicDuration = Math.round(gameSettings.settings.RelicDuration);
|
||||
title = sprintf(
|
||||
translatePluralWithContext(
|
||||
"victory condition",
|
||||
@@ -230,11 +233,11 @@ function getGameDescription(mapCache)
|
||||
if (isCaptureTheRelic)
|
||||
titles.push({
|
||||
"label": translate("Relic Count"),
|
||||
"value": Math.round(g_GameAttributes.settings.RelicCount)
|
||||
"value": Math.round(gameSettings.settings.RelicCount)
|
||||
});
|
||||
|
||||
if (victoryCondition.Name == "regicide")
|
||||
if (g_GameAttributes.settings.RegicideGarrison)
|
||||
if (gameSettings.settings.RegicideGarrison)
|
||||
titles.push({
|
||||
"label": translate("Hero Garrison"),
|
||||
"value": translate("Heroes can be garrisoned.")
|
||||
@@ -246,14 +249,14 @@ function getGameDescription(mapCache)
|
||||
});
|
||||
}
|
||||
|
||||
if (g_GameAttributes.settings.RatingEnabled &&
|
||||
g_GameAttributes.settings.PlayerData.length == 2)
|
||||
if (gameSettings.settings.RatingEnabled &&
|
||||
gameSettings.settings.PlayerData.length == 2)
|
||||
titles.push({
|
||||
"label": translate("Rated game"),
|
||||
"value": translate("When the winner of this match is determined, the lobby score will be adapted.")
|
||||
});
|
||||
|
||||
if (g_GameAttributes.settings.LockTeams)
|
||||
if (gameSettings.settings.LockTeams)
|
||||
titles.push({
|
||||
"label": translate("Locked Teams"),
|
||||
"value": translate("Players can't change the initial teams.")
|
||||
@@ -264,7 +267,7 @@ function getGameDescription(mapCache)
|
||||
"value": translate("Players can make alliances and declare war on allies.")
|
||||
});
|
||||
|
||||
if (g_GameAttributes.settings.LastManStanding)
|
||||
if (gameSettings.settings.LastManStanding)
|
||||
titles.push({
|
||||
"label": translate("Last Man Standing"),
|
||||
"value": translate("Only one player can win the game. If the remaining players are allies, the game continues until only one remains.")
|
||||
@@ -275,11 +278,11 @@ function getGameDescription(mapCache)
|
||||
"value": translate("If one player wins, his or her allies win too. If one group of allies remains, they win.")
|
||||
});
|
||||
|
||||
let ceasefire = Math.round(g_GameAttributes.settings.Ceasefire);
|
||||
let ceasefire = Math.round(gameSettings.settings.Ceasefire);
|
||||
titles.push({
|
||||
"label": translate("Ceasefire"),
|
||||
"value":
|
||||
ceasefire == 0 ?
|
||||
!ceasefire ?
|
||||
translate("disabled") :
|
||||
sprintf(translatePlural(
|
||||
"For the first minute, other players will stay neutral.",
|
||||
@@ -288,7 +291,7 @@ function getGameDescription(mapCache)
|
||||
{ "min": ceasefire })
|
||||
});
|
||||
|
||||
if (g_GameAttributes.map == "random")
|
||||
if (gameSettings.map == "random")
|
||||
titles.push({
|
||||
"label": translateWithContext("Map Selection", "Random Map"),
|
||||
"value": translate("Randomly select a map from the list.")
|
||||
@@ -298,23 +301,23 @@ function getGameDescription(mapCache)
|
||||
titles.push({
|
||||
"label": translate("Map Name"),
|
||||
"value": mapCache.translateMapName(
|
||||
mapCache.getTranslatableMapName(g_GameAttributes.mapType, g_GameAttributes.map, g_GameAttributes))
|
||||
mapCache.getTranslatableMapName(gameSettings.mapType, gameSettings.map, gameSettings))
|
||||
});
|
||||
|
||||
titles.push({
|
||||
"label": translate("Map Description"),
|
||||
"value": mapCache.getTranslatedMapDescription(g_GameAttributes.mapType, g_GameAttributes.map)
|
||||
"value": mapCache.getTranslatedMapDescription(gameSettings.mapType, gameSettings.map)
|
||||
});
|
||||
}
|
||||
|
||||
titles.push({
|
||||
"label": translate("Map Type"),
|
||||
"value": g_MapTypes.Title[g_MapTypes.Name.indexOf(g_GameAttributes.mapType)]
|
||||
"value": g_MapTypes.Title[g_MapTypes.Name.indexOf(gameSettings.mapType)]
|
||||
});
|
||||
|
||||
if (g_GameAttributes.mapType == "random")
|
||||
if (gameSettings.mapType == "random")
|
||||
{
|
||||
let mapSize = g_MapSizes.Name[g_MapSizes.Tiles.indexOf(g_GameAttributes.settings.Size)];
|
||||
let mapSize = g_MapSizes.Name[g_MapSizes.Tiles.indexOf(gameSettings.settings.Size)];
|
||||
if (mapSize)
|
||||
titles.push({
|
||||
"label": translate("Map Size"),
|
||||
@@ -322,90 +325,90 @@ function getGameDescription(mapCache)
|
||||
});
|
||||
}
|
||||
|
||||
if (g_GameAttributes.settings.Biome)
|
||||
if (gameSettings.settings.Biome)
|
||||
{
|
||||
let biome = g_Settings.Biomes.find(b => b.Id == g_GameAttributes.settings.Biome);
|
||||
let biome = g_Settings.Biomes.find(b => b.Id == gameSettings.settings.Biome);
|
||||
titles.push({
|
||||
"label": biome ? biome.Title : translateWithContext("biome", "Random Biome"),
|
||||
"value": biome ? biome.Description : translate("Randomly select a biome from the list.")
|
||||
});
|
||||
}
|
||||
|
||||
if (g_GameAttributes.settings.TriggerDifficulty !== undefined)
|
||||
if (gameSettings.settings.TriggerDifficulty !== undefined)
|
||||
{
|
||||
let triggerDifficulty = g_Settings.TriggerDifficulties.find(difficulty => difficulty.Difficulty == g_GameAttributes.settings.TriggerDifficulty);
|
||||
let triggerDifficulty = g_Settings.TriggerDifficulties.find(difficulty => difficulty.Difficulty == gameSettings.settings.TriggerDifficulty);
|
||||
titles.push({
|
||||
"label": triggerDifficulty.Title,
|
||||
"value": triggerDifficulty.Tooltip
|
||||
});
|
||||
}
|
||||
|
||||
if (g_GameAttributes.settings.Nomad !== undefined)
|
||||
if (gameSettings.settings.Nomad !== undefined)
|
||||
titles.push({
|
||||
"label": g_GameAttributes.settings.Nomad ? translate("Nomad Mode") : translate("Civic Centers"),
|
||||
"label": gameSettings.settings.Nomad ? translate("Nomad Mode") : translate("Civic Centers"),
|
||||
"value":
|
||||
g_GameAttributes.settings.Nomad ?
|
||||
gameSettings.settings.Nomad ?
|
||||
translate("Players start with only few units and have to find a suitable place to build their city.") :
|
||||
translate("Players start with a Civic Center.")
|
||||
});
|
||||
|
||||
if (g_GameAttributes.settings.StartingResources !== undefined)
|
||||
if (gameSettings.settings.StartingResources !== undefined)
|
||||
titles.push({
|
||||
"label": translate("Starting Resources"),
|
||||
"value":
|
||||
g_GameAttributes.settings.PlayerData &&
|
||||
g_GameAttributes.settings.PlayerData.some(pData => pData && pData.Resources !== undefined) ?
|
||||
gameSettings.settings.PlayerData &&
|
||||
gameSettings.settings.PlayerData.some(pData => pData && pData.Resources !== undefined) ?
|
||||
translateWithContext("starting resources", "Per Player") :
|
||||
sprintf(translate("%(startingResourcesTitle)s (%(amount)s)"), {
|
||||
"startingResourcesTitle":
|
||||
g_StartingResources.Title[
|
||||
g_StartingResources.Resources.indexOf(
|
||||
g_GameAttributes.settings.StartingResources)],
|
||||
"amount": g_GameAttributes.settings.StartingResources
|
||||
gameSettings.settings.StartingResources)],
|
||||
"amount": gameSettings.settings.StartingResources
|
||||
})
|
||||
});
|
||||
|
||||
if (g_GameAttributes.settings.PopulationCap !== undefined)
|
||||
if (gameSettings.settings.PopulationCap !== undefined)
|
||||
titles.push({
|
||||
"label": translate("Population Limit"),
|
||||
"value":
|
||||
g_GameAttributes.settings.PlayerData &&
|
||||
g_GameAttributes.settings.PlayerData.some(pData => pData && pData.PopulationLimit !== undefined) ?
|
||||
gameSettings.settings.PlayerData &&
|
||||
gameSettings.settings.PlayerData.some(pData => pData && pData.PopulationLimit !== undefined) ?
|
||||
translateWithContext("population limit", "Per Player") :
|
||||
g_PopulationCapacities.Title[
|
||||
g_PopulationCapacities.Population.indexOf(
|
||||
g_GameAttributes.settings.PopulationCap)]
|
||||
gameSettings.settings.PopulationCap)]
|
||||
});
|
||||
|
||||
if (g_GameAttributes.settings.WorldPopulationCap !== undefined)
|
||||
if (gameSettings.settings.WorldPopulationCap !== undefined)
|
||||
titles.push({
|
||||
"label": translate("World Population Cap"),
|
||||
"value":
|
||||
g_WorldPopulationCapacities.Title[
|
||||
g_WorldPopulationCapacities.Population.indexOf(
|
||||
g_GameAttributes.settings.WorldPopulationCap)]
|
||||
gameSettings.settings.WorldPopulationCap)]
|
||||
});
|
||||
|
||||
titles.push({
|
||||
"label": translate("Treasures"),
|
||||
"value": g_GameAttributes.settings.DisableTreasures ?
|
||||
"value": gameSettings.settings.DisableTreasures ?
|
||||
translateWithContext("treasures", "Disabled") :
|
||||
translateWithContext("treasures", "As defined by the map.")
|
||||
});
|
||||
|
||||
titles.push({
|
||||
"label": translate("Revealed Map"),
|
||||
"value": g_GameAttributes.settings.RevealMap
|
||||
"value": gameSettings.settings.RevealMap
|
||||
});
|
||||
|
||||
titles.push({
|
||||
"label": translate("Explored Map"),
|
||||
"value": g_GameAttributes.settings.ExploreMap
|
||||
"value": gameSettings.settings.ExploreMap
|
||||
});
|
||||
|
||||
titles.push({
|
||||
"label": translate("Cheats"),
|
||||
"value": g_GameAttributes.settings.CheatsEnabled
|
||||
"value": gameSettings.settings.CheatsEnabled
|
||||
});
|
||||
|
||||
return titles.map(title => sprintf(translate("%(label)s %(details)s"), {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* The game settings are split in subclasses that are only responsible
|
||||
* for a logical subset of settings.
|
||||
* These are observables so updates are automated.
|
||||
*/
|
||||
class GameSetting extends Observable /* ProfilableMixin(Observable) /* Replace to profile automatically. */
|
||||
{
|
||||
constructor(settings)
|
||||
{
|
||||
super();
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
getDefaultValue(settingsProp, dataProp)
|
||||
{
|
||||
for (let index in g_Settings[settingsProp])
|
||||
if (g_Settings[settingsProp][index].Default)
|
||||
return g_Settings[settingsProp][index][dataProp];
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getLegacySetting(attrib, name)
|
||||
{
|
||||
if (!attrib || !attrib.settings || attrib.settings[name] === undefined)
|
||||
return undefined;
|
||||
return attrib.settings[name];
|
||||
}
|
||||
|
||||
getMapSetting(name)
|
||||
{
|
||||
if (!this.settings.map.data || !this.settings.map.data.settings ||
|
||||
this.settings.map.data.settings[name] === undefined)
|
||||
return undefined;
|
||||
return this.settings.map.data.settings[name];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Data store for game settings.
|
||||
*
|
||||
* This is intended as a helper to create the settings object for a game.
|
||||
* This object is referred to as:
|
||||
* - g_GameAttributes in the GUI session context
|
||||
* - InitAttributes in the JS simulation context
|
||||
* - Either InitAttributes or MapSettings in the C++ simulation.
|
||||
* Settings can depend on each other, and the map provides many.
|
||||
* This class's job is thus to provide a simpler interface around that.
|
||||
*/
|
||||
class GameSettings
|
||||
{
|
||||
init(mapCache)
|
||||
{
|
||||
if (!mapCache)
|
||||
mapCache = new MapCache();
|
||||
Object.defineProperty(this, "mapCache", {
|
||||
"value": mapCache,
|
||||
});
|
||||
|
||||
// Load all possible civ data - don't presume that some will be available.
|
||||
Object.defineProperty(this, "civData", {
|
||||
"value": loadCivData(false, false),
|
||||
});
|
||||
|
||||
Object.defineProperty(this, "isNetworked", {
|
||||
"value": Engine.HasNetClient(),
|
||||
});
|
||||
|
||||
Object.defineProperty(this, "isController", {
|
||||
"value": !this.isNetworked || Engine.IsNetController(),
|
||||
});
|
||||
|
||||
// Load attributes as regular enumerable (i.e. iterable) properties.
|
||||
for (let comp in GameSettings.prototype.Attributes)
|
||||
{
|
||||
let name = comp[0].toLowerCase() + comp.substr(1);
|
||||
if (name in this)
|
||||
error("Game Settings attribute '" + name + "' is already used.");
|
||||
this[name] = new GameSettings.prototype.Attributes[comp](this);
|
||||
}
|
||||
for (let comp in this)
|
||||
this[comp].init();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 'Serialize' the settings into the InitAttributes format,
|
||||
* which can then be saved as JSON.
|
||||
* Used to set the InitAttributes, for network synching, for hotloading & for persistence.
|
||||
* TODO: it would probably be better to have different paths for at least a few of these.
|
||||
*/
|
||||
toInitAttributes()
|
||||
{
|
||||
let attribs = {
|
||||
"settings": {}
|
||||
};
|
||||
for (let comp in this)
|
||||
if (this[comp].toInitAttributes)
|
||||
this[comp].toInitAttributes(attribs);
|
||||
|
||||
return attribs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize from a the InitAttributes format (i.e. parsed JSON).
|
||||
* TODO: this could/should maybe support partial deserialization,
|
||||
* which means MP might actually send only the bits that change.
|
||||
*/
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
// Settings depend on the map, but some settings
|
||||
// may also be illegal for a given map.
|
||||
// It would be good to validate, but just bulk-accept at the moment.
|
||||
// There is some light order-dependency between settings.
|
||||
// First deserialize the map, then the player #, then victory conditions, then the rest.
|
||||
// TODO: there's a DAG in there.
|
||||
this.map.fromInitAttributes(attribs);
|
||||
this.playerCount.fromInitAttributes(attribs);
|
||||
this.victoryConditions.fromInitAttributes(attribs);
|
||||
for (let comp in this)
|
||||
if (this[comp].fromInitAttributes &&
|
||||
comp !== "map" && comp !== "playerCount" && comp !== "victoryConditions")
|
||||
this[comp].fromInitAttributes(attribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the game settings to the server.
|
||||
*/
|
||||
setNetworkGameAttributes()
|
||||
{
|
||||
if (this.isNetworked && this.isController)
|
||||
Engine.SetNetworkGameAttributes(this.toInitAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Change "random" settings into their proper settings.
|
||||
*/
|
||||
pickRandomItems()
|
||||
{
|
||||
let components = Object.keys(this);
|
||||
let i = 0;
|
||||
while (components.length && i < 100)
|
||||
{
|
||||
// Re-pick if any random setting was unrandomised,
|
||||
// to make sure dependencies are cleared.
|
||||
// TODO: there's probably a better way to handle this.
|
||||
components = components.filter(comp => this[comp].pickRandomItems ?
|
||||
!!this[comp].pickRandomItems() : false);
|
||||
++i;
|
||||
}
|
||||
if (i === 100)
|
||||
{
|
||||
throw new Error("Infinite loop picking random items, remains : " + uneval(components));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the game & switch to the loading page.
|
||||
* This is here because there's limited value in having a separate folder/file for it,
|
||||
* since you'll need a GameSettings object anyways.
|
||||
* @param playerAssignments - A dict of 'local'/GUID per player and their name/slot.
|
||||
*/
|
||||
launchGame(playerAssignments)
|
||||
{
|
||||
this.pickRandomItems();
|
||||
|
||||
Engine.SetRankedGame(this.rating.enabled);
|
||||
this.setNetworkGameAttributes();
|
||||
|
||||
// Replace player names with the real players.
|
||||
for (let guid in playerAssignments)
|
||||
if (playerAssignments[guid].player !== -1)
|
||||
this.playerName.values[playerAssignments[guid].player -1] = playerAssignments[guid].name;
|
||||
|
||||
// NB: for multiplayer support, the clients must be listening to "start" net messages.
|
||||
if (this.isNetworked)
|
||||
Engine.StartNetworkGame();
|
||||
else
|
||||
Engine.StartGame(this.toInitAttributes(), playerAssignments.local.player);
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(GameSettings.prototype, "Attributes", {
|
||||
"value": {},
|
||||
"enumerable": false,
|
||||
"writable": true,
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
GameSettings.prototype.Attributes.Biome = class Biome extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.biomes = loadBiomes();
|
||||
this.biomeData = {};
|
||||
for (let biome of this.biomes)
|
||||
this.biomeData[biome.Id] = biome;
|
||||
this.cachedMapData = undefined;
|
||||
|
||||
this.biome = undefined;
|
||||
// NB: random is always available.
|
||||
this.available = new Set();
|
||||
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!this.biome)
|
||||
return;
|
||||
attribs.settings.Biome = this.biome;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "Biome"))
|
||||
this.setBiome(undefined);
|
||||
else
|
||||
this.setBiome(this.getLegacySetting(attribs, "Biome"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
let mapData = this.settings.map.data;
|
||||
if (mapData && mapData.settings && mapData.settings.SupportedBiomes !== undefined)
|
||||
{
|
||||
if (mapData.settings.SupportedBiomes === this.cachedMapData)
|
||||
return;
|
||||
this.cachedMapData = mapData.settings.SupportedBiomes;
|
||||
this.available = new Set(this.biomes.filter(biome => biome.Id.indexOf(mapData.settings.SupportedBiomes) !== -1)
|
||||
.map(biome => biome.Id));
|
||||
this.biome = "random";
|
||||
}
|
||||
else if (this.cachedMapData !== undefined)
|
||||
{
|
||||
this.cachedMapData = undefined;
|
||||
this.available = new Set();
|
||||
this.biome = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
setBiome(biome)
|
||||
{
|
||||
// TODO: more validation.
|
||||
if (this.available.size)
|
||||
this.biome = biome || "random";
|
||||
else
|
||||
this.biome = undefined;
|
||||
}
|
||||
|
||||
getAvailableBiomeData()
|
||||
{
|
||||
return Array.from(this.available).map(biome => this.biomeData[biome]);
|
||||
}
|
||||
|
||||
getData()
|
||||
{
|
||||
if (!this.biome)
|
||||
return undefined;
|
||||
return this.biomeData[this.biome];
|
||||
}
|
||||
|
||||
pickRandomItems()
|
||||
{
|
||||
// If the map is random, we need to wait until it selects to know if we need to pick a biome.
|
||||
if (this.settings.map.map === "random")
|
||||
return true;
|
||||
|
||||
if (this.biome !== "random")
|
||||
return false;
|
||||
this.biome = pickRandom(this.available);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Store campaign-related data.
|
||||
* This is just a passthrough and makes no assumption about the data.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.CampaignData = class CampaignData extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.campaignData = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.value = attribs.campaignData;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
GameSettings.prototype.Attributes.Ceasefire = class Ceasefire extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.value = 0;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!this.value)
|
||||
return;
|
||||
attribs.settings.Ceasefire = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "Ceasefire"))
|
||||
this.value = 0;
|
||||
else
|
||||
this.value = +this.getLegacySetting(attribs, "Ceasefire");
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
if (!this.getMapSetting("Ceasefire"))
|
||||
this.value = 0;
|
||||
else
|
||||
this.value = +this.getMapSetting("Ceasefire");
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
this.value = Math.round(val);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
GameSettings.prototype.Attributes.Cheats = class Cheats extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.enabled = false;
|
||||
this.settings.rating.watch(() => this.maybeUpdate(), ["enabled"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.CheatsEnabled = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.enabled = !!this.getLegacySetting(attribs, "CheatsEnabled");
|
||||
}
|
||||
|
||||
_set(enabled)
|
||||
{
|
||||
this.enabled = (enabled && !this.settings.rating.enabled);
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this._set(enabled);
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this._set(this.enabled);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* This doesn't have a GUI setting.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.CircularMap = class CircularMap extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.CircularMap = this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptionally, this setting has no Deserialize: it's entirely determined by the map
|
||||
*/
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
this.value = this.getMapSetting("CircularMap") || false;
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
this.value = val;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
GameSettings.prototype.Attributes.Daytime = class Daytime extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.data = undefined;
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.Daytime = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "Daytime"))
|
||||
this.setValue(undefined);
|
||||
else
|
||||
this.setValue(this.getLegacySetting(attribs, "Daytime"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
let mapData = this.settings.map.data;
|
||||
if (!mapData || !mapData.settings || !mapData.settings.Daytime)
|
||||
{
|
||||
this.value = undefined;
|
||||
this.data = undefined;
|
||||
return;
|
||||
}
|
||||
// TODO: validation
|
||||
this.data = mapData.settings.Daytime;
|
||||
this.value = "random";
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
// TODO: more validation.
|
||||
if (this.data)
|
||||
this.value = val || "random";
|
||||
else
|
||||
this.value = undefined;
|
||||
}
|
||||
|
||||
pickRandomItems()
|
||||
{
|
||||
if (this.value !== "random")
|
||||
return false;
|
||||
this.value = pickRandom(this.data).Id;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
GameSettings.prototype.Attributes.DisableSpies = class DisableSpies extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.enabled = false;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.DisableSpies = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.enabled = !!this.getLegacySetting(attribs, "DisableSpies");
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setEnabled(!!this.getMapSetting("DisableSpies"));
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
GameSettings.prototype.Attributes.DisableTreasures = class DisableTreasures extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.enabled = false;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.DisableTreasures = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.enabled = !!this.getLegacySetting(attribs, "DisableTreasures");
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setEnabled(!!this.getMapSetting("DisableTreasures"));
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* TODO: this would probably be better handled by map triggers.
|
||||
* This doesn't have a GUI setting.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.DisabledTechnologies = class DisabledTechnologies extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.DisabledTechnologies = this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptionally, this setting has no Deserialize: it's entirely determined by the map
|
||||
*/
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("DisabledTechnologies"))
|
||||
this.setValue(undefined);
|
||||
else
|
||||
this.setValue(this.getMapSetting("DisabledTechnologies"));
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
this.value = val;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* TODO: this would probably be better handled by map triggers.
|
||||
* This doesn't have a GUI setting.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.DisabledTemplates = class DisabledTemplates extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.DisabledTemplates = this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptionally, this setting has no Deserialize: it's entirely determined by the map
|
||||
*/
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("DisabledTemplates"))
|
||||
this.setValue(undefined);
|
||||
else
|
||||
this.setValue(this.getMapSetting("DisabledTemplates"));
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
this.value = val;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
GameSettings.prototype.Attributes.GameSpeed = class GameSpeed extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.gameSpeed = 1;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.gameSpeed = this.gameSpeed;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.gameSpeed)
|
||||
return;
|
||||
this.gameSpeed = +attribs.gameSpeed;
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("gameSpeed"))
|
||||
return;
|
||||
this.setSpeed(+this.getMapSetting("gameSpeed"));
|
||||
}
|
||||
|
||||
setSpeed(speed)
|
||||
{
|
||||
this.gameSpeed = speed;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
GameSettings.prototype.Attributes.Landscape = class Landscape extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.data = undefined;
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.Landscape = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "Landscape"))
|
||||
this.setValue(undefined);
|
||||
else
|
||||
this.setValue(this.getLegacySetting(attribs, "Landscape"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("Landscapes"))
|
||||
{
|
||||
this.value = undefined;
|
||||
this.data = undefined;
|
||||
return;
|
||||
}
|
||||
// TODO: validation
|
||||
this.data = this.getMapSetting("Landscapes");
|
||||
this.value = "random";
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
// TODO: more validation.
|
||||
if (this.data)
|
||||
this.value = val || "random";
|
||||
else
|
||||
this.value = undefined;
|
||||
}
|
||||
|
||||
getPreviewFilename()
|
||||
{
|
||||
if (!this.value)
|
||||
return undefined;
|
||||
for (let group of this.data)
|
||||
for (let item of group.Items)
|
||||
if (item.Id == this.value)
|
||||
return item.Preview;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
pickRandomItems()
|
||||
{
|
||||
if (!this.value || !this.value.startsWith("random"))
|
||||
return false;
|
||||
|
||||
let items = [];
|
||||
if (this.value.indexOf("_") !== -1)
|
||||
{
|
||||
let subgroup = this.data.find(x => x.Id == this.value);
|
||||
items = subgroup.Items.map(x => x.Id);
|
||||
}
|
||||
else
|
||||
items = this.data.map(x => x.Items.map(item => item.Id));
|
||||
|
||||
this.value = pickRandom(items);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
GameSettings.prototype.Attributes.LastManStanding = class LastManStanding extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.available = !this.settings.lockedTeams.enabled;
|
||||
this.enabled = false;
|
||||
this.settings.lockedTeams.watch(() => this.maybeUpdate(), ["enabled"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.LastManStanding = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.enabled = !!this.getLegacySetting(attribs, "LastManStanding");
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setEnabled(!!this.getMapSetting("LastManStanding"));
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.available = !this.settings.lockedTeams.enabled;
|
||||
this.enabled = (enabled && this.available);
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this.setEnabled(this.enabled);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
GameSettings.prototype.Attributes.LockedTeams = class LockedTeams extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.enabled = false;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
this.settings.rating.watch(() => this.onRatingChange(), ["enabled"]);
|
||||
this.onRatingChange();
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.LockTeams = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.enabled = !!this.getLegacySetting(attribs, "LockTeams");
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setEnabled(!!this.getMapSetting("LockTeams"));
|
||||
}
|
||||
|
||||
onRatingChange()
|
||||
{
|
||||
if (this.settings.rating.enabled)
|
||||
{
|
||||
this.available = false;
|
||||
this.setEnabled(true);
|
||||
}
|
||||
else
|
||||
this.available = true;
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Map choice. This handles:
|
||||
* - the map itself
|
||||
* - map type (which is mostly a GUI thing and should probably be refactored out)
|
||||
* - map script (for random maps).
|
||||
* When a non-"random" map is selected, the map 'script settings' are available at this.data.
|
||||
* TODO: the map description is currently tied to the map itself.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.Map = class Map extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.watch(() => this.updateMapMetadata(), ["map"]);
|
||||
this.randomOptions = [];
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.map = this.map;
|
||||
attribs.mapType = this.type;
|
||||
if (this.script)
|
||||
attribs.script = this.script;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (attribs.mapType)
|
||||
this.setType(attribs.mapType);
|
||||
|
||||
if (!attribs.map)
|
||||
return;
|
||||
|
||||
this.selectMap(attribs.map);
|
||||
}
|
||||
|
||||
setType(mapType)
|
||||
{
|
||||
this.type = mapType;
|
||||
}
|
||||
|
||||
selectMap(map)
|
||||
{
|
||||
this.data = this.settings.mapCache.getMapData(this.type, map);
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
updateMapMetadata()
|
||||
{
|
||||
if (this.type == "random" && this.data)
|
||||
this.script = this.data.settings.Script;
|
||||
else
|
||||
this.script = undefined;
|
||||
}
|
||||
|
||||
pickRandomItems()
|
||||
{
|
||||
if (this.map !== "random")
|
||||
return false;
|
||||
this.selectMap(pickRandom(this.randomOptions));
|
||||
return true;
|
||||
}
|
||||
|
||||
setRandomOptions(options)
|
||||
{
|
||||
this.randomOptions = clone(options);
|
||||
if (this.randomOptions.indexOf("random") !== -1)
|
||||
this.randomOptions.splice(this.randomOptions.indexOf("random"), 1);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
GameSettings.prototype.Attributes.MapExploration = class MapExploration extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.explored = false;
|
||||
this.revealed = false;
|
||||
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.RevealMap = this.revealed;
|
||||
attribs.settings.ExploreMap = this.explored;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.explored = !!this.getLegacySetting(attribs, "ExploreMap");
|
||||
this.revealed = !!this.getLegacySetting(attribs, "RevealMap");
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setExplored(this.getMapSetting("ExploreMap"));
|
||||
this.setRevealed(this.getMapSetting("RevealMap"));
|
||||
}
|
||||
|
||||
setExplored(enabled)
|
||||
{
|
||||
this.explored = enabled;
|
||||
this.revealed = this.revealed && this.explored;
|
||||
}
|
||||
|
||||
setRevealed(enabled)
|
||||
{
|
||||
this.explored = this.explored || enabled;
|
||||
this.revealed = enabled;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Map name.
|
||||
* This is usually just the regular map name, but can be overwritten.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.MapName = class MapName extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.Name = this.value;
|
||||
else
|
||||
{
|
||||
// Copy from the map data by default - this helps make InitAttributes self-sufficient,
|
||||
// which is nice for replays / saved games.
|
||||
// Fallback to the map name to avoid 'undefined' errors.
|
||||
attribs.settings.Name = this.settings.map?.data?.settings?.Name || this.settings.map.map;
|
||||
}
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
// Ser/Deser from a different attribute name as a poor man's not-persisted-setting.
|
||||
// TODO: split this off more properly.
|
||||
if (attribs.mapName)
|
||||
this.value = attribs.mapName;
|
||||
}
|
||||
|
||||
set(name)
|
||||
{
|
||||
this.value = name;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Map Preview.
|
||||
* Can optionally overwrite the default map preview.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.MapPreview = class MapPreview extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.isDefault = true;
|
||||
this.settings.map.watch(() => this.updatePreview(), ["map"]);
|
||||
this.settings.biome.watch(() => this.updatePreview(), ["biome"]);
|
||||
this.settings.landscape.watch(() => this.updatePreview(), ["value"]);
|
||||
this.settings.daytime.watch(() => this.updatePreview(), ["value"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
// TODO: this shouldn't be persisted, only serialised for the game proper.
|
||||
if (this.value)
|
||||
attribs.mapPreview = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
// For now - this won't be deserialized or persisted match settings will be problematic.
|
||||
}
|
||||
|
||||
getPreviewForSubtype(basepath, subtype)
|
||||
{
|
||||
if (!subtype)
|
||||
return undefined;
|
||||
let substr = subtype.substr(subtype.lastIndexOf("/") + 1);
|
||||
let path = basepath + "_" + substr + ".png";
|
||||
if (this.settings.mapCache.previewExists(path))
|
||||
return this.settings.mapCache.getMapPreview(this.settings.map.type,
|
||||
this.settings.map.map, path);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getLandscapePreview()
|
||||
{
|
||||
let filename = this.settings.landscape.getPreviewFilename();
|
||||
if (!filename)
|
||||
return undefined;
|
||||
return this.settings.mapCache.getMapPreview(this.settings.map.type,
|
||||
this.settings.map.map, filename);
|
||||
}
|
||||
|
||||
updatePreview()
|
||||
{
|
||||
// Don't overwrite the preview if it's been manually set.
|
||||
if (!this.isDefault)
|
||||
return;
|
||||
|
||||
if (!this.settings.map.map)
|
||||
{
|
||||
this.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// This handles "random" map type (mostly for convenience).
|
||||
let mapPath = basename(this.settings.map.map);
|
||||
this.value = this.getPreviewForSubtype(mapPath, this.settings.biome.biome) ||
|
||||
this.getLandscapePreview() ||
|
||||
this.getPreviewForSubtype(mapPath, this.settings.daytime.value) ||
|
||||
this.settings.mapCache.getMapPreview(this.settings.map.type, this.settings.map.map);
|
||||
}
|
||||
|
||||
setCustom(preview)
|
||||
{
|
||||
this.isDefault = false;
|
||||
this.value = preview;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
GameSettings.prototype.Attributes.MapSize = class MapSize extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.defaultValue = this.getDefaultValue("MapSizes", "Tiles") || 256;
|
||||
this.setSize(this.defaultValue);
|
||||
this.settings.map.watch(() => this.onTypeChange(), ["type"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.settings.map.type === "random")
|
||||
attribs.settings.Size = this.size;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!!this.getLegacySetting(attribs, "Size"))
|
||||
this.setSize(this.getLegacySetting(attribs, "Size"));
|
||||
}
|
||||
|
||||
setSize(size)
|
||||
{
|
||||
this.available = this.settings.map.type === "random";
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
onTypeChange(old)
|
||||
{
|
||||
if (this.settings.map.type === "random" && old !== "random")
|
||||
this.setSize(this.defaultValue);
|
||||
else if (this.settings.map.type !== "random")
|
||||
this.available = false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
GameSettings.prototype.Attributes.MatchID = class MatchID extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.matchID = Engine.GetMatchID();
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.matchID = this.matchID;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (attribs.matchID !== undefined)
|
||||
this.matchID = attribs.matchID;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
GameSettings.prototype.Attributes.Nomad = class Nomad extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.settings.map.type == "random")
|
||||
attribs.settings.Nomad = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.setEnabled(!!this.getLegacySetting(attribs, "Nomad"));
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Stores AI settings for all players.
|
||||
* Note that tby default, this does not assign AI
|
||||
* unless an AI bot is explicitly specified.
|
||||
* This is because:
|
||||
* - the regular GameSetup does that on its own
|
||||
* - this makes campaign/autostart scenarios easier to handle.
|
||||
* - cleans the code here.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.PlayerAI = class PlayerAI extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
// NB: watchers aren't auto-triggered when modifying array elements.
|
||||
this.values = [];
|
||||
this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.values.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i = 0; i < this.values.length; ++i)
|
||||
if (this.values[i])
|
||||
{
|
||||
attribs.settings.PlayerData[i].AI = this.values[i].bot;
|
||||
attribs.settings.PlayerData[i].AIDiff = this.values[i].difficulty;
|
||||
attribs.settings.PlayerData[i].AIBehavior = this.values[i].behavior;
|
||||
}
|
||||
else
|
||||
attribs.settings.PlayerData[i].AI = false;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "PlayerData"))
|
||||
return;
|
||||
let pData = this.getLegacySetting(attribs, "PlayerData");
|
||||
if (this.values.length < pData.length)
|
||||
this._resize(pData.length);
|
||||
for (let i in pData)
|
||||
{
|
||||
// Also covers the "" case.
|
||||
if (!pData[i] || !pData[i].AI)
|
||||
{
|
||||
this.set(+i, undefined);
|
||||
continue;
|
||||
}
|
||||
this.set(+i, {
|
||||
"bot": pData[i].AI,
|
||||
"difficulty": pData[i].AIDiff || +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"),
|
||||
"behavior": pData[i].AIBehavior || Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_resize(nb)
|
||||
{
|
||||
while (this.values.length > nb)
|
||||
this.values.pop();
|
||||
while (this.values.length < nb)
|
||||
this.values.push(undefined);
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
if (this.values.length === this.settings.playerCount.nbPlayers)
|
||||
return;
|
||||
this._resize(this.settings.playerCount.nbPlayers);
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
swap(sourceIndex, targetIndex)
|
||||
{
|
||||
[this.values[sourceIndex], this.values[targetIndex]] = [this.values[targetIndex], this.values[sourceIndex]];
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
set(playerIndex, botSettings)
|
||||
{
|
||||
this.values[playerIndex] = botSettings;
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
setAI(playerIndex, ai)
|
||||
{
|
||||
let old = this.values[playerIndex] ? this.values[playerIndex].bot : undefined;
|
||||
if (!ai)
|
||||
this.values[playerIndex] = undefined;
|
||||
else
|
||||
this.values[playerIndex].bot = ai;
|
||||
if (old !== (this.values[playerIndex] ? this.values[playerIndex].bot : undefined))
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
setBehavior(playerIndex, value)
|
||||
{
|
||||
if (!this.values[playerIndex])
|
||||
return;
|
||||
this.values[playerIndex].behavior = value;
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
setDifficulty(playerIndex, value)
|
||||
{
|
||||
if (!this.values[playerIndex])
|
||||
return;
|
||||
this.values[playerIndex].difficulty = value;
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
get(playerIndex)
|
||||
{
|
||||
return this.values[playerIndex];
|
||||
}
|
||||
|
||||
describe(playerIndex)
|
||||
{
|
||||
if (!this.values[playerIndex])
|
||||
return "";
|
||||
return translateAISettings({
|
||||
"AI": this.values[playerIndex].bot,
|
||||
"AIDiff": this.values[playerIndex].difficulty,
|
||||
"AIBehavior": this.values[playerIndex].behavior,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Stores civ settings for all players.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.PlayerCiv = class PlayerCiv extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
// NB: watchers aren't auto-triggered when modifying array elements.
|
||||
this.values = [];
|
||||
this.locked = [];
|
||||
this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.values.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.values)
|
||||
if (this.values[i])
|
||||
attribs.settings.PlayerData[i].Civ = this.values[i];
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "PlayerData"))
|
||||
return;
|
||||
let pData = this.getLegacySetting(attribs, "PlayerData");
|
||||
if (this.values.length < pData.length)
|
||||
this._resize(pData.length);
|
||||
for (let i in pData)
|
||||
if (pData[i] && pData[i].Civ)
|
||||
this.setValue(i, pData[i].Civ);
|
||||
}
|
||||
|
||||
_resize(nb)
|
||||
{
|
||||
while (this.values.length > nb)
|
||||
{
|
||||
this.values.pop();
|
||||
this.locked.pop();
|
||||
}
|
||||
while (this.values.length < nb)
|
||||
{
|
||||
this.values.push("random");
|
||||
this.locked.push(false);
|
||||
}
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
// Reset.
|
||||
if (this.settings.map.type == "scenario" ||
|
||||
this.getMapSetting("PlayerData") &&
|
||||
this.getMapSetting("PlayerData").some(data => data && data.Civ))
|
||||
{
|
||||
this._resize(0);
|
||||
this.maybeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this._resize(this.settings.playerCount.nbPlayers);
|
||||
this.values.forEach((c, i) => this._set(i, c));
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
pickRandomItems()
|
||||
{
|
||||
// Get a unique array of selectable cultures
|
||||
let cultures = Object.keys(this.settings.civData).filter(civ => this.settings.civData[civ].SelectableInGameSetup).map(civ => this.settings.civData[civ].Culture);
|
||||
cultures = cultures.filter((culture, index) => cultures.indexOf(culture) === index);
|
||||
|
||||
let picked = false;
|
||||
for (let i in this.values)
|
||||
{
|
||||
if (this.values[i] != "random")
|
||||
continue;
|
||||
picked = true;
|
||||
|
||||
// Pick a random civ of a random culture
|
||||
let culture = pickRandom(cultures);
|
||||
this.values[i] = pickRandom(Object.keys(this.settings.civData).filter(civ =>
|
||||
this.settings.civData[civ].Culture == culture && this.settings.civData[civ].SelectableInGameSetup));
|
||||
|
||||
}
|
||||
if (picked)
|
||||
this.trigger("values");
|
||||
|
||||
return picked;
|
||||
}
|
||||
|
||||
_getMapData(i)
|
||||
{
|
||||
let data = this.settings.map.data;
|
||||
if (!data || !data.settings || !data.settings.PlayerData)
|
||||
return undefined;
|
||||
if (data.settings.PlayerData.length <= i)
|
||||
return undefined;
|
||||
return data.settings.PlayerData[i].Civ;
|
||||
}
|
||||
|
||||
_set(playerIndex, value)
|
||||
{
|
||||
let map = this._getMapData(playerIndex);
|
||||
if (!!map)
|
||||
{
|
||||
this.values[playerIndex] = map;
|
||||
this.locked[playerIndex] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.values[playerIndex] = value;
|
||||
this.locked[playerIndex] = this.settings.map.type == "scenario";
|
||||
}
|
||||
}
|
||||
|
||||
setValue(playerIndex, val)
|
||||
{
|
||||
this._set(playerIndex, val);
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
swap(sourceIndex, targetIndex)
|
||||
{
|
||||
[this.values[sourceIndex], this.values[targetIndex]] = [this.values[targetIndex], this.values[sourceIndex]];
|
||||
[this.locked[sourceIndex], this.locked[targetIndex]] = [this.locked[targetIndex], this.locked[sourceIndex]];
|
||||
this.trigger("values");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Stores player color for all players.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.PlayerColor = class PlayerColor extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.defaultColors = g_Settings.PlayerDefaults.slice(1).map(pData => pData.Color);
|
||||
|
||||
this.watch(() => this.maybeUpdate(), ["available"]);
|
||||
this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
|
||||
// NB: watchers aren't auto-triggered when modifying array elements.
|
||||
this.values = [];
|
||||
this.locked = [];
|
||||
this._updateAvailable();
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.values.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.values)
|
||||
if (this.values[i])
|
||||
attribs.settings.PlayerData[i].Color = this.values[i];
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "PlayerData"))
|
||||
return;
|
||||
let pData = this.getLegacySetting(attribs, "PlayerData");
|
||||
if (this.values.length < pData.length)
|
||||
this._resize(pData.length);
|
||||
for (let i in pData)
|
||||
if (pData[i] && pData[i].Color)
|
||||
this.setColor(i, pData[i].Color);
|
||||
}
|
||||
|
||||
_resize(nb)
|
||||
{
|
||||
while (this.values.length > nb)
|
||||
{
|
||||
this.values.pop();
|
||||
this.locked.pop();
|
||||
}
|
||||
while (this.values.length < nb)
|
||||
{
|
||||
this.values.push(undefined);
|
||||
this.locked.push(false);
|
||||
}
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
// Reset.
|
||||
if (this.settings.map.type == "scenario" ||
|
||||
this.getMapSetting("PlayerData") &&
|
||||
this.getMapSetting("PlayerData").some(data => data && data.Color))
|
||||
{
|
||||
this._resize(0);
|
||||
this._updateAvailable();
|
||||
this.maybeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this._resize(this.settings.playerCount.nbPlayers);
|
||||
|
||||
this.values.forEach((c, i) => this._set(i, c));
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
_set(playerIndex, color)
|
||||
{
|
||||
let inUse = this.values.findIndex((otherColor, i) =>
|
||||
color && otherColor &&
|
||||
sameColor(color, otherColor));
|
||||
if (inUse !== -1 && inUse !== playerIndex)
|
||||
{
|
||||
// Swap colors.
|
||||
let col = this.values[playerIndex];
|
||||
this.values[playerIndex] = undefined;
|
||||
this._set(inUse, col);
|
||||
}
|
||||
if (!color || this.available.indexOf(color) == -1)
|
||||
{
|
||||
this.values[playerIndex] = color ?
|
||||
this._findClosestColor(color, this.available) :
|
||||
this._getUnusedColor();
|
||||
}
|
||||
else
|
||||
this.values[playerIndex] = color;
|
||||
}
|
||||
|
||||
get(playerIndex)
|
||||
{
|
||||
if (playerIndex >= this.values.length)
|
||||
return undefined;
|
||||
return this.values[playerIndex];
|
||||
}
|
||||
|
||||
setColor(playerIndex, color)
|
||||
{
|
||||
this._set(playerIndex, color);
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
swap(sourceIndex, targetIndex)
|
||||
{
|
||||
[this.values[sourceIndex], this.values[targetIndex]] = [this.values[targetIndex], this.values[sourceIndex]];
|
||||
[this.locked[sourceIndex], this.locked[targetIndex]] = [this.locked[targetIndex], this.locked[sourceIndex]];
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
_getMapData(i)
|
||||
{
|
||||
let data = this.settings.map.data;
|
||||
if (!data || !data.settings || !data.settings.PlayerData)
|
||||
return undefined;
|
||||
if (data.settings.PlayerData.length <= i)
|
||||
return undefined;
|
||||
return data.settings.PlayerData[i].Color;
|
||||
}
|
||||
|
||||
_updateAvailable()
|
||||
{
|
||||
// Pick colors that the map specifies, add most unsimilar default colors
|
||||
// Provide the access to g_MaxPlayers different colors, regardless of current playercount.
|
||||
let values = [];
|
||||
for (let i = 0; i < g_MaxPlayers; ++i)
|
||||
values.push(this._getMapData(i) ||
|
||||
this.defaultColors[i] || this._findFarthestUnusedColor(values));
|
||||
this.available = values;
|
||||
}
|
||||
|
||||
_findClosestColor(targetColor, colors)
|
||||
{
|
||||
let colorDistances = colors.map(color => colorDistance(color, targetColor));
|
||||
|
||||
let smallestDistance = colorDistances.find(
|
||||
distance => colorDistances.every(distance2 => distance2 >= distance));
|
||||
|
||||
return colors.find(color => colorDistance(color, targetColor) == smallestDistance);
|
||||
}
|
||||
|
||||
_findFarthestUnusedColor(values)
|
||||
{
|
||||
let farthestColor;
|
||||
let farthestDistance = 0;
|
||||
|
||||
for (let defaultColor of this.defaultColors)
|
||||
{
|
||||
let smallestDistance = Infinity;
|
||||
for (let usedColor of values)
|
||||
{
|
||||
let distance = colorDistance(usedColor, defaultColor);
|
||||
if (distance < smallestDistance)
|
||||
smallestDistance = distance;
|
||||
}
|
||||
|
||||
if (smallestDistance >= farthestDistance)
|
||||
{
|
||||
farthestColor = defaultColor;
|
||||
farthestDistance = smallestDistance;
|
||||
}
|
||||
}
|
||||
return farthestColor;
|
||||
}
|
||||
|
||||
_getUnusedColor()
|
||||
{
|
||||
return this.available.find(color => {
|
||||
return this.values.every(otherColor => !otherColor || !sameColor(color, otherColor));
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
GameSettings.prototype.Attributes.PlayerCount = class PlayerCount extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.nbPlayers = 1;
|
||||
this.settings.map.watch(() => this.onMapTypeChange(), ["type"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.nbPlayers)
|
||||
attribs.settings.PlayerData.push({});
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "PlayerData"))
|
||||
return;
|
||||
let pData = this.getLegacySetting(attribs, "PlayerData");
|
||||
if (this.nbPlayers !== pData.length)
|
||||
this.nbPlayers = pData.length;
|
||||
}
|
||||
|
||||
onMapTypeChange(old)
|
||||
{
|
||||
if (this.settings.map.type == "random" && old != "random")
|
||||
this.nbPlayers = 2;
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type == "random")
|
||||
return;
|
||||
if (!this.settings.map.data || !this.settings.map.data.settings ||
|
||||
!this.settings.map.data.settings.PlayerData)
|
||||
return;
|
||||
this.nbPlayers = this.settings.map.data.settings.PlayerData.length;
|
||||
}
|
||||
|
||||
reloadFromLegacy(data)
|
||||
{
|
||||
if (this.settings.map.type != "random")
|
||||
{
|
||||
this.nbPlayers = this.settings.map.data.settings.PlayerData.length;
|
||||
return;
|
||||
}
|
||||
if (!data || !data.settings || data.settings.PlayerData === undefined)
|
||||
return;
|
||||
this.nbPlayers = data.settings.PlayerData.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param index - Player Index, 0 is 'player 1' since GAIA isn't there.
|
||||
*/
|
||||
get(index)
|
||||
{
|
||||
return this.data[index];
|
||||
}
|
||||
|
||||
setNb(nb)
|
||||
{
|
||||
this.nbPlayers = Math.max(1, Math.min(g_MaxPlayers, nb));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Stores in-game names for all players.
|
||||
*
|
||||
* NB: the regular gamesetup has a particular handling of this setting.
|
||||
* The names are loaded from the map, but the GUI also show playernames
|
||||
* and forces them when starting the game.
|
||||
* This is therefore just handling map-defined names & AI random bot names.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.PlayerName = class PlayerName extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
// NB: watchers aren't auto-triggered when modifying array elements.
|
||||
this.values = [];
|
||||
this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.values.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.values)
|
||||
if (this.values[i])
|
||||
attribs.settings.PlayerData[i].Name = this.values[i];
|
||||
}
|
||||
|
||||
_resize(nb)
|
||||
{
|
||||
while (this.values.length > nb)
|
||||
this.values.pop();
|
||||
while (this.values.length < nb)
|
||||
this.values.push(undefined);
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
// Reset.
|
||||
this._resize(0);
|
||||
this.maybeUpdate();
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this._resize(this.settings.playerCount.nbPlayers);
|
||||
this.values.forEach((_, i) => this._set(i));
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick bot names.
|
||||
*/
|
||||
pickRandomItems()
|
||||
{
|
||||
let picked = false;
|
||||
for (let i in this.values)
|
||||
{
|
||||
if (!!this.values[i] &&
|
||||
this.values[i] !== g_Settings.PlayerDefaults[+i + 1].Name)
|
||||
continue;
|
||||
|
||||
let ai = this.settings.playerAI.values[i];
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
let civ = this.settings.playerCiv.values[i];
|
||||
if (!civ || civ == "random")
|
||||
continue;
|
||||
|
||||
picked = true;
|
||||
// Pick one of the available botnames for the chosen civ
|
||||
// Determine botnames
|
||||
let chosenName = pickRandom(this.settings.civData[civ].AINames);
|
||||
|
||||
// Count how many players use the chosenName
|
||||
let usedName = this.values.filter(oName => oName && oName.indexOf(chosenName) !== -1).length;
|
||||
|
||||
this.values[i] =
|
||||
usedName ?
|
||||
sprintf(this.RomanLabel, {
|
||||
"playerName": chosenName,
|
||||
"romanNumber": this.RomanNumbers[usedName + 1]
|
||||
}) :
|
||||
chosenName;
|
||||
}
|
||||
if (picked)
|
||||
this.trigger("values");
|
||||
return picked;
|
||||
}
|
||||
|
||||
_getMapData(i)
|
||||
{
|
||||
let data = this.settings.map.data;
|
||||
if (!data || !data.settings || !data.settings.PlayerData)
|
||||
return undefined;
|
||||
if (data.settings.PlayerData.length <= i)
|
||||
return undefined;
|
||||
return data.settings.PlayerData[i].Name;
|
||||
}
|
||||
|
||||
_set(playerIndex)
|
||||
{
|
||||
this.values[playerIndex] = this._getMapData(playerIndex) || g_Settings && g_Settings.PlayerDefaults[playerIndex + 1].Name || "";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GameSettings.prototype.Attributes.PlayerName.prototype.RomanLabel =
|
||||
translate("%(playerName)s %(romanNumber)s");
|
||||
|
||||
GameSettings.prototype.Attributes.PlayerName.prototype.RomanNumbers =
|
||||
[undefined, "I", "II", "III", "IV", "V", "VI", "VII", "VIII"];
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Stores team settings for all players.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.PlayerTeam = class PlayerTeam extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
// NB: watchers aren't auto-triggered when modifying array elements.
|
||||
this.values = [];
|
||||
this.locked = [];
|
||||
this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.values.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.values)
|
||||
if (this.values[i] !== undefined)
|
||||
attribs.settings.PlayerData[i].Team = this.values[i];
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!this.getLegacySetting(attribs, "PlayerData"))
|
||||
return;
|
||||
let pData = this.getLegacySetting(attribs, "PlayerData");
|
||||
if (this.values.length < pData.length)
|
||||
this._resize(pData.length);
|
||||
for (let i in pData)
|
||||
if (pData[i] && pData[i].Team !== undefined)
|
||||
this.setValue(i, pData[i].Team);
|
||||
}
|
||||
|
||||
_resize(nb)
|
||||
{
|
||||
while (this.values.length > nb)
|
||||
{
|
||||
this.values.pop();
|
||||
this.locked.pop();
|
||||
}
|
||||
while (this.values.length < nb)
|
||||
{
|
||||
// -1 is None
|
||||
this.values.push(-1);
|
||||
this.locked.push(false);
|
||||
}
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type === "random")
|
||||
return;
|
||||
let pData = this.getMapSetting("PlayerData");
|
||||
if (pData && pData.every(x => x.Team === undefined))
|
||||
return;
|
||||
for (let p in pData)
|
||||
this._set(+p, pData[p].Team === undefined ? -1 : pData[p].Team);
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this._resize(this.settings.playerCount.nbPlayers);
|
||||
this.values.forEach((c, i) => this._set(i, c));
|
||||
this.trigger("values");
|
||||
}
|
||||
|
||||
_set(playerIndex, value)
|
||||
{
|
||||
this.values[playerIndex] = value;
|
||||
this.locked[playerIndex] = this.settings.map.type == "scenario";
|
||||
}
|
||||
|
||||
setValue(playerIndex, val)
|
||||
{
|
||||
this._set(playerIndex, val);
|
||||
this.trigger("values");
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Combines the worldPopulation and regular population cap.
|
||||
* At the moment those are incompatible so this makes sense.
|
||||
* TODO: Should there be a dialog allowing per-player pop limits?
|
||||
*/
|
||||
GameSettings.prototype.Attributes.Population = class Population extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.popDefault = this.getDefaultValue("PopulationCapacities", "Population") || 200;
|
||||
this.worldPopDefault = this.getDefaultValue("WorldPopulationCapacities", "Population") || 800;
|
||||
|
||||
this.perPlayer = false;
|
||||
this.useWorldPop = false;
|
||||
this.cap = this.popDefault;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.perPlayer)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.perPlayer.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.perPlayer)
|
||||
if (this.perPlayer[i])
|
||||
attribs.settings.PlayerData[i].PopulationLimit = this.perPlayer[i];
|
||||
}
|
||||
if (this.useWorldPop)
|
||||
{
|
||||
attribs.settings.WorldPopulation = true;
|
||||
attribs.settings.WorldPopulationCap = this.cap;
|
||||
}
|
||||
else
|
||||
attribs.settings.PopulationCap = this.cap;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!!this.getLegacySetting(attribs, "WorldPopulation"))
|
||||
this.setPopCap(true, this.getLegacySetting(attribs, "WorldPopulationCap"));
|
||||
else if (!!this.getLegacySetting(attribs, "PopulationCap"))
|
||||
this.setPopCap(false, this.getLegacySetting(attribs, "PopulationCap"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
this.perPlayer = undefined;
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
if (this.getMapSetting("PlayerData")?.some(data => data.PopulationLimit))
|
||||
this.perPlayer = this.getMapSetting("PlayerData").map(data => data.PopulationLimit || undefined);
|
||||
else if (this.getMapSetting("WorldPopulation"))
|
||||
this.setPopCap(true, +this.getMapSetting("WorldPopulationCap"));
|
||||
else
|
||||
this.setPopCap(false, +this.getMapSetting("PopulationCap"));
|
||||
}
|
||||
|
||||
setPopCap(worldPop, cap = undefined)
|
||||
{
|
||||
if (worldPop != this.useWorldPop)
|
||||
this.cap = undefined;
|
||||
|
||||
this.useWorldPop = worldPop;
|
||||
|
||||
if (!!cap)
|
||||
this.cap = cap;
|
||||
else if (!this.cap && !this.useWorldPop)
|
||||
this.cap = this.popDefault;
|
||||
else if (!this.cap && this.useWorldPop)
|
||||
this.cap = this.worldPopDefault;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
GameSettings.prototype.Attributes.Rating = class Rating extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.hasXmppClient = Engine.HasXmppClient();
|
||||
this.settings.playerCount.watch(() => this.maybeUpdate(), ["nbPlayers"]);
|
||||
this.maybeUpdate();
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.available)
|
||||
attribs.settings.RatingEnabled = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (this.getLegacySetting(attribs, "RatingEnabled"))
|
||||
{
|
||||
this.available = this.hasXmppClient && this.settings.playerCount.nbPlayers === 2;
|
||||
this.enabled = this.available && !!this.getLegacySetting(attribs, "RatingEnabled");
|
||||
}
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.enabled = this.available && enabled;
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
// This setting is activated by default if it's possible.
|
||||
this.available = this.hasXmppClient && this.settings.playerCount.nbPlayers === 2;
|
||||
this.enabled = this.available;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
GameSettings.prototype.Attributes.RegicideGarrison = class RegicideGarrison extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.setEnabled(false);
|
||||
this.settings.victoryConditions.watch(() => this.maybeUpdate(), ["active"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.RegicideGarrison = this.enabled;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
this.enabled = !!this.getLegacySetting(attribs, "RegicideGarrison");
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setEnabled(!!this.getMapSetting("RegicideGarrison"));
|
||||
}
|
||||
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.available = this.settings.victoryConditions.active.has("regicide");
|
||||
this.enabled = (enabled && this.available);
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this.setEnabled(this.enabled);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
GameSettings.prototype.Attributes.Relic = class Relic extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.available = false;
|
||||
this.count = 0;
|
||||
this.duration = 0;
|
||||
this.settings.victoryConditions.watch(() => this.maybeUpdate(), ["active"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
// For consistency, only save this if the victory condition is active.
|
||||
if (this.available)
|
||||
{
|
||||
attribs.settings.RelicCount = this.count;
|
||||
attribs.settings.RelicDuration = this.duration;
|
||||
}
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!!this.getLegacySetting(attribs, "RelicCount"))
|
||||
this.setCount(this.getLegacySetting(attribs, "RelicCount"));
|
||||
if (!!this.getLegacySetting(attribs, "RelicDuration"))
|
||||
this.setDuration(this.getLegacySetting(attribs, "RelicDuration"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
// TODO: probably should sync the victory condition.
|
||||
if (!this.getMapSetting("RelicCount"))
|
||||
this.available = false;
|
||||
else
|
||||
this._set(+this.getMapSetting("RelicCount"), +this.getMapSetting("RelicDuration"));
|
||||
}
|
||||
|
||||
_set(count, duration)
|
||||
{
|
||||
this.available = this.settings.victoryConditions.active.has("capture_the_relic");
|
||||
this.count = Math.max(1, count);
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
setCount(val)
|
||||
{
|
||||
this._set(Math.round(val), this.duration);
|
||||
}
|
||||
|
||||
setDuration(val)
|
||||
{
|
||||
this._set(this.count, Math.round(val));
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this._set(this.count, this.duration);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
GameSettings.prototype.Attributes.SeaLevelRise = class SeaLevelRise extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.min = undefined;
|
||||
this.max = undefined;
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.SeaLevelRiseTime = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (!!this.getLegacySetting(attribs, "SeaLevelRiseTime"))
|
||||
this.setValue(this.getLegacySetting(attribs, "SeaLevelRiseTime"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("SeaLevelRise"))
|
||||
{
|
||||
this.value = undefined;
|
||||
return;
|
||||
}
|
||||
let mapData = this.settings.map.data;
|
||||
this.min = mapData.settings.SeaLevelRise.Min;
|
||||
this.max = mapData.settings.SeaLevelRise.Max;
|
||||
this.value = mapData.settings.SeaLevelRise.Default;
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
if (!this.getMapSetting("SeaLevelRise"))
|
||||
this.value = undefined;
|
||||
else
|
||||
this.value = Math.max(this.min, Math.min(this.max, Math.round(val)));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
GameSettings.prototype.Attributes.Seeds = class Seeds extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.seed = 0;
|
||||
this.AIseed = 0;
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
// Seed is used for map generation and simulation.
|
||||
attribs.settings.Seed = this.seed;
|
||||
attribs.settings.AISeed = this.AIseed;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
// Seed is used for map generation and simulation.
|
||||
if (this.getLegacySetting(attribs, "Seed") !== undefined)
|
||||
this.seed = this.getLegacySetting(attribs, "Seed");
|
||||
if (this.getLegacySetting(attribs, "AISeed") !== undefined)
|
||||
this.AIseed = this.getLegacySetting(attribs, "AISeed");
|
||||
}
|
||||
|
||||
pickRandomItems()
|
||||
{
|
||||
this.seed = randIntExclusive(0, Math.pow(2, 32));
|
||||
this.AIseed = randIntExclusive(0, Math.pow(2, 32));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* For compatibility reasons, this loads the per-player StartingCamera from the map data
|
||||
* In general, this is probably better handled by map triggers or the default camera placement.
|
||||
* This doesn't have a GUI setting.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.StartingCamera = class StartingCamera extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.values = [];
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.values.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.values)
|
||||
if (this.values[i])
|
||||
attribs.settings.PlayerData[i].StartingCamera = this.values[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptionally, this setting has no Deserialize: it's entirely determined by the map
|
||||
*/
|
||||
|
||||
_resize(nb)
|
||||
{
|
||||
while (this.values.length > nb)
|
||||
this.values.pop();
|
||||
while (this.values.length < nb)
|
||||
this.values.push(undefined);
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
let pData = this.getMapSetting("PlayerData");
|
||||
this._resize(pData?.length || 0);
|
||||
for (let i in pData)
|
||||
this.values[i] = pData?.[i]?.StartingCamera;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* TODO: There should be a dialog allowing to specify starting resources per player
|
||||
*/
|
||||
GameSettings.prototype.Attributes.StartingResources = class StartingResources extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.defaultValue = this.getDefaultValue("StartingResources", "Resources") || 300;
|
||||
this.perPlayer = undefined;
|
||||
this.setResources(this.defaultValue);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.perPlayer)
|
||||
{
|
||||
if (!attribs.settings.PlayerData)
|
||||
attribs.settings.PlayerData = [];
|
||||
while (attribs.settings.PlayerData.length < this.perPlayer.length)
|
||||
attribs.settings.PlayerData.push({});
|
||||
for (let i in this.perPlayer)
|
||||
if (this.perPlayer[i])
|
||||
attribs.settings.PlayerData[i].Resources = this.perPlayer[i];
|
||||
}
|
||||
attribs.settings.StartingResources = this.resources;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (this.getLegacySetting(attribs, "StartingResources") !== undefined)
|
||||
this.setResources(this.getLegacySetting(attribs, "StartingResources"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
this.perPlayer = undefined;
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
if (!!this.getMapSetting("PlayerData") &&
|
||||
this.getMapSetting("PlayerData").some(data => data.Resources))
|
||||
this.perPlayer = this.getMapSetting("PlayerData").map(data => data.Resources || undefined);
|
||||
else if (!this.getMapSetting("StartingResources"))
|
||||
this.setResources(this.defaultValue);
|
||||
else
|
||||
this.setResources(this.getMapSetting("StartingResources"));
|
||||
}
|
||||
|
||||
setResources(res)
|
||||
{
|
||||
this.resources = res;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* TODO: this would probably be better handled by map triggers.
|
||||
* This doesn't have a GUI setting.
|
||||
*/
|
||||
GameSettings.prototype.Attributes.StartingTechnologies = class StartingTechnologies extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.value)
|
||||
attribs.settings.StartingTechnologies = this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptionally, this setting has no Deserialize: it's entirely determined by the map
|
||||
*/
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("StartingTechnologies"))
|
||||
this.setValue(undefined);
|
||||
else
|
||||
this.setValue(this.getMapSetting("StartingTechnologies"));
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
this.value = val;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
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)
|
||||
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 (this.value !== "random")
|
||||
return false;
|
||||
this.value = pickRandom(this.available).Id;
|
||||
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."),
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,52 @@
|
||||
GameSettings.prototype.Attributes.TriggerDifficulty = class TriggerDifficulty extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.difficulties = loadSettingValuesFile("trigger_difficulties.json");
|
||||
this.available = undefined;
|
||||
this.value = undefined;
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.available)
|
||||
attribs.settings.TriggerDifficulty = this.value;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (this.getLegacySetting(attribs, "TriggerDifficulty") !== undefined)
|
||||
this.setValue(this.getLegacySetting(attribs, "TriggerDifficulty"));
|
||||
}
|
||||
|
||||
getAvailableSettings()
|
||||
{
|
||||
return this.difficulties.filter(x => this.available.indexOf(x.Name) !== -1);
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (!this.getMapSetting("SupportedTriggerDifficulties"))
|
||||
{
|
||||
this.value = undefined;
|
||||
this.available = undefined;
|
||||
return;
|
||||
}
|
||||
// TODO: should probably validate that they fit one of the known schemes.
|
||||
this.available = this.getMapSetting("SupportedTriggerDifficulties").Values;
|
||||
this.value = this.difficulties.find(x => x.Default && this.available.indexOf(x.Name) !== -1).Difficulty;
|
||||
}
|
||||
|
||||
setValue(val)
|
||||
{
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
getData()
|
||||
{
|
||||
if (!this.value)
|
||||
return undefined;
|
||||
return this.difficulties[this.value - 1];
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
GameSettings.prototype.Attributes.TriggerScripts = class TriggerScripts extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.victory = new Set();
|
||||
this.map = new Set();
|
||||
this.settings.map.watch(() => this.updateMapScripts(), ["map"]);
|
||||
this.settings.victoryConditions.watch(() => this.updateVictoryScripts(), ["active"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
let scripts = new Set(this.victory);
|
||||
for (let elem of this.map)
|
||||
scripts.add(elem);
|
||||
attribs.settings.TriggerScripts = Array.from(scripts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptionally, this setting has no Deserialize: it's entirely determined from other settings.
|
||||
*/
|
||||
|
||||
updateVictoryScripts()
|
||||
{
|
||||
let setting = this.settings.victoryConditions;
|
||||
let scripts = new Set();
|
||||
for (let cond of setting.active)
|
||||
setting.conditions[cond].Scripts.forEach(script => scripts.add(script));
|
||||
this.victory = scripts;
|
||||
}
|
||||
|
||||
updateMapScripts()
|
||||
{
|
||||
if (!this.settings.map.data || !this.settings.map.data.settings ||
|
||||
!this.settings.map.data.settings.TriggerScripts)
|
||||
{
|
||||
this.map = new Set();
|
||||
return;
|
||||
}
|
||||
this.map = new Set(this.settings.map.data.settings.TriggerScripts);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
GameSettings.prototype.Attributes.VictoryConditions = class VictoryConditions extends GameSetting
|
||||
{
|
||||
constructor(settings)
|
||||
{
|
||||
super(settings);
|
||||
// Set of victory condition names.
|
||||
this.active = new Set();
|
||||
this.disabled = new Set();
|
||||
this.conditions = {};
|
||||
}
|
||||
|
||||
init()
|
||||
{
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
|
||||
let conditions = loadVictoryConditions();
|
||||
for (let cond of conditions)
|
||||
this.conditions[cond.Name] = cond;
|
||||
|
||||
for (let cond in this.conditions)
|
||||
if (this.conditions[cond].Default)
|
||||
this._add(this.conditions[cond].Name);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
attribs.settings.VictoryConditions = Array.from(this.active);
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
let legacy = this.getLegacySetting(attribs, "VictoryConditions");
|
||||
if (legacy)
|
||||
{
|
||||
this.disabled = new Set();
|
||||
this.active = new Set();
|
||||
for (let cond of legacy)
|
||||
this._add(cond);
|
||||
}
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
// If a map specifies victory conditions, replace them all.
|
||||
if (!this.getMapSetting("VictoryConditions"))
|
||||
return;
|
||||
this.disabled = new Set();
|
||||
this.active = new Set();
|
||||
// TODO: could be optimised.
|
||||
for (let cond of this.getMapSetting("VictoryConditions"))
|
||||
this._add(cond);
|
||||
}
|
||||
|
||||
_reconstructDisabled(active)
|
||||
{
|
||||
let disabled = new Set();
|
||||
for (let cond of active)
|
||||
if (this.conditions[cond].DisabledWhenChecked)
|
||||
this.conditions[cond].DisabledWhenChecked.forEach(x => disabled.add(x));
|
||||
|
||||
return disabled;
|
||||
}
|
||||
|
||||
_add(name)
|
||||
{
|
||||
if (this.disabled.has(name))
|
||||
return;
|
||||
let active = clone(this.active);
|
||||
active.add(name);
|
||||
// Assume we want to remove incompatible ones.
|
||||
if (this.conditions[name].DisabledWhenChecked)
|
||||
this.conditions[name].DisabledWhenChecked.forEach(x => active.delete(x));
|
||||
// TODO: sanity check
|
||||
this.disabled = this._reconstructDisabled(active);
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
_delete(name)
|
||||
{
|
||||
let active = clone(this.active);
|
||||
active.delete(name);
|
||||
// TODO: sanity check
|
||||
this.disabled = this._reconstructDisabled(active);
|
||||
this.active = active;
|
||||
}
|
||||
|
||||
setEnabled(name, enabled)
|
||||
{
|
||||
if (enabled)
|
||||
this._add(name);
|
||||
else
|
||||
this._delete(name);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
GameSettings.prototype.Attributes.Wonder = class Wonder extends GameSetting
|
||||
{
|
||||
init()
|
||||
{
|
||||
this.available = false;
|
||||
this.duration = 0;
|
||||
this.settings.victoryConditions.watch(() => this.maybeUpdate(), ["active"]);
|
||||
this.settings.map.watch(() => this.onMapChange(), ["map"]);
|
||||
}
|
||||
|
||||
toInitAttributes(attribs)
|
||||
{
|
||||
if (this.available)
|
||||
attribs.settings.WonderDuration = this.duration;
|
||||
}
|
||||
|
||||
fromInitAttributes(attribs)
|
||||
{
|
||||
if (this.getLegacySetting(attribs, "WonderDuration") !== undefined)
|
||||
this.setDuration(+this.getLegacySetting(attribs, "WonderDuration"));
|
||||
}
|
||||
|
||||
onMapChange()
|
||||
{
|
||||
if (this.settings.map.type != "scenario")
|
||||
return;
|
||||
this.setDuration(+this.getMapSetting("WonderDuration") || 0);
|
||||
}
|
||||
|
||||
setDuration(duration)
|
||||
{
|
||||
this.available = this.settings.victoryConditions.active.has("wonder");
|
||||
this.duration = Math.round(duration);
|
||||
}
|
||||
|
||||
maybeUpdate()
|
||||
{
|
||||
this.setDuration(this.duration);
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* This class provides a property independent interface to g_GameAttributes events.
|
||||
* Classes may use this interface in order to react to changing g_GameAttributes.
|
||||
* 'Controller' for the GUI handling of gamesettings.
|
||||
*/
|
||||
class GameSettingsControl
|
||||
{
|
||||
@@ -10,18 +9,10 @@ class GameSettingsControl
|
||||
this.mapCache = mapCache;
|
||||
this.gameSettingsFile = new GameSettingsFile(this);
|
||||
|
||||
this.previousMap = undefined;
|
||||
this.depth = 0;
|
||||
this.guiData = new GameSettingsGuiData();
|
||||
|
||||
// This property may be read from publicly
|
||||
this.autostart = false;
|
||||
|
||||
this.gameAttributesChangeHandlers = new Set();
|
||||
this.gameAttributesBatchChangeHandlers = new Set();
|
||||
this.gameAttributesFinalizeHandlers = new Set();
|
||||
this.pickRandomItemsHandlers = new Set();
|
||||
this.assignPlayerHandlers = new Set();
|
||||
this.mapChangeHandlers = new Set();
|
||||
this.updateLayoutHandlers = new Set();
|
||||
this.settingsChangeHandlers = new Set();
|
||||
|
||||
setupWindow.registerLoadHandler(this.onLoad.bind(this));
|
||||
setupWindow.registerGetHotloadDataHandler(this.onGetHotloadData.bind(this));
|
||||
@@ -34,186 +25,92 @@ class GameSettingsControl
|
||||
netMessages.registerNetMessageHandler("gamesetup", this.onGamesetupMessage.bind(this));
|
||||
}
|
||||
|
||||
registerMapChangeHandler(handler)
|
||||
registerUpdateLayoutHandler(handler)
|
||||
{
|
||||
this.mapChangeHandlers.add(handler);
|
||||
}
|
||||
|
||||
unregisterMapChangeHandler(handler)
|
||||
{
|
||||
this.mapChangeHandlers.delete(handler);
|
||||
this.updateLayoutHandlers.add(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* This message is triggered everytime g_GameAttributes change.
|
||||
* Handlers may subsequently change g_GameAttributes and trigger this message again.
|
||||
* @param handler will be called when any setting change.
|
||||
* (this isn't exactly what happens but the behaviour should be similar).
|
||||
*/
|
||||
registerGameAttributesChangeHandler(handler)
|
||||
registerSettingsChangeHandler(handler)
|
||||
{
|
||||
this.gameAttributesChangeHandlers.add(handler);
|
||||
}
|
||||
|
||||
unregisterGameAttributesChangeHandler(handler)
|
||||
{
|
||||
this.gameAttributesChangeHandlers.delete(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* This message is triggered after g_GameAttributes changed and recursed gameAttributesChangeHandlers finished.
|
||||
* The use case for this is to update GUI objects which do not change g_GameAttributes but only display the attributes.
|
||||
*/
|
||||
registerGameAttributesBatchChangeHandler(handler)
|
||||
{
|
||||
this.gameAttributesBatchChangeHandlers.add(handler);
|
||||
}
|
||||
|
||||
unregisterGameAttributesBatchChangeHandler(handler)
|
||||
{
|
||||
this.gameAttributesBatchChangeHandlers.delete(handler);
|
||||
}
|
||||
|
||||
registerGameAttributesFinalizeHandler(handler)
|
||||
{
|
||||
this.gameAttributesFinalizeHandlers.add(handler);
|
||||
}
|
||||
|
||||
unregisterGameAttributesFinalizeHandler(handler)
|
||||
{
|
||||
this.gameAttributesFinalizeHandlers.delete(handler);
|
||||
}
|
||||
|
||||
registerAssignPlayerHandler(handler)
|
||||
{
|
||||
this.assignPlayerHandlers.add(handler);
|
||||
}
|
||||
|
||||
unregisterAssignPlayerHandler(handler)
|
||||
{
|
||||
this.assignPlayerHandlers.delete(handler);
|
||||
}
|
||||
|
||||
registerPickRandomItemsHandler(handler)
|
||||
{
|
||||
this.pickRandomItemsHandlers.add(handler);
|
||||
}
|
||||
|
||||
unregisterPickRandomItemsHandler(handler)
|
||||
{
|
||||
this.pickRandomItemsHandlers.delete(handler);
|
||||
this.settingsChangeHandlers.add(handler);
|
||||
}
|
||||
|
||||
onLoad(initData, hotloadData)
|
||||
{
|
||||
if (initData && initData.map && initData.mapType)
|
||||
if (hotloadData)
|
||||
this.parseSettings(hotloadData.gameAttributes);
|
||||
else if (g_IsController && this.gameSettingsFile.enabled)
|
||||
{
|
||||
if (initData.autostart)
|
||||
Object.defineProperty(this, "autostart", {
|
||||
"value": true,
|
||||
"writable": false,
|
||||
"configurable": false
|
||||
});
|
||||
|
||||
// TODO: Fix g_GameAttributes, g_GameAttributes.settings,
|
||||
// g_GameAttributes.settings.PlayerData object references and
|
||||
// copy over each attribute individually when receiving
|
||||
// settings from the server or the local file.
|
||||
g_GameAttributes = initData;
|
||||
|
||||
this.updateGameAttributes();
|
||||
// Don't launchGame before all Load handlers finished
|
||||
let settings = this.gameSettingsFile.loadFile();
|
||||
if (settings)
|
||||
this.parseSettings(settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hotloadData)
|
||||
g_GameAttributes = hotloadData.gameAttributes;
|
||||
else if (g_IsController && this.gameSettingsFile.enabled)
|
||||
g_GameAttributes = this.gameSettingsFile.loadFile();
|
||||
|
||||
this.updateGameAttributes();
|
||||
this.setNetworkGameAttributes();
|
||||
}
|
||||
this.updateLayout();
|
||||
this.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onClose()
|
||||
{
|
||||
if (!this.autostart)
|
||||
this.gameSettingsFile.saveFile();
|
||||
this.gameSettingsFile.saveFile();
|
||||
}
|
||||
|
||||
onGetHotloadData(object)
|
||||
{
|
||||
object.gameAttributes = g_GameAttributes;
|
||||
object.gameAttributes = this.getSettings();
|
||||
}
|
||||
|
||||
onGamesetupMessage(message)
|
||||
{
|
||||
if (!message.data)
|
||||
if (!message.data || g_IsController)
|
||||
return;
|
||||
|
||||
g_GameAttributes = message.data;
|
||||
this.updateGameAttributes();
|
||||
this.parseSettings(message.data);
|
||||
|
||||
// This assumes that messages aren't sent spuriously without changes
|
||||
// (which is generally fair), but technically it would be good
|
||||
// to check if the new data is different from the previous data.
|
||||
for (let handler of this.settingsChangeHandlers)
|
||||
handler();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is to be called whenever g_GameAttributes has been changed except on gameAttributes finalization.
|
||||
* Returns the InitAttributes, augmented by GUI-specific data.
|
||||
*/
|
||||
updateGameAttributes()
|
||||
getSettings()
|
||||
{
|
||||
if (this.depth == 0)
|
||||
Engine.ProfileStart("updateGameAttributes");
|
||||
let ret = g_GameSettings.toInitAttributes();
|
||||
ret.guiData = this.guiData.Serialize();
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (this.depth >= this.MaxDepth)
|
||||
{
|
||||
error("Infinite loop: " + new Error().stack);
|
||||
Engine.ProfileStop();
|
||||
/**
|
||||
* Parse the following settings.
|
||||
*/
|
||||
parseSettings(settings)
|
||||
{
|
||||
if (settings.guiData)
|
||||
this.guiData.Deserialize(settings.guiData);
|
||||
g_GameSettings.fromInitAttributes(settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called whenever the GUI layout needs to be updated.
|
||||
* Triggers on the next GUI tick to avoid un-necessary layout.
|
||||
*/
|
||||
updateLayout()
|
||||
{
|
||||
if (this.layoutTimer)
|
||||
return;
|
||||
}
|
||||
|
||||
++this.depth;
|
||||
|
||||
// Basic sanitization
|
||||
{
|
||||
if (!g_GameAttributes.settings)
|
||||
g_GameAttributes.settings = {};
|
||||
|
||||
if (!g_GameAttributes.settings.PlayerData)
|
||||
g_GameAttributes.settings.PlayerData = new Array(this.DefaultPlayerCount);
|
||||
|
||||
for (let i = 0; i < g_GameAttributes.settings.PlayerData.length; ++i)
|
||||
if (!g_GameAttributes.settings.PlayerData[i])
|
||||
g_GameAttributes.settings.PlayerData[i] = {};
|
||||
}
|
||||
|
||||
// Map change handlers are triggered first, so that GameSettingControls can update their
|
||||
// gameAttributes model prior to applying that model in their gameAttributesChangeHandler.
|
||||
if (g_GameAttributes.map && this.previousMap != g_GameAttributes.map && g_GameAttributes.mapType)
|
||||
{
|
||||
this.previousMap = g_GameAttributes.map;
|
||||
// Use a try..catch to avoid completely failing in case of an error
|
||||
// as this prevents even going back to the main menu.
|
||||
try
|
||||
{
|
||||
let mapData = this.mapCache.getMapData(g_GameAttributes.mapType, g_GameAttributes.map);
|
||||
for (let handler of this.mapChangeHandlers)
|
||||
handler(mapData);
|
||||
} catch(err) {
|
||||
// Report the error regardless so that the underlying bug gets fixed.
|
||||
error(err);
|
||||
error(err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
for (let handler of this.gameAttributesChangeHandlers)
|
||||
handler();
|
||||
|
||||
--this.depth;
|
||||
|
||||
if (this.depth == 0)
|
||||
{
|
||||
for (let handler of this.gameAttributesBatchChangeHandlers)
|
||||
this.layoutTimer = setTimeout(() => {
|
||||
for (let handler of this.updateLayoutHandlers)
|
||||
handler();
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
delete this.layoutTimer;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,73 +124,32 @@ class GameSettingsControl
|
||||
*/
|
||||
setNetworkGameAttributes()
|
||||
{
|
||||
for (let handler of this.settingsChangeHandlers)
|
||||
handler();
|
||||
|
||||
if (g_IsNetworked && this.timer === undefined)
|
||||
this.timer = setTimeout(this.setNetworkGameAttributesImmediately.bind(this), this.Timeout);
|
||||
}
|
||||
|
||||
setNetworkGameAttributesImmediately()
|
||||
{
|
||||
delete this.timer;
|
||||
if (g_IsNetworked)
|
||||
Engine.SetNetworkGameAttributes(g_GameAttributes);
|
||||
}
|
||||
|
||||
getPlayerData(gameAttributes, playerIndex)
|
||||
{
|
||||
return gameAttributes &&
|
||||
gameAttributes.settings &&
|
||||
gameAttributes.settings.PlayerData &&
|
||||
gameAttributes.settings.PlayerData[playerIndex] || undefined;
|
||||
}
|
||||
|
||||
assignPlayer(sourcePlayerIndex, playerIndex)
|
||||
{
|
||||
if (playerIndex == -1)
|
||||
return;
|
||||
|
||||
let target = this.getPlayerData(g_GameAttributes, playerIndex);
|
||||
let source = this.getPlayerData(g_GameAttributes, sourcePlayerIndex);
|
||||
|
||||
for (let handler of this.assignPlayerHandlers)
|
||||
handler(source, target);
|
||||
|
||||
this.updateGameAttributes();
|
||||
this.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called everytime a random setting selection was resolved,
|
||||
* so that subsequent random settings are triggered too,
|
||||
* for example picking a random biome after picking a random map.
|
||||
*/
|
||||
pickRandomItems()
|
||||
{
|
||||
for (let handler of this.pickRandomItemsHandlers)
|
||||
handler();
|
||||
if (this.timer)
|
||||
{
|
||||
clearTimeout(this.timer);
|
||||
delete this.timer;
|
||||
}
|
||||
g_GameSettings.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onLaunchGame()
|
||||
{
|
||||
if (!this.autostart)
|
||||
this.gameSettingsFile.saveFile();
|
||||
|
||||
this.pickRandomItems();
|
||||
|
||||
for (let handler of this.gameAttributesFinalizeHandlers)
|
||||
handler();
|
||||
|
||||
this.setNetworkGameAttributesImmediately();
|
||||
// Save the file before random settings are resolved.
|
||||
this.gameSettingsFile.saveFile();
|
||||
}
|
||||
}
|
||||
|
||||
GameSettingsControl.prototype.MaxDepth = 512;
|
||||
|
||||
/**
|
||||
* Wait (at most) this many milliseconds before sending network messages.
|
||||
*/
|
||||
GameSettingsControl.prototype.Timeout = 400;
|
||||
|
||||
/**
|
||||
* This number is used when selecting the random map type, which doesn't provide PlayerData.
|
||||
*/
|
||||
GameSettingsControl.prototype.DefaultPlayerCount = 4;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* This class provides a way to save g_GameAttributes to a file and load them.
|
||||
* This class provides a way to save game settings to a file and load them.
|
||||
*/
|
||||
class GameSettingsFile
|
||||
{
|
||||
@@ -9,6 +9,8 @@ class GameSettingsFile
|
||||
this.GameAttributesFileMultiplayer :
|
||||
this.GameAttributesFileSingleplayer;
|
||||
|
||||
this.gameSettingsControl = GameSettingsControl;
|
||||
|
||||
this.engineInfo = Engine.GetEngineInfo();
|
||||
this.enabled = Engine.ConfigDB_GetValue("user", this.ConfigName) == "true";
|
||||
}
|
||||
@@ -45,7 +47,7 @@ class GameSettingsFile
|
||||
|
||||
Engine.ProfileStart("savePersistMatchSettingsFile");
|
||||
Engine.WriteJSONFile(this.filename, {
|
||||
"attributes": this.enabled ? g_GameAttributes : {},
|
||||
"attributes": this.enabled ? this.gameSettingsControl.getSettings() : {},
|
||||
"engine_info": this.engineInfo
|
||||
});
|
||||
Engine.ProfileStop();
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* This class contains GUI-specific gamesetting data.
|
||||
*/
|
||||
class GameSettingsGuiData
|
||||
{
|
||||
constructor()
|
||||
{
|
||||
this.mapFilter = new Observable();
|
||||
this.mapFilter.filter = "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize for network transmission, settings persistence or convenience in other GUI files.
|
||||
*/
|
||||
Serialize()
|
||||
{
|
||||
let ret = {
|
||||
"mapFilter": this.mapFilter.filter,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
Deserialize(data)
|
||||
{
|
||||
this.mapFilter.filter = data.mapFilter;
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ class PlayerAssignmentsControl
|
||||
};
|
||||
}
|
||||
|
||||
g_GameSettings.playerCount.watch(() => this.unassignInvalidPlayers(), ["nbPlayers"]);
|
||||
|
||||
setupWindow.registerLoadHandler(this.onLoad.bind(this));
|
||||
setupWindow.registerGetHotloadDataHandler(this.onGetHotloadData.bind(this));
|
||||
netMessages.registerNetMessageHandler("players", this.onPlayerAssignmentMessage.bind(this));
|
||||
@@ -104,19 +106,18 @@ class PlayerAssignmentsControl
|
||||
g_PlayerAssignments = newAssignments;
|
||||
this.updatePlayerAssignments();
|
||||
// Send at most one gameRegisterStanza after all handlers run in case a
|
||||
// joining observer has been assigned to a playerslot.
|
||||
// joining observer has been assigned to a playerslot.
|
||||
this.gameRegisterStanza.sendImmediately?.();
|
||||
}
|
||||
|
||||
assignClient(guid, playerIndex)
|
||||
{
|
||||
g_GameSettings.playerAI.setAI(playerIndex - 1, undefined);
|
||||
if (g_IsNetworked)
|
||||
Engine.AssignNetworkPlayer(playerIndex, guid);
|
||||
else
|
||||
{
|
||||
if (g_PlayerAssignments[guid])
|
||||
g_PlayerAssignments[guid].player = playerIndex;
|
||||
this.updatePlayerAssignments();
|
||||
}
|
||||
this.updatePlayerAssignments();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,17 +126,15 @@ class PlayerAssignmentsControl
|
||||
assignPlayer(guidToAssign, playerIndex)
|
||||
{
|
||||
if (g_PlayerAssignments[guidToAssign].player != -1)
|
||||
{
|
||||
for (let guid in g_PlayerAssignments)
|
||||
if (g_PlayerAssignments[guid].player == playerIndex + 1)
|
||||
{
|
||||
this.assignClient(guid, g_PlayerAssignments[guidToAssign].player);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
this.assignClient(guidToAssign, playerIndex + 1);
|
||||
|
||||
if (!g_IsNetworked)
|
||||
this.updatePlayerAssignments();
|
||||
}
|
||||
|
||||
unassignClient(playerID)
|
||||
@@ -152,11 +151,10 @@ class PlayerAssignmentsControl
|
||||
unassignInvalidPlayers()
|
||||
{
|
||||
if (g_IsNetworked)
|
||||
for (let playerID = g_GameAttributes.settings.PlayerData.length + 1; playerID <= g_MaxPlayers; ++playerID)
|
||||
for (let playerID = g_GameSettings.playerCount.nbPlayers + 1; playerID <= g_MaxPlayers; ++playerID)
|
||||
// Remove obsolete playerIDs from the servers playerassignments copy
|
||||
Engine.AssignNetworkPlayer(playerID, "");
|
||||
|
||||
else if (g_PlayerAssignments.local.player > g_GameAttributes.settings.PlayerData.length)
|
||||
else if (g_PlayerAssignments.local.player > g_GameSettings.playerCount.nbPlayers)
|
||||
{
|
||||
g_PlayerAssignments.local.player = -1;
|
||||
this.updatePlayerAssignments();
|
||||
|
||||
@@ -25,7 +25,7 @@ class ReadyControl
|
||||
this.readyState = this.NotReady;
|
||||
|
||||
netMessages.registerNetMessageHandler("ready", this.onReadyMessage.bind(this));
|
||||
gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
|
||||
gameSettingsControl.registerSettingsChangeHandler(this.onSettingsChange.bind(this));
|
||||
playerAssignmentsControl.registerClientJoinHandler(this.onClientJoin.bind(this));
|
||||
playerAssignmentsControl.registerClientLeaveHandler(this.onClientLeave.bind(this));
|
||||
}
|
||||
@@ -73,7 +73,7 @@ class ReadyControl
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
onSettingsChange()
|
||||
{
|
||||
this.resetReady();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ class StartGameControl
|
||||
// This may be read from publicly
|
||||
this.gameStarted = false;
|
||||
|
||||
// In MP, the host launches the game and switches right away,
|
||||
// clients switch when they receive the appropriate message.
|
||||
netMessages.registerNetMessageHandler("start", this.switchToLoadingPage.bind(this));
|
||||
}
|
||||
|
||||
@@ -34,19 +36,17 @@ class StartGameControl
|
||||
for (let handler of this.gameLaunchHandlers)
|
||||
handler();
|
||||
|
||||
if (g_IsNetworked)
|
||||
Engine.StartNetworkGame();
|
||||
else
|
||||
{
|
||||
Engine.StartGame(g_GameAttributes, g_PlayerAssignments.local.player);
|
||||
this.switchToLoadingPage();
|
||||
}
|
||||
g_GameSettings.launchGame(g_PlayerAssignments);
|
||||
|
||||
// Switch to the loading page right away,
|
||||
// the GUI will otherwise show the unrandomised settings.
|
||||
this.switchToLoadingPage();
|
||||
}
|
||||
|
||||
switchToLoadingPage()
|
||||
{
|
||||
Engine.SwitchGuiPage("page_loading.xml", {
|
||||
"attribs": g_GameAttributes,
|
||||
"attribs": g_GameSettings.toInitAttributes(),
|
||||
"playerAssignments": g_PlayerAssignments
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,11 +20,15 @@ class GameRegisterStanza
|
||||
|
||||
// Events
|
||||
setupWindow.registerClosePageHandler(this.onClosePage.bind(this));
|
||||
gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
|
||||
netMessages.registerNetMessageHandler("start", this.onGameStart.bind(this));
|
||||
|
||||
g_GameSettings.map.watch(() => this.onSettingsChange(), ["map", "type"]);
|
||||
g_GameSettings.mapSize.watch(() => this.onSettingsChange(), ["size"]);
|
||||
g_GameSettings.victoryConditions.watch(() => this.onSettingsChange(), ["active"]);
|
||||
g_GameSettings.playerCount.watch(() => this.onSettingsChange(), ["nbPlayers"]);
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
onSettingsChange()
|
||||
{
|
||||
if (this.lastStanza)
|
||||
this.sendDelayed();
|
||||
@@ -83,13 +87,13 @@ class GameRegisterStanza
|
||||
let stanza = {
|
||||
"name": this.serverName,
|
||||
"hostUsername": Engine.LobbyGetNick(),
|
||||
"mapName": g_GameAttributes.map,
|
||||
"niceMapName": this.mapCache.getTranslatableMapName(g_GameAttributes.mapType, g_GameAttributes.map),
|
||||
"mapSize": g_GameAttributes.mapType == "random" ? g_GameAttributes.settings.Size : "Default",
|
||||
"mapType": g_GameAttributes.mapType,
|
||||
"victoryConditions": g_GameAttributes.settings.VictoryConditions.join(","),
|
||||
"mapName": g_GameSettings.map.map,
|
||||
"niceMapName": this.mapCache.getTranslatableMapName(g_GameSettings.map.type, g_GameSettings.map.map),
|
||||
"mapSize": g_GameSettings.map.type == "random" ? g_GameSettings.mapSize.size : "Default",
|
||||
"mapType": g_GameSettings.map.type,
|
||||
"victoryConditions": Array.from(g_GameSettings.victoryConditions.active).join(","),
|
||||
"nbp": clients.connectedPlayers,
|
||||
"maxnbp": g_GameAttributes.settings.PlayerData.length,
|
||||
"maxnbp": g_GameSettings.playerCount.nbPlayers,
|
||||
"players": clients.list,
|
||||
"mods": this.mods,
|
||||
"hasPassword": this.hasPassword || ""
|
||||
@@ -107,7 +111,6 @@ class GameRegisterStanza
|
||||
/**
|
||||
* Send a list of playernames and distinct between players and observers.
|
||||
* Don't send teams, AIs or anything else until the game was started.
|
||||
* The playerData format from g_GameAttributes is kept to reuse the GUI function presenting the data.
|
||||
*/
|
||||
formatClientsForStanza()
|
||||
{
|
||||
@@ -118,7 +121,7 @@ class GameRegisterStanza
|
||||
{
|
||||
let pData = { "Name": g_PlayerAssignments[guid].name };
|
||||
|
||||
if (g_GameAttributes.settings.PlayerData[g_PlayerAssignments[guid].player - 1])
|
||||
if (g_PlayerAssignments[guid].player <= g_GameSettings.playerCount.nbPlayers)
|
||||
++connectedPlayers;
|
||||
else
|
||||
pData.Team = "observer";
|
||||
|
||||
@@ -25,8 +25,7 @@ SetupWindowPages.AIConfigPage = class
|
||||
this.aiConfigPage = Engine.GetGUIObjectByName("aiConfigPage");
|
||||
Engine.GetGUIObjectByName("aiConfigOkButton").onPress = this.closePage.bind(this);
|
||||
|
||||
this.gameSettingsControl.registerGameAttributesBatchChangeHandler(
|
||||
this.onGameAttributesBatchChange.bind(this));
|
||||
g_GameSettings.playerAI.watch(() => this.maybeClose(), ["values"]);
|
||||
}
|
||||
|
||||
registerOpenPageHandler(handler)
|
||||
@@ -49,10 +48,9 @@ SetupWindowPages.AIConfigPage = class
|
||||
this.aiConfigPage.hidden = false;
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
maybeClose()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
if (!g_GameSettings.playerAI.get(this.playerIndex))
|
||||
this.closePage();
|
||||
}
|
||||
|
||||
@@ -60,7 +58,7 @@ SetupWindowPages.AIConfigPage = class
|
||||
{
|
||||
this.aiConfigPage.hidden = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SetupWindowPages.AIConfigPage.prototype.AIGameSettingControlOrder = [
|
||||
"AISelection",
|
||||
|
||||
@@ -6,29 +6,22 @@ class AIDescription
|
||||
|
||||
this.aiDescription = Engine.GetGUIObjectByName("aiDescription");
|
||||
|
||||
this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
|
||||
this.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
|
||||
|
||||
aiConfigPage.registerOpenPageHandler(this.onOpenPage.bind(this));
|
||||
|
||||
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
|
||||
}
|
||||
|
||||
onOpenPage(playerIndex)
|
||||
{
|
||||
this.playerIndex = playerIndex;
|
||||
this.updateSelectedValue();
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
render()
|
||||
{
|
||||
this.updateSelectedValue();
|
||||
}
|
||||
|
||||
updateSelectedValue()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
let AI = g_Settings.AIDescriptions.find(AI => AI.id == pData.AI);
|
||||
let AI = g_GameSettings.playerAI.get(this.playerIndex);
|
||||
if (!!AI)
|
||||
AI = g_Settings.AIDescriptions.find(desc => desc.id == AI.bot);
|
||||
this.aiDescription.caption = AI ? AI.data.description : this.NoAIDescription;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
class AIGameSettingControlDropdown extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
onOpenPage(playerIndex)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.gameSettingsControl.registerAssignPlayerHandler(this.onAssignPlayer.bind(this));
|
||||
this.playerIndex = playerIndex;
|
||||
this.render();
|
||||
}
|
||||
|
||||
setControl(aiConfigPage)
|
||||
@@ -25,24 +24,6 @@ class AIGameSettingControlDropdown extends GameSettingControlDropdown
|
||||
|
||||
this.setHidden(false);
|
||||
}
|
||||
|
||||
onOpenPage(playerIndex)
|
||||
{
|
||||
this.playerIndex = playerIndex;
|
||||
this.updateSelectedValue();
|
||||
this.updateVisibility();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
for (let playerIndex = 0; playerIndex < g_MaxPlayers; ++playerIndex)
|
||||
this.onGameAttributesChangePlayer(playerIndex);
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.updateSelectedValue();
|
||||
}
|
||||
}
|
||||
|
||||
AIGameSettingControlDropdown.prototype.Height= 28;
|
||||
|
||||
@@ -4,92 +4,26 @@ AIGameSettingControls.AIBehavior = class extends AIGameSettingControlDropdown
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.fixedAIBehavior = [];
|
||||
this.defaultBehavior = Engine.ConfigDB_GetValue("user", this.ConfigBehavior);
|
||||
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
this.dropdown.list = g_Settings.AIBehaviors.map(AIBehavior => AIBehavior.Title);
|
||||
this.dropdown.list_data = g_Settings.AIBehaviors.map(AIBehavior => AIBehavior.Name);
|
||||
}
|
||||
|
||||
onAssignPlayer(source, target)
|
||||
{
|
||||
if (source && target.AIBehavior)
|
||||
source.AIBehavior = target.AIBehavior;
|
||||
|
||||
delete target.AIBehavior;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
for (let playerIndex = 0; playerIndex < g_MaxPlayers; ++playerIndex)
|
||||
{
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, playerIndex);
|
||||
this.fixedAIBehavior[playerIndex] =
|
||||
mapPData && mapPData.AI ?
|
||||
(mapPData.AIBehavior !== undefined ?
|
||||
mapPData.AIBehavior :
|
||||
g_Settings.PlayerDefaults[playerIndex + 1].AIBehavior) :
|
||||
undefined;
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesChangePlayer(playerIndex)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (pData.AI)
|
||||
{
|
||||
if (this.fixedAIBehavior[playerIndex] &&
|
||||
(pData.AIBehavior === undefined ||
|
||||
pData.AIBehavior !== this.fixedAIBehavior[playerIndex]))
|
||||
{
|
||||
pData.AIBehavior = this.fixedAIBehavior[playerIndex];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
else if (pData.AIDiff !== undefined &&
|
||||
g_Settings.AIDifficulties[pData.AIDiff].Name == "sandbox" &&
|
||||
pData.AIBehavior != "balanced")
|
||||
{
|
||||
pData.AIBehavior = "balanced";
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
else if (pData.AIBehavior === undefined)
|
||||
{
|
||||
pData.AIBehavior = this.defaultBehavior;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (pData.AIBehavior !== undefined)
|
||||
{
|
||||
delete pData.AIBehavior;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectedValue()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
this.setHidden(!pData || !pData.AI || g_Settings.AIDifficulties[pData.AIDiff].Name == "sandbox");
|
||||
if (pData && pData.AI && pData.AIDiff !== undefined && pData.AIBehavior !== undefined)
|
||||
this.setSelectedValue(pData.AIBehavior);
|
||||
let ai = g_GameSettings.playerAI.get(this.playerIndex);
|
||||
this.setHidden(!ai);
|
||||
if (!!ai)
|
||||
this.setSelectedValue(ai.behavior);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!g_IsController || !pData)
|
||||
return;
|
||||
|
||||
pData.AIBehavior = g_Settings.AIBehaviors[itemIdx].Name;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerAI.setBehavior(this.playerIndex, this.dropdown.list_data[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
AIGameSettingControls.AIBehavior.prototype.ConfigBehavior =
|
||||
"gui.gamesetup.aibehavior";
|
||||
};
|
||||
|
||||
AIGameSettingControls.AIBehavior.prototype.TitleCaption =
|
||||
translate("AI Behavior");
|
||||
|
||||
+9
-66
@@ -4,80 +4,23 @@ AIGameSettingControls.AIDifficulty = class extends AIGameSettingControlDropdown
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.fixedAIDiff = [];
|
||||
this.defaultAIDiff = +Engine.ConfigDB_GetValue("user", this.ConfigDifficulty);
|
||||
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
this.dropdown.list = g_Settings.AIDifficulties.map(AI => AI.Title);
|
||||
this.dropdown.list_data = g_Settings.AIDifficulties.map((AI, i) => i);
|
||||
}
|
||||
|
||||
onAssignPlayer(source, target)
|
||||
{
|
||||
if (source && target.AIDiff !== undefined)
|
||||
source.AIDiff = target.AIDiff;
|
||||
|
||||
delete target.AIDiff;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
for (let playerIndex = 0; playerIndex < g_MaxPlayers; ++playerIndex)
|
||||
{
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, playerIndex);
|
||||
this.fixedAIDiff[playerIndex] =
|
||||
mapPData && mapPData.AI ?
|
||||
(mapPData.AIDiff !== undefined ?
|
||||
mapPData.AIDiff :
|
||||
g_Settings.PlayerDefaults[playerIndex + 1].AIDiff) :
|
||||
undefined;
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesChangePlayer(playerIndex)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (pData.AI)
|
||||
{
|
||||
if (this.fixedAIDiff[playerIndex] !== undefined &&
|
||||
(pData.AIDiff === undefined ||
|
||||
pData.AIDiff !== this.fixedAIDiff[playerIndex]))
|
||||
{
|
||||
pData.AIDiff = this.fixedAIDiff[playerIndex];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
else if (pData.AIDiff === undefined)
|
||||
{
|
||||
pData.AIDiff = this.defaultAIDiff;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (pData.AIDiff !== undefined)
|
||||
{
|
||||
delete pData.AIDiff;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectedValue()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
this.setHidden(!pData || !pData.AI);
|
||||
|
||||
if (pData && pData.AIDiff !== undefined)
|
||||
this.setSelectedValue(pData.AIDiff);
|
||||
let ai = g_GameSettings.playerAI.get(this.playerIndex);
|
||||
this.setHidden(!ai);
|
||||
if (!!ai)
|
||||
this.setSelectedValue(ai.difficulty);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!g_IsController || !pData)
|
||||
return;
|
||||
|
||||
pData.AIDiff = itemIdx;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerAI.setDifficulty(this.playerIndex, this.dropdown.list_data[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+12
-59
@@ -4,7 +4,7 @@ AIGameSettingControls.AISelection = class extends AIGameSettingControlDropdown
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.fixedAI = [];
|
||||
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
|
||||
|
||||
this.values = prepareForDropdown([
|
||||
this.NoAI,
|
||||
@@ -15,76 +15,29 @@ AIGameSettingControls.AISelection = class extends AIGameSettingControlDropdown
|
||||
]);
|
||||
|
||||
this.dropdown.list = this.values.Title;
|
||||
this.dropdown.list_data = this.values.Id.map((v, i) => i);
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
}
|
||||
|
||||
onAssignPlayer(source, target)
|
||||
render()
|
||||
{
|
||||
if (source && target.AI)
|
||||
source.AI = target.AI;
|
||||
|
||||
target.AI = false;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
for (let playerIndex = 0; playerIndex < g_MaxPlayers; ++playerIndex)
|
||||
{
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, playerIndex);
|
||||
this.fixedAI[playerIndex] = mapPData && mapPData.AI || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesChangePlayer(playerIndex)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (this.fixedAI[playerIndex] && pData.AI !== this.fixedAI[playerIndex])
|
||||
{
|
||||
pData.AI = this.fixedAI[playerIndex];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
else if (pData.AI === undefined)
|
||||
{
|
||||
let assignedGUID;
|
||||
for (let guid in g_PlayerAssignments)
|
||||
if (g_PlayerAssignments[guid].player == playerIndex + 1)
|
||||
{
|
||||
assignedGUID = guid;
|
||||
break;
|
||||
}
|
||||
|
||||
pData.AI = assignedGUID ? false : g_Settings.PlayerDefaults[playerIndex + 1].AI;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectedValue()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData || pData.AI === undefined)
|
||||
return;
|
||||
|
||||
this.setSelectedValue(this.values.Id.indexOf(pData.AI));
|
||||
let ai = g_GameSettings.playerAI.get(this.playerIndex);
|
||||
this.setHidden(!ai);
|
||||
if (!!ai)
|
||||
this.setSelectedValue(ai.bot);
|
||||
else
|
||||
this.setSelectedValue(undefined);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
pData.AI = this.values.Id[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerAI.setAI(this.playerIndex, this.dropdown.list_data[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AIGameSettingControls.AISelection.prototype.NoAI = {
|
||||
"Title": translateWithContext("ai", "None"),
|
||||
"Id": false
|
||||
"Id": undefined
|
||||
};
|
||||
|
||||
AIGameSettingControls.AISelection.prototype.TitleCaption =
|
||||
|
||||
+10
-43
@@ -2,29 +2,17 @@
|
||||
* The GameSettingControl is an abstract class that is inherited by game-setting control classes specific to a GUI-object type,
|
||||
* such as the GameSettingControlCheckbox or GameSettingControlDropdown.
|
||||
*
|
||||
* These classes are abstract classes too and are implemented by each handler class specific to one logical setting of g_GameAttributes.
|
||||
* The purpose of these classes is to control precisely one logical setting of g_GameAttributes.
|
||||
* Having one class per logical setting allows to handle each setting without making a restriction as to how the property should be written to g_GameAttributes or g_PlayerAssignments.
|
||||
* The purpose of these classes is to control one logical game setting.
|
||||
* The base classes allow implementing that while avoiding duplication.
|
||||
*
|
||||
* A GameSettingControl may depend on and read from other g_GameAttribute values,
|
||||
* but the class instance is to be the sole instance writing to its setting value in g_GameAttributes and
|
||||
* shall not write to setting values of other logical settings.
|
||||
* GameSettingControl classes watch for g_GameSettings property changes,
|
||||
* and re-render accordingly. They also trigger changes in g_GameSettings.
|
||||
*
|
||||
* The derived classes shall not make assumptions on the validity of g_GameAttributes,
|
||||
* sanitize or delete their value if it is incompatible.
|
||||
*
|
||||
* A class should only write values to g_GameAttributes that it itself has confirmed to be accurate.
|
||||
* This means that handlers may not copy an entire object or array of values, for example on mapchange.
|
||||
* This avoids writing a setting value to g_GameAttributes that is not tracked and deleted when it becomes invalid.
|
||||
*
|
||||
* Since GameSettingControls shall be able to subscribe to g_GameAttributes changes,
|
||||
* it is an obligation of the derived GameSettingControl class to broadcast the GameAttributesChange event each time it changes g_GameAttributes.
|
||||
* The GameSettingControl classes are responsible for triggering network synchronisation,
|
||||
* and for updating the whole gamesetup layout when necessary.
|
||||
*/
|
||||
class GameSettingControl
|
||||
class GameSettingControl /* extends Profilable /* Uncomment to profile controls without hassle. */
|
||||
{
|
||||
// The constructor and inherited constructors shall not modify game attributes,
|
||||
// since all GameSettingControl shall be able to subscribe to any game-setting change.
|
||||
constructor(gameSettingControlManager, category, playerIndex, setupWindow)
|
||||
{
|
||||
// Store arguments
|
||||
@@ -59,27 +47,9 @@ class GameSettingControl
|
||||
|
||||
this.setHidden(false);
|
||||
|
||||
if (this.onMapChange)
|
||||
this.gameSettingsControl.registerMapChangeHandler(this.onMapChange.bind(this));
|
||||
|
||||
if (this.onLoad)
|
||||
this.setupWindow.registerLoadHandler(this.onLoad.bind(this));
|
||||
|
||||
if (this.onGameAttributesChange)
|
||||
this.gameSettingsControl.registerGameAttributesChangeHandler(this.onGameAttributesChange.bind(this));
|
||||
|
||||
if (this.onGameAttributesBatchChange)
|
||||
this.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
|
||||
|
||||
if (this.onAssignPlayer && this.playerIndex === 0)
|
||||
this.gameSettingsControl.registerAssignPlayerHandler(this.onAssignPlayer.bind(this));
|
||||
|
||||
if (this.onPickRandomItems)
|
||||
this.gameSettingsControl.registerPickRandomItemsHandler(this.onPickRandomItems.bind(this));
|
||||
|
||||
if (this.onGameAttributesFinalize)
|
||||
this.gameSettingsControl.registerGameAttributesFinalizeHandler(this.onGameAttributesFinalize.bind(this));
|
||||
|
||||
if (this.onPlayerAssignmentsChange)
|
||||
this.playerAssignmentsControl.registerPlayerAssignmentsChangeHandler(this.onPlayerAssignmentsChange.bind(this));
|
||||
}
|
||||
@@ -104,10 +74,6 @@ class GameSettingControl
|
||||
this.setControlTooltip(tooltip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not call functions calling updateVisibility onMapChange but onGameAttributesChange,
|
||||
* so that changes take effect when increasing the playercount as well.
|
||||
*/
|
||||
setEnabled(enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
@@ -117,7 +83,8 @@ class GameSettingControl
|
||||
setHidden(hidden)
|
||||
{
|
||||
this.hidden = hidden;
|
||||
this.updateVisibility();
|
||||
// Trigger a layout update to reposition items.
|
||||
this.gameSettingsControl.updateLayout();
|
||||
}
|
||||
|
||||
updateVisibility()
|
||||
@@ -126,8 +93,8 @@ class GameSettingControl
|
||||
this.hidden ||
|
||||
this.playerIndex === undefined &&
|
||||
this.category != g_TabCategorySelected ||
|
||||
this.playerIndex !== undefined &&
|
||||
g_GameAttributes.settings && this.playerIndex >= g_GameAttributes.settings.PlayerData.length;
|
||||
this.playerIndex !== undefined &&
|
||||
this.playerIndex >= g_GameSettings.playerCount.nbPlayers;
|
||||
|
||||
if (this.frame)
|
||||
this.frame.hidden = hidden;
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ class GameSettingControls
|
||||
}
|
||||
|
||||
/**
|
||||
* The GameSettingControlManager owns all classes that handle a logical property of g_GameAttributes.
|
||||
* The GameSettingControlManager owns all GUI controls.
|
||||
*/
|
||||
class GameSettingControlManager
|
||||
{
|
||||
|
||||
+1
-1
@@ -41,8 +41,8 @@ var g_GameSettingsLayout = [
|
||||
...g_VictoryConditions.map(victoryCondition => victoryCondition.Name),
|
||||
"RelicCount",
|
||||
"RelicDuration",
|
||||
"WonderDuration",
|
||||
"RegicideGarrison",
|
||||
"WonderDuration",
|
||||
"GameSpeed",
|
||||
"Ceasefire",
|
||||
"LockedTeams",
|
||||
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
PlayerSettingControls.AIConfigButton = class extends GameSettingControl
|
||||
PlayerSettingControls.AIConfigButton = class AIConfigButton extends GameSettingControl
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -6,8 +6,10 @@ PlayerSettingControls.AIConfigButton = class extends GameSettingControl
|
||||
|
||||
this.aiConfigButton = Engine.GetGUIObjectByName("aiConfigButton[" + this.playerIndex + "]");
|
||||
|
||||
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
|
||||
// Save little performance by not reallocating every call
|
||||
this.sprintfArgs = {};
|
||||
this.render();
|
||||
}
|
||||
|
||||
onLoad()
|
||||
@@ -16,15 +18,13 @@ PlayerSettingControls.AIConfigButton = class extends GameSettingControl
|
||||
this.aiConfigButton.onPress = aiConfigPage.openPage.bind(aiConfigPage, this.playerIndex);
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
render()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
this.aiConfigButton.hidden = !g_GameSettings.playerAI.get(this.playerIndex);
|
||||
if (this.aiConfigButton.hidden)
|
||||
return;
|
||||
|
||||
this.sprintfArgs.description = translateAISettings(pData);
|
||||
this.sprintfArgs.description = g_GameSettings.playerAI.describe(this.playerIndex);
|
||||
this.aiConfigButton.tooltip = sprintf(this.Tooltip, this.sprintfArgs);
|
||||
this.aiConfigButton.hidden = !pData.AI;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+76
-60
@@ -1,4 +1,15 @@
|
||||
PlayerSettingControls.PlayerAssignment = class extends GameSettingControlDropdown
|
||||
// Declare this first to avoid redundant lint warnings.
|
||||
class PlayerAssignmentItem
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning: this class handles more than most other GUI controls.
|
||||
* Indeed, the logic of how to handle player assignments is here,
|
||||
* as that is not really a GUI-agnostic concern
|
||||
* (campaigns and other autostarting scripts should handle that themselves).
|
||||
*/
|
||||
PlayerSettingControls.PlayerAssignment = class PlayerAssignment extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -16,6 +27,11 @@ PlayerSettingControls.PlayerAssignment = class extends GameSettingControlDropdow
|
||||
this.assignedGUID = undefined;
|
||||
this.fixedAI = undefined;
|
||||
|
||||
g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
|
||||
g_GameSettings.playerCount.watch((_, oldNb) => this.OnPlayerNbChange(oldNb), ["nbPlayers"]);
|
||||
|
||||
// Sets up the dropdown and renders.
|
||||
this.onPlayerAssignmentsChange();
|
||||
this.playerAssignmentsControl.registerClientJoinHandler(this.onClientJoin.bind(this));
|
||||
}
|
||||
|
||||
@@ -29,6 +45,23 @@ PlayerSettingControls.PlayerAssignment = class extends GameSettingControlDropdow
|
||||
{
|
||||
if (!hotloadData && !g_IsNetworked)
|
||||
this.onClientJoin("local", g_PlayerAssignments);
|
||||
this.playerAssignmentsControl.updatePlayerAssignments();
|
||||
}
|
||||
|
||||
OnPlayerNbChange(oldNb)
|
||||
{
|
||||
let isPlayerSlot = Object.values(g_PlayerAssignments).some(x => x.player === this.playerIndex + 1);
|
||||
if (!isPlayerSlot && !g_GameSettings.playerAI.get(this.playerIndex) &&
|
||||
this.playerIndex >= oldNb && this.playerIndex < g_GameSettings.playerCount.nbPlayers)
|
||||
{
|
||||
// Add AIs to unused slots by default.
|
||||
// TODO: we could save the settings in case the player lowers, then re-raises the # of players.
|
||||
g_GameSettings.playerAI.set(this.playerIndex, {
|
||||
"bot": g_Settings.PlayerDefaults[this.playerIndex + 1].AI,
|
||||
"difficulty": +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"),
|
||||
"behavior": Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onClientJoin(newGUID, newAssignments)
|
||||
@@ -36,16 +69,12 @@ PlayerSettingControls.PlayerAssignment = class extends GameSettingControlDropdow
|
||||
if (!g_IsController || this.fixedAI || newAssignments[newGUID].player != -1)
|
||||
return;
|
||||
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
// Assign the client (or only buddies if prefered) to a free slot
|
||||
if (newGUID != Engine.GetPlayerGUID())
|
||||
{
|
||||
let assignOption = Engine.ConfigDB_GetValue("user", this.ConfigAssignPlayers);
|
||||
if (assignOption == "disabled" ||
|
||||
assignOption == "buddies" && g_Buddies.indexOf(splitRatingFromNick(newAssignments[newGUID].name).nick) == -1)
|
||||
assignOption == "buddies" && g_Buddies.indexOf(splitRatingFromNick(newAssignments[newGUID].name).nick) == -1)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,71 +82,45 @@ PlayerSettingControls.PlayerAssignment = class extends GameSettingControlDropdow
|
||||
if (newAssignments[guid].player == this.playerIndex + 1)
|
||||
return;
|
||||
|
||||
if (pData.AI)
|
||||
{
|
||||
pData.AI = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
newAssignments[newGUID].player = this.playerIndex + 1;
|
||||
this.playerAssignmentsControl.assignClient(newGUID, this.playerIndex + 1);
|
||||
}
|
||||
|
||||
onPlayerAssignmentsChange()
|
||||
{
|
||||
this.assignedGUID = undefined;
|
||||
let newGUID;
|
||||
for (let guid in g_PlayerAssignments)
|
||||
if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
|
||||
{
|
||||
this.assignedGUID = guid;
|
||||
newGUID = guid;
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.playerItems && newGUID === this.assignedGUID)
|
||||
return;
|
||||
this.assignedGUID = newGUID;
|
||||
this.playerItems = sortGUIDsByPlayerID().map(
|
||||
this.clientItemFactory.createItem.bind(this.clientItemFactory));
|
||||
|
||||
this.rebuildList();
|
||||
this.updateSelection();
|
||||
this.render();
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
this.fixedAI = mapPData && mapPData.AI || undefined;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (this.fixedAI && (pData.AI === undefined || pData.AI != this.fixedAI))
|
||||
this.setEnabled(true);
|
||||
if (this.assignedGUID)
|
||||
{
|
||||
pData.AI = this.fixedAI;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.playerAssignmentsControl.unassignClient(this.playerIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
this.setSelectedValue(this.assignedGUID);
|
||||
return;
|
||||
}
|
||||
let ai = g_GameSettings.playerAI.get(this.playerIndex);
|
||||
if (ai)
|
||||
{
|
||||
this.setSelectedValue(ai.bot);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setEnabled(!this.fixedAI);
|
||||
this.updateSelection();
|
||||
}
|
||||
|
||||
updateSelection()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (pData && this.values)
|
||||
this.setSelectedValue(
|
||||
this.values.Value.findIndex((value, i) =>
|
||||
this.values.Handler[i].isSelected(pData, this.assignedGUID, value)));
|
||||
this.setSelectedValue(undefined);
|
||||
}
|
||||
|
||||
rebuildList()
|
||||
@@ -130,7 +133,7 @@ PlayerSettingControls.PlayerAssignment = class extends GameSettingControlDropdow
|
||||
]);
|
||||
|
||||
this.dropdown.list = this.values.Caption;
|
||||
this.dropdown.list_data = this.values.Value.map((value, i) => i);
|
||||
this.dropdown.list_data = this.values.Value;
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
|
||||
@@ -157,10 +160,6 @@ PlayerSettingControls.PlayerAssignment.prototype.AutocompleteOrder = 100;
|
||||
PlayerSettingControls.PlayerAssignment.prototype.ConfigAssignPlayers =
|
||||
"gui.gamesetup.assignplayers";
|
||||
|
||||
class PlayerAssignmentItem
|
||||
{
|
||||
}
|
||||
|
||||
{
|
||||
PlayerAssignmentItem.Client = class
|
||||
{
|
||||
@@ -179,8 +178,22 @@ class PlayerAssignmentItem
|
||||
onSelectionChange(gameSettingsControl, playerAssignmentsControl, playerIndex, guidToAssign)
|
||||
{
|
||||
let sourcePlayer = g_PlayerAssignments[guidToAssign].player - 1;
|
||||
if (sourcePlayer >= 0)
|
||||
{
|
||||
let ai = g_GameSettings.playerAI.get(playerIndex);
|
||||
// If the target was an AI, swap so AI settings are kept.
|
||||
if (ai)
|
||||
g_GameSettings.playerAI.swap(sourcePlayer, playerIndex);
|
||||
// Swap color + civ as well - this allows easy reorganizing of player order.
|
||||
if (g_GameSettings.map.type !== "scenario")
|
||||
{
|
||||
g_GameSettings.playerCiv.swap(sourcePlayer, playerIndex);
|
||||
g_GameSettings.playerColor.swap(sourcePlayer, playerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
playerAssignmentsControl.assignPlayer(guidToAssign, playerIndex);
|
||||
gameSettingsControl.assignPlayer(sourcePlayer, playerIndex);
|
||||
gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
isSelected(pData, guid, value)
|
||||
@@ -214,9 +227,12 @@ class PlayerAssignmentItem
|
||||
{
|
||||
playerAssignmentsControl.unassignClient(playerIndex + 1);
|
||||
|
||||
g_GameAttributes.settings.PlayerData[playerIndex].AI = value;
|
||||
g_GameSettings.playerAI.set(playerIndex, {
|
||||
"bot": value,
|
||||
"difficulty": +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty"),
|
||||
"behavior": Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior"),
|
||||
});
|
||||
|
||||
gameSettingsControl.updateGameAttributes();
|
||||
gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
@@ -250,8 +266,8 @@ class PlayerAssignmentItem
|
||||
{
|
||||
playerAssignmentsControl.unassignClient(playerIndex + 1);
|
||||
|
||||
g_GameAttributes.settings.PlayerData[playerIndex].AI = false;
|
||||
gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerAI.setAI(playerIndex, undefined);
|
||||
|
||||
gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
|
||||
+8
-68
@@ -1,14 +1,16 @@
|
||||
PlayerSettingControls.PlayerCiv = class extends GameSettingControlDropdown
|
||||
PlayerSettingControls.PlayerCiv = class PlayerCiv extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.fixedCiv = undefined;
|
||||
this.values = prepareForDropdown(this.getItems());
|
||||
|
||||
this.dropdown.list = this.values.name;
|
||||
this.dropdown.list_data = this.values.civ;
|
||||
|
||||
g_GameSettings.playerCiv.watch(() => this.render(), ["values", "locked"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
setControl()
|
||||
@@ -22,51 +24,10 @@ PlayerSettingControls.PlayerCiv = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = this.values && this.values.tooltip[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
this.fixedCiv = mapPData && mapPData.Civ || undefined;
|
||||
}
|
||||
|
||||
onAssignPlayer(source, target)
|
||||
{
|
||||
if (g_GameAttributes.mapType != "scenario" && source && target)
|
||||
[source.Civ, target.Civ] = [target.Civ, source.Civ];
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData || !g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (this.fixedCiv)
|
||||
{
|
||||
if (!pData.Civ || this.fixedCiv != pData.Civ)
|
||||
{
|
||||
pData.Civ = this.fixedCiv;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (this.values.civ.indexOf(pData.Civ || undefined) == -1)
|
||||
{
|
||||
pData.Civ =
|
||||
g_GameAttributes.mapType == "scenario" ?
|
||||
g_Settings.PlayerDefaults[this.playerIndex + 1].Civ :
|
||||
this.RandomCivId;
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData || !g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
this.setEnabled(!this.fixedCiv);
|
||||
this.setSelectedValue(pData.Civ);
|
||||
this.setEnabled(!g_GameSettings.playerCiv.locked[this.playerIndex]);
|
||||
this.setSelectedValue(g_GameSettings.playerCiv.values[this.playerIndex]);
|
||||
}
|
||||
|
||||
getItems()
|
||||
@@ -101,30 +62,9 @@ PlayerSettingControls.PlayerCiv = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
pData.Civ = this.values.civ[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerCiv.setValue(this.playerIndex, this.values.civ[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onPickRandomItems()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData || pData.Civ != this.RandomCivId)
|
||||
return;
|
||||
|
||||
// Get a unique array of selectable cultures
|
||||
let cultures = Object.keys(g_CivData).filter(civ => g_CivData[civ].SelectableInGameSetup).map(civ => g_CivData[civ].Culture);
|
||||
cultures = cultures.filter((culture, index) => cultures.indexOf(culture) === index);
|
||||
|
||||
// Pick a random civ of a random culture
|
||||
let culture = pickRandom(cultures);
|
||||
pData.Civ = pickRandom(Object.keys(g_CivData).filter(civ =>
|
||||
g_CivData[civ].Culture == culture && g_CivData[civ].SelectableInGameSetup));
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.pickRandomItems();
|
||||
}
|
||||
};
|
||||
|
||||
PlayerSettingControls.PlayerCiv.prototype.Tooltip =
|
||||
|
||||
+13
-141
@@ -1,10 +1,11 @@
|
||||
PlayerSettingControls.PlayerColor = class extends GameSettingControlDropdown
|
||||
PlayerSettingControls.PlayerColor = class PlayerColor extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.defaultColors = g_Settings.PlayerDefaults.slice(1).map(pData => pData.Color);
|
||||
g_GameSettings.playerColor.watch(() => this.render(), ["values", "locked"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
setControl()
|
||||
@@ -14,158 +15,29 @@ PlayerSettingControls.PlayerColor = class extends GameSettingControlDropdown
|
||||
this.playerColorHeading = Engine.GetGUIObjectByName("playerColorHeading");
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
Engine.ProfileStart("updatePlayerColorList");
|
||||
let hidden = !g_IsController || g_GameAttributes.mapType == "scenario";
|
||||
if (g_GameSettings.playerCount.nbPlayers < this.playerIndex + 1)
|
||||
return;
|
||||
|
||||
let hidden = !g_IsController || g_GameSettings.map.type == "scenario";
|
||||
this.dropdown.hidden = hidden;
|
||||
this.playerColorHeading.hidden = hidden;
|
||||
|
||||
// Step 1: Pick colors that the map specifies, add most unsimilar default colors
|
||||
// Provide the access to g_MaxPlayers different colors, regardless of current playercount.
|
||||
let values = [];
|
||||
for (let i = 0; i < g_MaxPlayers; ++i)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(mapData, i);
|
||||
values.push(pData && pData.Color || this.findFarthestUnusedColor(values));
|
||||
}
|
||||
|
||||
// Step 2: Sort these colors so that the order is most reminiscent of the default colors
|
||||
this.values = [];
|
||||
for (let i = 0; i < g_MaxPlayers; ++i)
|
||||
{
|
||||
let closestColor;
|
||||
let smallestDistance = Infinity;
|
||||
for (let color of values)
|
||||
{
|
||||
if (this.values.some(col => sameColor(col, color)))
|
||||
continue;
|
||||
|
||||
let distance = colorDistance(color, this.defaultColors[i]);
|
||||
if (distance <= smallestDistance)
|
||||
{
|
||||
closestColor = color;
|
||||
smallestDistance = distance;
|
||||
}
|
||||
}
|
||||
this.values.push(closestColor);
|
||||
}
|
||||
let value = g_GameSettings.playerColor.get(this.playerIndex);
|
||||
this.setSelectedValue(value);
|
||||
this.playerBackgroundColor.sprite = "color:" + rgbToGuiColor(value, 100);
|
||||
|
||||
this.values = g_GameSettings.playerColor.available;
|
||||
this.dropdown.list = this.values.map(color => coloredText(this.ColorIcon, rgbToGuiColor(color)));
|
||||
this.dropdown.list_data = this.values.map((color, i) => i);
|
||||
|
||||
// If the map specified a color for this slot, use that
|
||||
let mapPlayerData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
let playerData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
|
||||
if (playerData && mapPlayerData && mapPlayerData.Color &&
|
||||
(!playerData.Color || !sameColor(playerData.Color, mapPlayerData.Color)))
|
||||
{
|
||||
playerData.Color = mapPlayerData.Color;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData || !this.values)
|
||||
return;
|
||||
|
||||
let inUse =
|
||||
pData.Color &&
|
||||
g_GameAttributes.settings.PlayerData.some((otherPData, i) =>
|
||||
i < this.playerIndex &&
|
||||
otherPData.Color &&
|
||||
sameColor(pData.Color, otherPData.Color));
|
||||
|
||||
if (!pData.Color || this.values.indexOf(pData.Color) == -1 || inUse)
|
||||
{
|
||||
pData.Color =
|
||||
(pData.Color && !inUse) ?
|
||||
this.findClosestColor(pData.Color, this.values) :
|
||||
this.getUnusedColor();
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData || !this.values)
|
||||
return;
|
||||
|
||||
this.setSelectedValue(this.values.indexOf(pData.Color));
|
||||
this.playerBackgroundColor.sprite = "color:" + rgbToGuiColor(pData.Color, 100);
|
||||
}
|
||||
|
||||
onAssignPlayer(source, target)
|
||||
{
|
||||
if (g_GameAttributes.mapType != "scenario" && source && target)
|
||||
[source.Color, target.Color] = [target.Color, source.Color];
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
|
||||
// If someone else has that color, give that player the old color
|
||||
let otherPData = g_GameAttributes.settings.PlayerData.find(data =>
|
||||
sameColor(this.values[itemIdx], data.Color));
|
||||
|
||||
if (otherPData)
|
||||
otherPData.Color = pData.Color;
|
||||
|
||||
pData.Color = this.values[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerColor.setColor(this.playerIndex, this.values[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
findClosestColor(targetColor, colors)
|
||||
{
|
||||
let colorDistances = colors.map(color => colorDistance(color, targetColor));
|
||||
|
||||
let smallestDistance = colorDistances.find(
|
||||
distance => colorDistances.every(distance2 => distance2 >= distance));
|
||||
|
||||
return colors.find(color => colorDistance(color, targetColor) == smallestDistance);
|
||||
}
|
||||
|
||||
findFarthestUnusedColor(values)
|
||||
{
|
||||
let farthestColor;
|
||||
let farthestDistance = 0;
|
||||
|
||||
for (let defaultColor of this.defaultColors)
|
||||
{
|
||||
let smallestDistance = Infinity;
|
||||
for (let usedColor of values)
|
||||
{
|
||||
let distance = colorDistance(usedColor, defaultColor);
|
||||
if (distance < smallestDistance)
|
||||
smallestDistance = distance;
|
||||
}
|
||||
|
||||
if (smallestDistance >= farthestDistance)
|
||||
{
|
||||
farthestColor = defaultColor;
|
||||
farthestDistance = smallestDistance;
|
||||
}
|
||||
}
|
||||
return farthestColor;
|
||||
}
|
||||
|
||||
getUnusedColor()
|
||||
{
|
||||
return this.values.find(color =>
|
||||
g_GameAttributes &&
|
||||
g_GameAttributes.settings &&
|
||||
g_GameAttributes.settings.PlayerData &&
|
||||
g_GameAttributes.settings.PlayerData.every(pData => !pData.Color || !sameColor(color, pData.Color)));
|
||||
}
|
||||
};
|
||||
|
||||
PlayerSettingControls.PlayerColor.prototype.Tooltip =
|
||||
|
||||
+9
-35
@@ -1,4 +1,4 @@
|
||||
PlayerSettingControls.PlayerTeam = class extends GameSettingControlDropdown
|
||||
PlayerSettingControls.PlayerTeam = class PlayerTeam extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -16,8 +16,12 @@ PlayerSettingControls.PlayerTeam = class extends GameSettingControlDropdown
|
||||
"id": i
|
||||
}))
|
||||
]);
|
||||
|
||||
this.dropdown.list = this.values.label;
|
||||
this.dropdown.list_data = this.values.id;
|
||||
|
||||
g_GameSettings.playerTeam.watch(() => this.render(), ["values", "locked"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
setControl()
|
||||
@@ -26,45 +30,15 @@ PlayerSettingControls.PlayerTeam = class extends GameSettingControlDropdown
|
||||
this.dropdown = Engine.GetGUIObjectByName("playerTeam[" + this.playerIndex + "]");
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
|
||||
if (pData && mapPData && mapPData.Team !== undefined)
|
||||
{
|
||||
pData.Team = mapPData.Team;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (pData.Team === undefined)
|
||||
{
|
||||
pData.Team = this.NoTeamId;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario");
|
||||
this.setSelectedValue(pData.Team);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario");
|
||||
this.setSelectedValue(g_GameSettings.playerTeam.values[this.playerIndex]);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.PlayerData[this.playerIndex].Team = itemIdx - 1;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerTeam.setValue(this.playerIndex, itemIdx - 1);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+7
-4
@@ -1,4 +1,4 @@
|
||||
PlayerSettingControls.PlayerFrame = class extends GameSettingControl
|
||||
PlayerSettingControls.PlayerFrame = class PlayerFrame extends GameSettingControl
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -12,12 +12,15 @@ PlayerSettingControls.PlayerFrame = class extends GameSettingControl
|
||||
size.bottom = this.Height * (this.playerIndex + 1);
|
||||
this.playerFrame.size = size;
|
||||
}
|
||||
|
||||
g_GameSettings.playerCount.watch(() => this.render(), ["nbPlayers"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
render()
|
||||
{
|
||||
this.playerFrame.hidden = !this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
this.playerFrame.hidden = this.playerIndex >= g_GameSettings.playerCount.nbPlayers;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PlayerSettingControls.PlayerFrame.prototype.Height = 32;
|
||||
|
||||
+8
-82
@@ -1,49 +1,16 @@
|
||||
// TODO: There should be an indication which player is not ready yet
|
||||
// The color does not indicate it's meaning and is insufficient to inform many players.
|
||||
PlayerSettingControls.PlayerName = class extends GameSettingControl
|
||||
PlayerSettingControls.PlayerName = class PlayerName extends GameSettingControl
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.playerName = Engine.GetGUIObjectByName("playerName[" + this.playerIndex + "]");
|
||||
g_GameSettings.playerCount.watch(() => this.render(), ["nbPlayers"]);
|
||||
|
||||
this.displayedName = undefined;
|
||||
this.guid = undefined;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
pData.Name = mapPData && mapPData.Name || g_Settings.PlayerDefaults[this.playerIndex + 1].Name;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (!pData.Name)
|
||||
{
|
||||
pData.Name = g_Settings.PlayerDefaults[this.playerIndex + 1].Name;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
this.displayedName = g_IsNetworked ? pData.Name : translate(pData.Name);
|
||||
this.rebuild();
|
||||
this.render();
|
||||
}
|
||||
|
||||
onPlayerAssignmentsChange()
|
||||
@@ -57,65 +24,24 @@ PlayerSettingControls.PlayerName = class extends GameSettingControl
|
||||
break;
|
||||
}
|
||||
|
||||
this.rebuild();
|
||||
this.render();
|
||||
}
|
||||
|
||||
rebuild()
|
||||
render()
|
||||
{
|
||||
let name = this.displayedName;
|
||||
if (!name)
|
||||
return;
|
||||
let name = this.guid ? g_PlayerAssignments[this.guid].name :
|
||||
g_GameSettings.playerName.values[this.playerIndex];
|
||||
|
||||
if (g_IsNetworked)
|
||||
{
|
||||
let status = this.guid ? g_PlayerAssignments[this.guid].status : this.ReadyTags.length - 1;
|
||||
name = setStringTags(this.displayedName, this.ReadyTags[status]);
|
||||
name = setStringTags(name, this.ReadyTags[status]);
|
||||
}
|
||||
|
||||
this.playerName.caption = name;
|
||||
}
|
||||
|
||||
onGameAttributesFinalize()
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.mapType != "scenario" && pData.AI)
|
||||
{
|
||||
// Pick one of the available botnames for the chosen civ
|
||||
// Determine botnames
|
||||
let chosenName = pickRandom(g_CivData[pData.Civ].AINames);
|
||||
|
||||
if (!g_IsNetworked)
|
||||
chosenName = translate(chosenName);
|
||||
|
||||
// Count how many players use the chosenName
|
||||
let usedName = g_GameAttributes.settings.PlayerData.filter(otherPData =>
|
||||
otherPData.Name && otherPData.Name.indexOf(chosenName) !== -1).length;
|
||||
|
||||
pData.Name =
|
||||
usedName ?
|
||||
sprintf(this.RomanLabel, {
|
||||
"playerName": chosenName,
|
||||
"romanNumber": this.RomanNumbers[usedName + 1]
|
||||
}) :
|
||||
chosenName;
|
||||
}
|
||||
else
|
||||
// Copy client playernames so they appear in replays
|
||||
for (let guid in g_PlayerAssignments)
|
||||
if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
|
||||
pData.Name = g_PlayerAssignments[guid].name;
|
||||
}
|
||||
};
|
||||
|
||||
PlayerSettingControls.PlayerName.prototype.RomanLabel =
|
||||
translate("%(playerName)s %(romanNumber)s");
|
||||
|
||||
PlayerSettingControls.PlayerName.prototype.RomanNumbers =
|
||||
[undefined, "I", "II", "III", "IV", "V", "VI", "VII", "VIII"];
|
||||
|
||||
PlayerSettingControls.PlayerName.prototype.ReadyTags = [
|
||||
{
|
||||
"color": "white",
|
||||
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
// TODO: There should be a dialog allowing to specify starting resources and population capacity per player
|
||||
PlayerSettingControls.PlayerSettings = class extends GameSettingControl
|
||||
{
|
||||
onMapChange(mapData)
|
||||
{
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
if (!pData)
|
||||
return;
|
||||
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
let isScenario = mapPData && g_GameAttributes.mapType == "scenario";
|
||||
|
||||
if (isScenario && mapPData.Resources)
|
||||
pData.Resources = mapPData.Resources;
|
||||
else
|
||||
delete pData.Resources;
|
||||
|
||||
if (isScenario && mapPData.PopulationLimit)
|
||||
pData.PopulationLimit = mapPData.PopulationLimit;
|
||||
else
|
||||
delete pData.PopulationLimit;
|
||||
}
|
||||
|
||||
onGameAttributesFinalize()
|
||||
{
|
||||
// Copy map well known properties (and only well known properties)
|
||||
let mapData = this.mapCache.getMapData(g_GameAttributes.mapType, g_GameAttributes.map);
|
||||
|
||||
let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
|
||||
let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
|
||||
if (!pData || !mapPData)
|
||||
return;
|
||||
|
||||
for (let property of this.MapSettings)
|
||||
if (mapPData[property] !== undefined)
|
||||
pData[property] = mapPData[property];
|
||||
}
|
||||
};
|
||||
|
||||
PlayerSettingControls.PlayerSettings.prototype.MapSettings = [
|
||||
"StartingTechnologies",
|
||||
"DisabledTechnologies",
|
||||
"DisabledTemplates",
|
||||
"StartingCamera"
|
||||
];
|
||||
+1
-1
@@ -7,7 +7,7 @@ class PlayerSettingControls
|
||||
}
|
||||
|
||||
/**
|
||||
* The purpose of the PlayerSettingControlManager is to own all controls that handle a property of g_GameAttributes.settings.PlayerData.
|
||||
* The purpose of the PlayerSettingControlManager is to own all GUI player controls.
|
||||
*/
|
||||
class PlayerSettingControlManager
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
GameSettingControls.MapBrowser = class extends GameSettingControlButton
|
||||
GameSettingControls.MapBrowser = class MapBrowser extends GameSettingControlButton
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
|
||||
+11
-17
@@ -1,37 +1,31 @@
|
||||
/**
|
||||
* Cheats are always enabled in singleplayer mode, since they are the choice of that one player.
|
||||
*/
|
||||
GameSettingControls.Cheats = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.Cheats = class Cheats extends GameSettingControlCheckbox
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
this.setHidden(!g_IsNetworked);
|
||||
|
||||
g_GameSettings.cheats.watch(() => this.render(), ["enabled"]);
|
||||
g_GameSettings.rating.watch(() => this.render(), ["enabled"]);
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
onLoad()
|
||||
{
|
||||
if (g_GameAttributes.settings.CheatsEnabled === undefined ||
|
||||
g_GameAttributes.settings.CheatsEnabled && g_GameAttributes.settings.RatingEnabled ||
|
||||
!g_GameAttributes.settings.CheatsEnabled && !g_IsNetworked)
|
||||
{
|
||||
g_GameAttributes.settings.CheatsEnabled = !g_IsNetworked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
g_GameSettings.cheats.setEnabled(!g_IsNetworked);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
render()
|
||||
{
|
||||
this.setChecked(g_GameAttributes.settings.CheatsEnabled);
|
||||
this.setEnabled(!g_GameAttributes.settings.RatingEnabled);
|
||||
this.setChecked(g_GameSettings.cheats.enabled);
|
||||
this.setEnabled(g_IsNetworked && !g_GameSettings.rating.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.CheatsEnabled =
|
||||
!g_IsNetworked ||
|
||||
checked && !g_GameAttributes.settings.RatingEnabled;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.cheats.setEnabled(!g_IsNetworked || checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+10
-39
@@ -1,51 +1,22 @@
|
||||
GameSettingControls.ExploredMap = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.ExploredMap = class ExploredMap extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.ExploreMap !== undefined)
|
||||
mapValue = mapData.settings.ExploreMap;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.ExploreMap)
|
||||
{
|
||||
g_GameAttributes.settings.ExploreMap = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
super(...args);
|
||||
g_GameSettings.mapExploration.watch(() => this.render(), ["explored"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.settings.ExploreMap === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.ExploreMap = !!g_GameAttributes.settings.RevealMap;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
else if (g_GameAttributes.settings.RevealMap &&
|
||||
!g_GameAttributes.settings.ExploreMap)
|
||||
{
|
||||
g_GameAttributes.settings.ExploreMap = true;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
this.setChecked(g_GameAttributes.settings.ExploreMap);
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario" && !g_GameAttributes.settings.RevealMap);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario");
|
||||
this.setChecked(g_GameSettings.mapExploration.explored);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.ExploreMap = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.mapExploration.setExplored(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+10
-43
@@ -1,56 +1,23 @@
|
||||
GameSettingControls.LastManStanding = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.LastManStanding = class LastManStanding extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.LastManStanding !== undefined)
|
||||
mapValue = !mapData.settings.LockTeams &&
|
||||
mapData.settings.LastManStanding;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.LastManStanding)
|
||||
{
|
||||
g_GameAttributes.settings.LastManStanding = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
super(...args);
|
||||
g_GameSettings.lastManStanding.watch(() => this.render(), ["enabled", "available"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
this.available = !g_GameAttributes.settings.LockTeams;
|
||||
if (this.available)
|
||||
{
|
||||
if (g_GameAttributes.settings.LastManStanding === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.LastManStanding = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.settings.LastManStanding !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.LastManStanding;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
// Always display this, so that players are aware that there is this gamemode
|
||||
this.setChecked(!!g_GameAttributes.settings.LastManStanding);
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario" && !g_GameAttributes.settings.LockTeams);
|
||||
this.setChecked(g_GameSettings.lastManStanding.enabled);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario" &&
|
||||
g_GameSettings.lastManStanding.available);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.LastManStanding = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.lastManStanding.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+12
-35
@@ -1,50 +1,27 @@
|
||||
GameSettingControls.LockedTeams = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.LockedTeams = class LockedTeams extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.LockTeams !== undefined)
|
||||
mapValue = !mapData.settings.LockTeams &&
|
||||
mapData.settings.LastManStanding;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.LastManStanding)
|
||||
{
|
||||
g_GameAttributes.settings.LastManStanding = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
super(...args);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
g_GameSettings.rating.watch(() => this.render(), ["available", "enabled"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
onLoad()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.settings.LockTeams === undefined ||
|
||||
g_GameAttributes.settings.RatingEnabled && !g_GameAttributes.settings.LockTeams)
|
||||
{
|
||||
g_GameAttributes.settings.LockTeams = g_IsNetworked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
g_GameSettings.lockedTeams.setEnabled(this.DefaultValue);
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
this.setChecked(g_GameAttributes.settings.LockTeams);
|
||||
|
||||
this.setEnabled(
|
||||
g_GameAttributes.mapType != "scenario" &&
|
||||
!g_GameAttributes.settings.RatingEnabled);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario" && g_GameSettings.lockedTeams.available);
|
||||
this.setChecked(g_GameSettings.lockedTeams.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.LockTeams = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.lockedTeams.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+10
-46
@@ -1,58 +1,22 @@
|
||||
GameSettingControls.Nomad = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.Nomad = class Nomad extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let available = g_GameAttributes.mapType == "random";
|
||||
this.setHidden(!available);
|
||||
if (!available)
|
||||
return;
|
||||
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.Nomad !== undefined)
|
||||
mapValue = mapData.settings.Nomad;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.Nomad)
|
||||
{
|
||||
g_GameAttributes.settings.Nomad = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
super(...args);
|
||||
g_GameSettings.nomad.watch(() => this.render(), ["enabled"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.mapType == "random")
|
||||
{
|
||||
if (g_GameAttributes.settings.Nomad === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.Nomad = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.settings.Nomad !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.Nomad;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.mapType == "random")
|
||||
this.setChecked(g_GameAttributes.settings.Nomad);
|
||||
this.setHidden(g_GameSettings.map.type != "random");
|
||||
this.setChecked(g_GameSettings.nomad.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.Nomad = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.nomad.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+9
-33
@@ -1,50 +1,26 @@
|
||||
GameSettingControls.Rating = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.Rating = class Rating extends GameSettingControlCheckbox
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.hasXmppClient = Engine.HasXmppClient();
|
||||
this.available = false;
|
||||
// The availability of rated games is not a GUI concern, unlike most other
|
||||
// potentially available settings.
|
||||
g_GameSettings.rating.watch(() => this.render(), ["enabled", "available"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
this.available = this.hasXmppClient && g_GameAttributes.settings.PlayerData.length == 2;
|
||||
if (this.available)
|
||||
{
|
||||
if (g_GameAttributes.settings.RatingEnabled === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.RatingEnabled = true;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.settings.RatingEnabled !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.RatingEnabled;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setHidden(!this.available);
|
||||
if (this.available)
|
||||
this.setChecked(g_GameAttributes.settings.RatingEnabled);
|
||||
this.setHidden(!g_GameSettings.rating.available);
|
||||
this.setChecked(g_GameSettings.rating.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.RatingEnabled = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.rating.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onGameAttributesFinalize()
|
||||
{
|
||||
if (this.hasXmppClient)
|
||||
Engine.SetRankedGame(!!g_GameAttributes.settings.RatingEnabled);
|
||||
}
|
||||
};
|
||||
|
||||
GameSettingControls.Rating.prototype.TitleCaption =
|
||||
|
||||
+11
-49
@@ -1,59 +1,24 @@
|
||||
GameSettingControls.RegicideGarrison = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.RegicideGarrison = class RegicideGarrison extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario");
|
||||
super(...args);
|
||||
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.VictoryConditions &&
|
||||
mapData.settings.VictoryConditions.indexOf(this.RegicideName) != -1 &&
|
||||
mapData.settings.RegicideGarrison !== undefined)
|
||||
mapValue = mapData.settings.RegicideGarrison;
|
||||
|
||||
if (mapValue !== undefined || !g_GameAttributes.settings || mapValue == g_GameAttributes.settings.RegicideGarrison)
|
||||
return;
|
||||
|
||||
if (!g_GameAttributes.settings.VictoryConditions)
|
||||
g_GameAttributes.settings.VictoryConditions = [];
|
||||
|
||||
if (g_GameAttributes.settings.VictoryConditions.indexOf(this.RegicideName) == -1)
|
||||
g_GameAttributes.settings.VictoryConditions.push(this.RegicideName);
|
||||
|
||||
g_GameAttributes.settings.RegicideGarrison = mapValue;
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.regicideGarrison.watch(() => this.render(), ["enabled", "available"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.settings.VictoryConditions)
|
||||
return;
|
||||
|
||||
let available = g_GameAttributes.settings.VictoryConditions.indexOf(this.RegicideName) != -1;
|
||||
this.setHidden(!available);
|
||||
|
||||
if (available)
|
||||
{
|
||||
if (g_GameAttributes.settings.RegicideGarrison === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.RegicideGarrison = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
this.setChecked(g_GameAttributes.settings.RegicideGarrison);
|
||||
}
|
||||
else if (g_GameAttributes.settings.RegicideGarrison !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.RegicideGarrison;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
this.setHidden(g_GameSettings.map.type == "scenario" ||
|
||||
!g_GameSettings.regicideGarrison.available);
|
||||
this.setChecked(g_GameSettings.regicideGarrison.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.RegicideGarrison = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.regicideGarrison.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
@@ -63,6 +28,3 @@ GameSettingControls.RegicideGarrison.prototype.TitleCaption =
|
||||
|
||||
GameSettingControls.RegicideGarrison.prototype.Tooltip =
|
||||
translate("Toggle whether heroes can be garrisoned.");
|
||||
|
||||
GameSettingControls.RegicideGarrison.prototype.RegicideName =
|
||||
"regicide";
|
||||
|
||||
+10
-34
@@ -1,46 +1,22 @@
|
||||
GameSettingControls.RevealedMap = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.RevealedMap = class RevealedMap extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.RevealMap !== undefined)
|
||||
mapValue = mapData.settings.RevealMap;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.RevealMap)
|
||||
{
|
||||
g_GameAttributes.settings.RevealMap = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario");
|
||||
super(...args);
|
||||
g_GameSettings.mapExploration.watch(() => this.render(), ["revealed"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.settings.RevealMap === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.RevealMap = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
this.setChecked(g_GameAttributes.settings.RevealMap);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario");
|
||||
this.setChecked(g_GameSettings.mapExploration.revealed);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.RevealMap = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.mapExploration.setRevealed(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+10
-27
@@ -1,40 +1,23 @@
|
||||
GameSettingControls.Spies = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.Spies = class Spies extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.DisableSpies !== undefined)
|
||||
mapValue = mapData.settings.DisableSpies;
|
||||
super(...args);
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.DisableSpies)
|
||||
{
|
||||
g_GameAttributes.settings.DisableSpies = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario");
|
||||
g_GameSettings.disableSpies.watch(() => this.render(), ["enabled"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (g_GameAttributes.settings.DisableSpies === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.DisableSpies = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setChecked(g_GameAttributes.settings.DisableSpies);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario");
|
||||
this.setChecked(g_GameSettings.disableSpies.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.DisableSpies = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.disableSpies.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+10
-28
@@ -1,40 +1,22 @@
|
||||
GameSettingControls.Treasures = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.Treasures = class Treasures extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.DisableTreasures !== undefined)
|
||||
mapValue = mapData.settings.DisableTreasures;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.DisableTreasures)
|
||||
{
|
||||
g_GameAttributes.settings.DisableTreasures = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario");
|
||||
super(...args);
|
||||
g_GameSettings.disableTreasures.watch(() => this.render(), ["enabled"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (g_GameAttributes.settings.DisableTreasures === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.DisableTreasures = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setChecked(g_GameAttributes.settings.DisableTreasures);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario");
|
||||
this.setChecked(g_GameSettings.disableTreasures.enabled);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.DisableTreasures = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.disableTreasures.setEnabled(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+10
-34
@@ -1,46 +1,22 @@
|
||||
GameSettingControls.WorldPopulation = class extends GameSettingControlCheckbox
|
||||
GameSettingControls.WorldPopulation = class WorldPopulation extends GameSettingControlCheckbox
|
||||
{
|
||||
onMapChange(mapData)
|
||||
constructor(...args)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.WorldPopulation !== undefined)
|
||||
mapValue = mapData.settings.WorldPopulation;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.WorldPopulation)
|
||||
{
|
||||
g_GameAttributes.settings.WorldPopulation = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
this.setEnabled(g_GameAttributes.mapType != "scenario");
|
||||
super(...args);
|
||||
g_GameSettings.population.watch(() => this.render(), ["useWorldPop"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
if (g_GameAttributes.settings.WorldPopulation !== undefined)
|
||||
return;
|
||||
|
||||
g_GameAttributes.settings.WorldPopulation = false;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
return;
|
||||
|
||||
this.setChecked(g_GameAttributes.settings.WorldPopulation);
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario");
|
||||
this.setChecked(g_GameSettings.population.useWorldPop);
|
||||
}
|
||||
|
||||
onPress(checked)
|
||||
{
|
||||
g_GameAttributes.settings.WorldPopulation = checked;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.population.setPopCap(checked);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+23
-97
@@ -1,127 +1,53 @@
|
||||
GameSettingControls.Biome = class extends GameSettingControlDropdown
|
||||
GameSettingControls.Biome = class Biome extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.values = undefined;
|
||||
this.biomeValues = undefined;
|
||||
|
||||
this.randomItem = {
|
||||
"Id": this.RandomBiomeId,
|
||||
"Title": setStringTags(this.RandomBiome, this.RandomItemTags),
|
||||
"Autocomplete": this.RandomBiome,
|
||||
"Description": this.RandomDescription
|
||||
};
|
||||
g_GameSettings.biome.watch(() => this.render(), ["biome", "available"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
{
|
||||
this.dropdown.tooltip =
|
||||
this.values && this.values.Description && this.values.Description[this.dropdown.hovered] ||
|
||||
this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
let available = g_GameAttributes.mapType == "random" &&
|
||||
mapData && mapData.settings && mapData.settings.SupportedBiomes || undefined;
|
||||
|
||||
this.setHidden(!available);
|
||||
|
||||
if (available)
|
||||
{
|
||||
Engine.ProfileStart("updateBiomeList");
|
||||
this.biomeValues =
|
||||
g_Settings.Biomes.filter(this.filterBiome(available)).map(this.createBiomeItem);
|
||||
|
||||
this.values = prepareForDropdown([
|
||||
this.randomItem,
|
||||
...this.biomeValues
|
||||
]);
|
||||
|
||||
this.dropdown.list = this.values.Title;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
if (!this.dropdown.list_data[this.dropdown.hovered])
|
||||
this.dropdown.tooltip = "";
|
||||
else if (this.dropdown.list_data[this.dropdown.hovered] == "random")
|
||||
this.dropdown.tooltip = this.RandomDescription;
|
||||
else
|
||||
this.values = undefined;
|
||||
|
||||
this.dropdown.tooltip = g_GameSettings.biome.biomeData[
|
||||
this.dropdown.list_data[this.dropdown.hovered]
|
||||
].Description;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType || !g_GameAttributes.map)
|
||||
return;
|
||||
this.setHidden(!g_GameSettings.biome.available.size);
|
||||
|
||||
if (this.values)
|
||||
{
|
||||
if (this.values.Id.indexOf(g_GameAttributes.settings.Biome || undefined) == -1)
|
||||
let values = prepareForDropdown([
|
||||
{
|
||||
g_GameAttributes.settings.Biome = this.RandomBiomeId;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
"Title": setStringTags(this.RandomBiome, this.RandomItemTags),
|
||||
"Id": "random"
|
||||
},
|
||||
...g_GameSettings.biome.getAvailableBiomeData()
|
||||
]);
|
||||
|
||||
let biomePreviewFile =
|
||||
basename(g_GameAttributes.map) + "_" +
|
||||
basename(g_GameAttributes.settings.Biome || "") + ".png";
|
||||
this.dropdown.list = values.Title;
|
||||
this.dropdown.list_data = values.Id;
|
||||
|
||||
if (!g_GameAttributes.settings.Preview || g_GameAttributes.settings.Preview != biomePreviewFile && Engine.TextureExists(this.mapCache.TexturesPath + this.mapCache.PreviewsPath + biomePreviewFile))
|
||||
g_GameAttributes.settings.Preview = biomePreviewFile;
|
||||
|
||||
}
|
||||
else if (g_GameAttributes.settings.Biome)
|
||||
{
|
||||
delete g_GameAttributes.settings.Biome;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (this.values)
|
||||
this.setSelectedValue(g_GameAttributes.settings.Biome);
|
||||
}
|
||||
|
||||
filterBiome(available)
|
||||
{
|
||||
if (typeof available == "string")
|
||||
return biome => biome.Id.startsWith(available);
|
||||
|
||||
return biome => available.indexOf(biome.Id) != -1;
|
||||
}
|
||||
|
||||
createBiomeItem(biome)
|
||||
{
|
||||
return {
|
||||
"Id": biome.Id,
|
||||
"Title": biome.Title,
|
||||
"Autocomplete": biome.Title,
|
||||
"Description": biome.Description
|
||||
};
|
||||
this.setSelectedValue(g_GameSettings.biome.biome);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
{
|
||||
return this.values && this.values.Autocomplete;
|
||||
return g_GameSettings.biome.biomes;
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.Biome = this.values.Id[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.biome.setBiome(this.dropdown.list_data[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onPickRandomItems()
|
||||
{
|
||||
if (this.values && g_GameAttributes.settings.Biome == this.RandomBiomeId)
|
||||
{
|
||||
g_GameAttributes.settings.Biome = pickRandom(this.biomeValues).Id;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.pickRandomItems();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GameSettingControls.Biome.prototype.TitleCaption =
|
||||
|
||||
+23
-67
@@ -1,10 +1,12 @@
|
||||
GameSettingControls.Daytime = class extends GameSettingControlDropdown
|
||||
GameSettingControls.Daytime = class Daytime extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.values = undefined;
|
||||
g_GameSettings.daytime.watch(() => this.render(), ["value", "data"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -12,65 +14,31 @@ GameSettingControls.Daytime = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
if (mapData && mapData.settings && mapData.settings.Daytime)
|
||||
{
|
||||
this.values = prepareForDropdown([
|
||||
{
|
||||
"Id": "random",
|
||||
"Name": setStringTags(this.RandomTitle, this.RandomItemTags),
|
||||
"Description": this.RandomDescription,
|
||||
"Preview": mapData.settings.Preview
|
||||
},
|
||||
...mapData.settings.Daytime.map(item => ({
|
||||
"Id": item.Id,
|
||||
"Name": translate(item.Name),
|
||||
"Description": translate(item.Description),
|
||||
"Preview": item.Preview
|
||||
}))
|
||||
]);
|
||||
|
||||
this.dropdown.list = this.values.Name;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
}
|
||||
else
|
||||
this.values = undefined;
|
||||
|
||||
this.setHidden(!this.values);
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (!g_GameAttributes.map)
|
||||
this.setHidden(!g_GameSettings.daytime.data);
|
||||
if (!g_GameSettings.daytime.data)
|
||||
return;
|
||||
|
||||
if (this.values)
|
||||
{
|
||||
if (this.values.Id.indexOf(g_GameAttributes.settings.Daytime || undefined) == -1)
|
||||
this.values = prepareForDropdown([
|
||||
{
|
||||
g_GameAttributes.settings.Daytime = "random";
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
"Id": "random",
|
||||
"Name": setStringTags(this.RandomTitle, this.RandomItemTags),
|
||||
"Description": this.RandomDescription,
|
||||
"Preview": g_GameSettings.map.data.settings.Preview
|
||||
},
|
||||
...g_GameSettings.daytime.data.map(item => ({
|
||||
"Id": item.Id,
|
||||
"Name": translate(item.Name),
|
||||
"Description": translate(item.Description),
|
||||
"Preview": item.Preview
|
||||
}))
|
||||
]);
|
||||
|
||||
let preview = this.values.Preview[this.values.Id.indexOf(g_GameAttributes.settings.Daytime)];
|
||||
if (!g_GameAttributes.settings.Preview || g_GameAttributes.settings.Preview != preview)
|
||||
{
|
||||
g_GameAttributes.settings.Preview = preview;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.settings.Daytime !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.Daytime;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
this.dropdown.list = this.values.Name;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (g_GameAttributes.settings.Daytime)
|
||||
this.setSelectedValue(g_GameAttributes.settings.Daytime);
|
||||
this.setSelectedValue(g_GameSettings.daytime.value);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
@@ -80,21 +48,9 @@ GameSettingControls.Daytime = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.Daytime = this.values.Id[itemIdx];
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.daytime.setValue(this.values.Id[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onPickRandomItems()
|
||||
{
|
||||
if (this.values && g_GameAttributes.settings.Daytime == "random")
|
||||
{
|
||||
g_GameAttributes.settings.Daytime = pickRandom(this.values.Id.slice(1));
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.pickRandomItems();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
GameSettingControls.Daytime.prototype.TitleCaption =
|
||||
|
||||
+10
-36
@@ -1,35 +1,21 @@
|
||||
GameSettingControls.GameSpeed = class extends GameSettingControlDropdown
|
||||
GameSettingControls.GameSpeed = class GameSpeed extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.previousAllowFastForward = undefined;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData && mapData.gameSpeed !== undefined)
|
||||
mapValue = mapData.gameSpeed;
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.gameSpeed)
|
||||
{
|
||||
g_GameAttributes.gameSpeed = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
g_GameSettings.gameSpeed.watch(() => this.render(), ["gameSpeed"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onPlayerAssignmentsChange()
|
||||
{
|
||||
this.update();
|
||||
this.render();
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
this.update();
|
||||
}
|
||||
|
||||
update()
|
||||
render()
|
||||
{
|
||||
let allowFastForward = true;
|
||||
for (let guid in g_PlayerAssignments)
|
||||
@@ -41,32 +27,20 @@ GameSettingControls.GameSpeed = class extends GameSettingControlDropdown
|
||||
|
||||
if (this.previousAllowFastForward !== allowFastForward)
|
||||
{
|
||||
Engine.ProfileStart("updateGameSpeedList");
|
||||
this.previousAllowFastForward = allowFastForward;
|
||||
this.values = prepareForDropdown(
|
||||
let values = prepareForDropdown(
|
||||
g_Settings.GameSpeeds.filter(speed => !speed.FastForward || allowFastForward));
|
||||
|
||||
this.dropdown.list = this.values.Title;
|
||||
this.dropdown.list_data = this.values.Speed;
|
||||
Engine.ProfileStop();
|
||||
this.dropdown.list = values.Title;
|
||||
this.dropdown.list_data = values.Speed;
|
||||
}
|
||||
|
||||
if (this.values.Speed.indexOf(g_GameAttributes.gameSpeed || undefined) == -1)
|
||||
{
|
||||
g_GameAttributes.gameSpeed = this.values.Speed[this.values.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setSelectedValue(g_GameAttributes.gameSpeed);
|
||||
this.setSelectedValue(g_GameSettings.gameSpeed.gameSpeed);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.gameSpeed = this.values.Speed[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.gameSpeed.setSpeed(this.dropdown.list_data[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+26
-105
@@ -1,11 +1,12 @@
|
||||
GameSettingControls.Landscape = class extends GameSettingControlDropdown
|
||||
GameSettingControls.Landscape = class Landscape extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.values = undefined;
|
||||
this.mapData = undefined;
|
||||
g_GameSettings.landscape.watch(() => this.render(), ["value", "data"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -13,37 +14,34 @@ GameSettingControls.Landscape = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
this.mapData = mapData;
|
||||
this.setHidden(!g_GameSettings.landscape.data);
|
||||
if (!g_GameSettings.landscape.data)
|
||||
return;
|
||||
|
||||
if (mapData && mapData.settings && mapData.settings.Landscapes)
|
||||
let randomItems = [{
|
||||
"Id": "random",
|
||||
"Name": setStringTags(translateWithContext("landscape selection", "Random"), this.RandomItemTags),
|
||||
"Description": translateWithContext("landscape selection", "Select a random landscape.")
|
||||
}];
|
||||
let data = g_GameSettings.landscape.data;
|
||||
let items = [];
|
||||
for (let group of data)
|
||||
{
|
||||
let randomItems = [];
|
||||
for (let item of this.RandomItems)
|
||||
if (item.Id == "random" || mapData.settings.Landscapes.land && mapData.settings.Landscapes.naval)
|
||||
randomItems.push({
|
||||
"Id": item.Id,
|
||||
"Name": setStringTags(item.Name, this.RandomItemTags),
|
||||
"Description": item.Description,
|
||||
"Preview": mapData.settings.Preview || this.mapCache.DefaultPreview
|
||||
});
|
||||
|
||||
let itemTag = this.translateItem(group);
|
||||
itemTag.Name = setStringTags(itemTag.Name, this.RandomItemTags);
|
||||
randomItems.push(itemTag);
|
||||
let sort = (item1, item2) => item1.Name > item2.Name;
|
||||
|
||||
this.values = prepareForDropdown([
|
||||
...randomItems,
|
||||
...mapData.settings.Landscapes.land.map(this.translateItem).sort(sort),
|
||||
...mapData.settings.Landscapes.naval.map(this.translateItem).sort(sort)
|
||||
]);
|
||||
|
||||
this.dropdown.list = this.values.Name;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
items = items.concat(group.Items.map(this.translateItem).sort(sort));
|
||||
}
|
||||
else
|
||||
this.values = undefined;
|
||||
|
||||
this.setHidden(!this.values);
|
||||
this.values = prepareForDropdown(randomItems.concat(items));
|
||||
|
||||
this.dropdown.list = this.values.Name;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
|
||||
this.setSelectedValue(g_GameSettings.landscape.value);
|
||||
}
|
||||
|
||||
translateItem(item)
|
||||
@@ -56,34 +54,6 @@ GameSettingControls.Landscape = class extends GameSettingControlDropdown
|
||||
};
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (this.values)
|
||||
{
|
||||
if (this.values.Id.indexOf(g_GameAttributes.settings.Landscape || undefined) == -1)
|
||||
{
|
||||
g_GameAttributes.settings.Landscape = "random";
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
let landscapePreview = this.values.Preview[this.values.Id.indexOf(g_GameAttributes.settings.Landscape)];
|
||||
if (!g_GameAttributes.settings.Preview || g_GameAttributes.settings.Preview != landscapePreview)
|
||||
g_GameAttributes.settings.Preview = landscapePreview;
|
||||
|
||||
}
|
||||
else if (g_GameAttributes.settings.Landscape !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.Landscape;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (g_GameAttributes.settings.Landscape)
|
||||
this.setSelectedValue(g_GameAttributes.settings.Landscape);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
{
|
||||
if (!this.values)
|
||||
@@ -99,39 +69,9 @@ GameSettingControls.Landscape = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.Landscape = this.values.Id[itemIdx];
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.landscape.setValue(this.values.Id[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onPickRandomItems()
|
||||
{
|
||||
if (!this.mapData)
|
||||
return;
|
||||
|
||||
let items;
|
||||
let landscapes = this.mapData.settings.Landscapes;
|
||||
|
||||
switch (g_GameAttributes.settings.Landscape || undefined)
|
||||
{
|
||||
case "random":
|
||||
items = [...landscapes.land, ...landscapes.naval];
|
||||
break;
|
||||
case "random_land":
|
||||
items = landscapes.land;
|
||||
break;
|
||||
case "random_naval":
|
||||
items = landscapes.naval;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
g_GameAttributes.settings.Landscape = pickRandom(items).Id;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.pickRandomItems();
|
||||
}
|
||||
};
|
||||
|
||||
GameSettingControls.Landscape.prototype.TitleCaption =
|
||||
@@ -140,23 +80,4 @@ GameSettingControls.Landscape.prototype.TitleCaption =
|
||||
GameSettingControls.Landscape.prototype.Tooltip =
|
||||
translate("Select one of the landscapes of this map.");
|
||||
|
||||
GameSettingControls.Landscape.prototype.RandomItems =
|
||||
[
|
||||
{
|
||||
"Id": "random",
|
||||
"Name": translateWithContext("landscape selection", "Random Land or Naval"),
|
||||
"Description": translateWithContext("landscape selection", "Select a random land or naval map generation.")
|
||||
},
|
||||
{
|
||||
"Id": "random_land",
|
||||
"Name": translateWithContext("landscape selection", "Random Land"),
|
||||
"Description": translateWithContext("landscape selection", "Select a random land map generation.")
|
||||
},
|
||||
{
|
||||
"Id": "random_naval",
|
||||
"Name": translateWithContext("landscape selection", "Random Naval"),
|
||||
"Description": translateWithContext("landscape selection", "Select a random naval map generation.")
|
||||
}
|
||||
];
|
||||
|
||||
GameSettingControls.Landscape.prototype.AutocompleteOrder = 0;
|
||||
|
||||
+14
-45
@@ -1,11 +1,12 @@
|
||||
GameSettingControls.MapFilter = class extends GameSettingControlDropdown
|
||||
GameSettingControls.MapFilter = class MapFilter extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.values = undefined;
|
||||
this.previousMapType = undefined;
|
||||
|
||||
g_GameSettings.map.watch(() => this.checkMapTypeChange(), ["type"]);
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -13,36 +14,14 @@ GameSettingControls.MapFilter = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
this.checkMapTypeChange();
|
||||
|
||||
if (this.values)
|
||||
{
|
||||
if (this.values.Name.indexOf(g_GameAttributes.mapFilter || undefined) == -1)
|
||||
{
|
||||
g_GameAttributes.mapFilter = this.values.Name[this.values.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.mapFilter !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.mapFilter;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
checkMapTypeChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType || this.previousMapType == g_GameAttributes.mapType)
|
||||
if (!g_GameSettings.map.type)
|
||||
return;
|
||||
|
||||
Engine.ProfileStart("updateMapFilterList");
|
||||
this.previousMapType = g_GameAttributes.mapType;
|
||||
|
||||
let values = prepareForDropdown(
|
||||
this.mapFilters.getAvailableMapFilters(
|
||||
g_GameAttributes.mapType));
|
||||
g_GameSettings.map.type));
|
||||
|
||||
if (values.Name.length)
|
||||
{
|
||||
@@ -53,14 +32,15 @@ GameSettingControls.MapFilter = class extends GameSettingControlDropdown
|
||||
else
|
||||
this.values = undefined;
|
||||
|
||||
this.setHidden(!this.values);
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
if (this.values && this.values.Name.indexOf(this.gameSettingsControl.guiData.mapFilter.filter) === -1)
|
||||
{
|
||||
this.gameSettingsControl.guiData.mapFilter.filter = this.values.Name[this.values.Default];
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
// Index may have changed, reset.
|
||||
this.setSelectedValue(this.gameSettingsControl.guiData.mapFilter.filter);
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (this.values && g_GameAttributes.mapFilter)
|
||||
this.setSelectedValue(g_GameAttributes.mapFilter);
|
||||
this.setHidden(!this.values);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
@@ -70,18 +50,7 @@ GameSettingControls.MapFilter = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
if (this.values)
|
||||
{
|
||||
g_GameAttributes.mapFilter = this.values.Name[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesFinalize()
|
||||
{
|
||||
// This setting is only relevant in the game-setup stage.
|
||||
delete g_GameAttributes.mapFilter;
|
||||
this.gameSettingsControl.guiData.mapFilter.filter = this.values.Name[itemIdx];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+42
-95
@@ -1,4 +1,4 @@
|
||||
GameSettingControls.MapSelection = class extends GameSettingControlDropdown
|
||||
GameSettingControls.MapSelection = class MapSelection extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -6,8 +6,10 @@ GameSettingControls.MapSelection = class extends GameSettingControlDropdown
|
||||
|
||||
this.values = undefined;
|
||||
|
||||
this.previousMapType = undefined;
|
||||
this.previousMapFilter = undefined;
|
||||
g_GameSettings.map.watch(() => this.render(), ["map"]);
|
||||
g_GameSettings.map.watch(() => this.updateMapList(), ["type"]);
|
||||
|
||||
this.gameSettingsControl.guiData.mapFilter.watch(() => this.updateMapList(), ["filter"]);
|
||||
|
||||
this.randomItem = {
|
||||
"file": this.RandomMapId,
|
||||
@@ -21,51 +23,34 @@ GameSettingControls.MapSelection = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = this.values.description[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (!g_GameAttributes.mapType || !g_GameAttributes.mapFilter)
|
||||
return;
|
||||
|
||||
this.updateMapList();
|
||||
|
||||
if (!this.gameSettingsControl.autostart &&
|
||||
this.values &&
|
||||
this.values.file.indexOf(g_GameAttributes.map || undefined) == -1)
|
||||
// Can happen with bad matchsettings
|
||||
if (this.values.file.indexOf(g_GameSettings.map.map) === -1)
|
||||
{
|
||||
g_GameAttributes.map = this.values.file[0];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.map.selectMap(this.values.file[this.values.Default]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (g_GameAttributes.map)
|
||||
this.setSelectedValue(g_GameAttributes.map);
|
||||
this.setSelectedValue(g_GameSettings.map.map);
|
||||
}
|
||||
|
||||
updateMapList()
|
||||
{
|
||||
if (this.previousMapType &&
|
||||
this.previousMapType == g_GameAttributes.mapType &&
|
||||
this.previousMapFilter &&
|
||||
this.previousMapFilter == g_GameAttributes.mapFilter)
|
||||
return;
|
||||
|
||||
Engine.ProfileStart("updateMapSelectionList");
|
||||
|
||||
this.previousMapType = g_GameAttributes.mapType;
|
||||
this.previousMapFilter = g_GameAttributes.mapFilter;
|
||||
if (!g_GameSettings.map.type)
|
||||
return;
|
||||
|
||||
{
|
||||
let values =
|
||||
this.mapFilters.getFilteredMaps(
|
||||
g_GameAttributes.mapType,
|
||||
g_GameAttributes.mapFilter,
|
||||
g_GameSettings.map.type,
|
||||
this.gameSettingsControl.guiData.mapFilter.filter,
|
||||
false);
|
||||
|
||||
values.sort(sortNameIgnoreCase);
|
||||
|
||||
if (g_GameAttributes.mapType == "random")
|
||||
if (g_GameSettings.map.type == "random")
|
||||
values.unshift(this.randomItem);
|
||||
|
||||
this.values = prepareForDropdown(values);
|
||||
@@ -73,70 +58,44 @@ GameSettingControls.MapSelection = class extends GameSettingControlDropdown
|
||||
|
||||
this.dropdown.list = this.values.name;
|
||||
this.dropdown.list_data = this.values.file;
|
||||
|
||||
g_GameSettings.map.setRandomOptions(this.values.file);
|
||||
|
||||
// Reset the selected map.
|
||||
if (this.values.file.indexOf(g_GameSettings.map.map) === -1)
|
||||
{
|
||||
g_GameSettings.map.selectMap(this.values.file[this.values.Default]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
// The index may have changed: reset.
|
||||
this.setSelectedValue(g_GameSettings.map.map);
|
||||
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
this.selectMap(this.values.file[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mapPath - for example "maps/skirmishes/Acropolis Bay (2)"
|
||||
*/
|
||||
selectMap(mapPath)
|
||||
{
|
||||
if (g_GameAttributes.map == mapPath)
|
||||
// The triggering that happens on map change can be just slow enough
|
||||
// that the next event happens before we're done when scrolling,
|
||||
// and then the scrolling is not smooth since it can take arbitrarily long to render.
|
||||
// To avoid that, run the change on the next GUI tick, and only do one increment.
|
||||
// TODO: the problem is mostly that updating visibility can relayout the gamesetting,
|
||||
// which takes a few ms, but this could only be done once per frame anyways.
|
||||
// NB: this technically makes it possible to start the game without the change going through
|
||||
// but it's essentially impossible to trigger accidentally.
|
||||
if (this.reRenderTimeout)
|
||||
return;
|
||||
|
||||
Engine.ProfileStart("selectMap");
|
||||
|
||||
// For scenario map, reset every setting per map selection
|
||||
// For skirmish and random maps, persist player choices
|
||||
if (g_GameAttributes.mapType == "scenario")
|
||||
g_GameAttributes = {
|
||||
"mapType": g_GameAttributes.mapType,
|
||||
"mapFilter": g_GameAttributes.mapFilter
|
||||
};
|
||||
|
||||
g_GameAttributes.map = mapPath;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
|
||||
Engine.ProfileStop();
|
||||
this.reRenderTimeout = setTimeout(() => {
|
||||
g_GameSettings.map.selectMap(this.values.file[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
delete this.reRenderTimeout;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
{
|
||||
return this.values.name;
|
||||
}
|
||||
|
||||
onPickRandomItems()
|
||||
{
|
||||
if (g_GameAttributes.map == this.RandomMapId)
|
||||
{
|
||||
this.selectMap(pickRandom(this.values.file.slice(1)));
|
||||
this.gameSettingsControl.pickRandomItems();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesFinalize()
|
||||
{
|
||||
// Copy map well known properties (and only well known properties)
|
||||
let mapData = this.mapCache.getMapData(g_GameAttributes.mapType, g_GameAttributes.map);
|
||||
|
||||
if (g_GameAttributes.mapType == "random")
|
||||
g_GameAttributes.script = mapData.settings.Script;
|
||||
|
||||
g_GameAttributes.settings.TriggerScripts = Array.from(new Set([
|
||||
...(g_GameAttributes.settings.TriggerScripts || []),
|
||||
...(mapData.settings.TriggerScripts || [])
|
||||
]));
|
||||
|
||||
for (let property of this.MapSettings)
|
||||
if (mapData.settings[property] !== undefined)
|
||||
g_GameAttributes.settings[property] = mapData.settings[property];
|
||||
}
|
||||
};
|
||||
|
||||
GameSettingControls.MapSelection.prototype.TitleCaption =
|
||||
@@ -155,15 +114,3 @@ GameSettingControls.MapSelection.prototype.RandomMapDescription =
|
||||
translate("Pick any of the given maps at random.");
|
||||
|
||||
GameSettingControls.MapSelection.prototype.AutocompleteOrder = 0;
|
||||
|
||||
GameSettingControls.MapSelection.prototype.MapSettings = [
|
||||
"CircularMap",
|
||||
"StartingTechnologies",
|
||||
"DisabledTechnologies",
|
||||
"DisabledTemplates",
|
||||
"StartingCamera",
|
||||
"Garrison",
|
||||
// Copy the map name so that the replay menu doesn't have to load hundreds of map descriptions on init
|
||||
// Also it allows determining the english mapname from the replay file if the map is not available.
|
||||
"Name"
|
||||
];
|
||||
|
||||
+9
-40
@@ -1,4 +1,4 @@
|
||||
GameSettingControls.MapSize = class extends GameSettingControlDropdown
|
||||
GameSettingControls.MapSize = class MapSize extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -6,6 +6,9 @@ GameSettingControls.MapSize = class extends GameSettingControlDropdown
|
||||
|
||||
this.dropdown.list = g_MapSizes.Name;
|
||||
this.dropdown.list_data = g_MapSizes.Tiles;
|
||||
|
||||
g_GameSettings.mapSize.watch(() => this.render(), ["size", "available"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -13,44 +16,11 @@ GameSettingControls.MapSize = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = g_MapSizes.Tooltip[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.Size !== undefined)
|
||||
mapValue = mapData.settings.Size;
|
||||
|
||||
if (g_GameAttributes.mapType == "random" && mapValue !== undefined && mapValue != g_GameAttributes.settings.Size)
|
||||
{
|
||||
g_GameAttributes.settings.Size = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType ||
|
||||
!g_GameAttributes.settings)
|
||||
return;
|
||||
|
||||
let available = g_GameAttributes.mapType == "random";
|
||||
this.setHidden(!available);
|
||||
|
||||
if (available)
|
||||
{
|
||||
if (g_GameAttributes.settings.Size === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.Size = g_MapSizes.Tiles[g_MapSizes.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
this.setSelectedValue(g_GameAttributes.settings.Size);
|
||||
}
|
||||
else if (g_GameAttributes.settings.Size !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.Size;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
this.setHidden(!g_GameSettings.mapSize.available);
|
||||
this.setSelectedValue(g_GameSettings.mapSize.size);
|
||||
// TODO: select first entry.
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
@@ -60,8 +30,7 @@ GameSettingControls.MapSize = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.Size = g_MapSizes.Tiles[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.mapSize.setSize(g_MapSizes.Tiles[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+15
-25
@@ -4,7 +4,7 @@
|
||||
* Skirmish maps have fixed terrain, playercount but settings are free.
|
||||
* For random maps, settings are fully determined by the player and the terrain is generated based on them.
|
||||
*/
|
||||
GameSettingControls.MapType = class extends GameSettingControlDropdown
|
||||
GameSettingControls.MapType = class MapType extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -12,6 +12,17 @@ GameSettingControls.MapType = class extends GameSettingControlDropdown
|
||||
|
||||
this.dropdown.list = g_MapTypes.Title;
|
||||
this.dropdown.list_data = g_MapTypes.Name;
|
||||
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onLoad()
|
||||
{
|
||||
// Select a default map type if none are currently chosen.
|
||||
// This in cascade will select a default filter and a default map.
|
||||
if (!g_GameSettings.map.type)
|
||||
g_GameSettings.map.setType(g_MapTypes.Name[g_MapTypes.Default]);
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -19,18 +30,9 @@ GameSettingControls.MapType = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = g_MapTypes.Description[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
render()
|
||||
{
|
||||
if (g_MapTypes.Name.indexOf(g_GameAttributes.mapType || undefined) == -1)
|
||||
{
|
||||
g_GameAttributes.mapType = g_MapTypes.Name[g_MapTypes.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setSelectedValue(g_GameAttributes.mapType);
|
||||
this.setSelectedValue(g_GameSettings.map.type);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
@@ -40,19 +42,7 @@ GameSettingControls.MapType = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let mapType = g_MapTypes.Name[itemIdx];
|
||||
|
||||
if (g_GameAttributes.mapType == mapType)
|
||||
return;
|
||||
|
||||
if (mapType == "scenario")
|
||||
g_GameAttributes = {
|
||||
"mapFilter": g_GameAttributes.mapFilter
|
||||
};
|
||||
|
||||
g_GameAttributes.mapType = mapType;
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.map.setType(g_MapTypes.Name[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+9
-25
@@ -1,4 +1,4 @@
|
||||
GameSettingControls.PlayerCount = class extends GameSettingControlDropdown
|
||||
GameSettingControls.PlayerCount = class PlayerCount extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -10,38 +10,22 @@ GameSettingControls.PlayerCount = class extends GameSettingControlDropdown
|
||||
|
||||
this.dropdown.list = this.values;
|
||||
this.dropdown.list_data = this.values;
|
||||
|
||||
g_GameSettings.playerCount.watch(() => this.render(), ["nbPlayers"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.PlayerData &&
|
||||
mapData.settings.PlayerData.length != g_GameAttributes.settings.PlayerData.length)
|
||||
{
|
||||
this.onSelectionChange(this.values.indexOf(mapData.settings.PlayerData.length));
|
||||
}
|
||||
|
||||
this.setEnabled(g_GameAttributes.mapType == "random");
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setSelectedValue(g_GameAttributes.settings.PlayerData.length);
|
||||
this.setEnabled(g_GameSettings.map.type == "random");
|
||||
this.setSelectedValue(g_GameSettings.playerCount.nbPlayers);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
let length = this.values[itemIdx];
|
||||
if (g_GameAttributes.settings.PlayerData.length == length)
|
||||
return;
|
||||
|
||||
g_GameAttributes.settings.PlayerData.length = length;
|
||||
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.playerCount.setNb(this.values[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
|
||||
this.playerAssignmentsControl.unassignInvalidPlayers();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+12
-50
@@ -1,4 +1,4 @@
|
||||
GameSettingControls.PopulationCap = class extends GameSettingControlDropdown
|
||||
GameSettingControls.PopulationCap = class PopulationCap extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -10,57 +10,20 @@ GameSettingControls.PopulationCap = class extends GameSettingControlDropdown
|
||||
this.dropdown.list_data = g_PopulationCapacities.Population;
|
||||
|
||||
this.sprintfArgs = {};
|
||||
|
||||
g_GameSettings.population.watch(() => this.render(), ["useWorldPop", "cap", "perPlayer"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.PopulationCap !== undefined)
|
||||
mapValue = mapData.settings.PopulationCap;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.PopulationCap)
|
||||
{
|
||||
g_GameAttributes.settings.PopulationCap = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
let isScenario = g_GameAttributes.mapType == "scenario";
|
||||
|
||||
this.perPlayer =
|
||||
isScenario &&
|
||||
mapData && mapData.settings && mapData.settings.PlayerData &&
|
||||
mapData.settings.PlayerData.some(pData => pData && pData.PopulationLimit !== undefined);
|
||||
|
||||
this.setEnabled(!isScenario && !this.perPlayer);
|
||||
|
||||
if (this.perPlayer)
|
||||
this.setHidden(g_GameSettings.population.useWorldPop);
|
||||
this.setEnabled(!g_GameSettings.map.type == "scenario" && !g_GameSettings.population.perPlayer);
|
||||
if (g_GameSettings.population.perPlayer)
|
||||
this.label.caption = this.PerPlayerCaption;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (g_GameAttributes.settings.WorldPopulation)
|
||||
{
|
||||
this.setHidden(true);
|
||||
g_GameAttributes.settings.PopulationCap = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setHidden(false);
|
||||
if (g_GameAttributes.settings.PopulationCap === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[g_PopulationCapacities.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!this.perPlayer)
|
||||
this.setSelectedValue(g_GameAttributes.settings.PopulationCap);
|
||||
this.setSelectedValue(g_GameSettings.population.cap);
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -69,7 +32,7 @@ GameSettingControls.PopulationCap = class extends GameSettingControlDropdown
|
||||
if (this.dropdown.hovered != -1)
|
||||
{
|
||||
let popCap = g_PopulationCapacities.Population[this.dropdown.hovered];
|
||||
let players = g_GameAttributes.settings.PlayerData.length;
|
||||
let players = g_GameSettings.playerCount.nbPlayers;
|
||||
if (popCap * players >= this.PopulationCapacityRecommendation)
|
||||
{
|
||||
this.sprintfArgs.players = players;
|
||||
@@ -82,8 +45,7 @@ GameSettingControls.PopulationCap = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.population.setPopCap(false, g_PopulationCapacities.Population[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+11
-43
@@ -1,4 +1,4 @@
|
||||
GameSettingControls.StartingResources = class extends GameSettingControlDropdown
|
||||
GameSettingControls.StartingResources = class StartingResources extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
@@ -7,8 +7,11 @@ GameSettingControls.StartingResources = class extends GameSettingControlDropdown
|
||||
this.dropdown.list = g_StartingResources.Title;
|
||||
this.dropdown.list_data = g_StartingResources.Resources;
|
||||
|
||||
this.perPlayer = false;
|
||||
this.sprintfArgs = {};
|
||||
|
||||
g_GameSettings.startingResources.watch(() => this.render(), ["resources", "perPlayer"]);
|
||||
g_GameSettings.map.watch(() => this.render(), ["type"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -22,47 +25,13 @@ GameSettingControls.StartingResources = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let mapValue;
|
||||
if (mapData &&
|
||||
mapData.settings &&
|
||||
mapData.settings.StartingResources !== undefined)
|
||||
mapValue = mapData.settings.StartingResources;
|
||||
|
||||
if (mapValue !== undefined && mapValue != g_GameAttributes.settings.StartingResources)
|
||||
{
|
||||
g_GameAttributes.settings.StartingResources = mapValue;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
|
||||
let isScenario = g_GameAttributes.mapType == "scenario";
|
||||
|
||||
this.perPlayer =
|
||||
isScenario &&
|
||||
mapData && mapData.settings && mapData.settings.PlayerData &&
|
||||
mapData.settings.PlayerData.some(pData => pData && pData.Resources !== undefined);
|
||||
|
||||
this.setEnabled(!isScenario && !this.perPlayer);
|
||||
|
||||
if (this.perPlayer)
|
||||
this.setEnabled(g_GameSettings.map.type != "scenario" && !g_GameSettings.startingResources.perPlayer);
|
||||
if (g_GameSettings.startingResources.perPlayer)
|
||||
this.label.caption = this.PerPlayerCaption;
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (g_GameAttributes.settings.StartingResources === undefined)
|
||||
{
|
||||
g_GameAttributes.settings.StartingResources =
|
||||
g_StartingResources.Resources[g_StartingResources.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (!this.perPlayer)
|
||||
this.setSelectedValue(g_GameAttributes.settings.StartingResources);
|
||||
else
|
||||
this.setSelectedValue(g_GameSettings.startingResources.resources);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
@@ -72,8 +41,7 @@ GameSettingControls.StartingResources = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.StartingResources = g_StartingResources.Resources[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.startingResources.setResources(g_StartingResources.Resources[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
+19
-80
@@ -1,9 +1,12 @@
|
||||
GameSettingControls.TeamPlacement = class extends GameSettingControlDropdown
|
||||
GameSettingControls.TeamPlacement = class TeamPlacement extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
this.values = undefined;
|
||||
|
||||
g_GameSettings.teamPlacement.watch(() => this.render(), ["value", "available"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -11,57 +14,27 @@ GameSettingControls.TeamPlacement = class extends GameSettingControlDropdown
|
||||
this.dropdown.tooltip = this.values.Description[this.dropdown.hovered] || this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
if (mapData && mapData.settings && mapData.settings.TeamPlacements)
|
||||
{
|
||||
let randomItem = clone(this.RandomItem);
|
||||
randomItem.Name = setStringTags(randomItem.Name, this.RandomItemTags);
|
||||
this.setHidden(!g_GameSettings.teamPlacement.value);
|
||||
if (!g_GameSettings.teamPlacement.value)
|
||||
return;
|
||||
|
||||
let patterns = [randomItem];
|
||||
let randomItem = clone(this.RandomItem);
|
||||
randomItem.Name = setStringTags(randomItem.Name, this.RandomItemTags);
|
||||
|
||||
for (let pattern of mapData.settings.TeamPlacements)
|
||||
patterns.push(
|
||||
typeof pattern == "string" ?
|
||||
this.DefaultStartingPositions.find(pObj => pObj.Id == pattern) :
|
||||
{
|
||||
"Id": pattern.Id,
|
||||
"Name": translate(pattern.Name),
|
||||
"Description": translate(pattern.Description)
|
||||
});
|
||||
let patterns = [randomItem];
|
||||
|
||||
this.values = prepareForDropdown(patterns);
|
||||
for (let pattern of g_GameSettings.teamPlacement.available)
|
||||
patterns.push(g_GameSettings.teamPlacement.StartingPositions
|
||||
.find(pObj => pObj.Id == pattern));
|
||||
|
||||
this.dropdown.list = this.values.Name;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
}
|
||||
else
|
||||
this.values = undefined;
|
||||
}
|
||||
this.values = prepareForDropdown(patterns);
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (this.values)
|
||||
{
|
||||
if (this.values.Id.indexOf(g_GameAttributes.settings.TeamPlacement || undefined) == -1)
|
||||
{
|
||||
g_GameAttributes.settings.TeamPlacement = this.values.Id[0];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.settings.TeamPlacement !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.TeamPlacement;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
this.dropdown.list = this.values.Name;
|
||||
this.dropdown.list_data = this.values.Id;
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
this.setHidden(!this.values);
|
||||
|
||||
if (this.values)
|
||||
this.setSelectedValue(g_GameAttributes.settings.TeamPlacement);
|
||||
this.setSelectedValue(g_GameSettings.teamPlacement.value);
|
||||
}
|
||||
|
||||
getAutocompleteEntries()
|
||||
@@ -71,20 +44,9 @@ GameSettingControls.TeamPlacement = class extends GameSettingControlDropdown
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.TeamPlacement = this.values.Id[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.teamPlacement.setValue(this.values.Id[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
|
||||
onPickRandomItems()
|
||||
{
|
||||
if (!this.values || g_GameAttributes.settings.TeamPlacement != "random")
|
||||
return;
|
||||
|
||||
g_GameAttributes.settings.TeamPlacement = pickRandom(this.values.Id.filter(id => id != "random"));
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
this.gameSettingsControl.pickRandomItems();
|
||||
}
|
||||
};
|
||||
|
||||
GameSettingControls.TeamPlacement.prototype.TitleCaption =
|
||||
@@ -99,27 +61,4 @@ GameSettingControls.TeamPlacement.prototype.RandomItem = {
|
||||
"Description": translateWithContext("team placement", "Select a random team placement pattern when starting the game.")
|
||||
};
|
||||
|
||||
GameSettingControls.TeamPlacement.prototype.DefaultStartingPositions = [
|
||||
{
|
||||
"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."),
|
||||
}
|
||||
];
|
||||
|
||||
GameSettingControls.TeamPlacement.prototype.AutocompleteOrder = 0;
|
||||
|
||||
+12
-52
@@ -1,10 +1,12 @@
|
||||
GameSettingControls.TriggerDifficulty = class extends GameSettingControlDropdown
|
||||
GameSettingControls.TriggerDifficulty = class TriggerDifficulty extends GameSettingControlDropdown
|
||||
{
|
||||
constructor(...args)
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.values = undefined;
|
||||
g_GameSettings.triggerDifficulty.watch(() => this.render(), ["value", "available"]);
|
||||
this.render();
|
||||
}
|
||||
|
||||
onHoverChange()
|
||||
@@ -14,65 +16,23 @@ GameSettingControls.TriggerDifficulty = class extends GameSettingControlDropdown
|
||||
this.Tooltip;
|
||||
}
|
||||
|
||||
onMapChange(mapData)
|
||||
render()
|
||||
{
|
||||
let available = mapData && mapData.settings && mapData.settings.SupportedTriggerDifficulties || undefined;
|
||||
this.setHidden(!available);
|
||||
|
||||
if (available)
|
||||
{
|
||||
Engine.ProfileStart("updateTriggerDifficultyList");
|
||||
let values = g_Settings.TriggerDifficulties;
|
||||
if (Array.isArray(available.Values))
|
||||
values = values.filter(diff => available.Values.indexOf(diff.Name) != -1);
|
||||
|
||||
this.values = prepareForDropdown(values);
|
||||
|
||||
this.dropdown.list = this.values.Title;
|
||||
this.dropdown.list_data = this.values.Difficulty;
|
||||
|
||||
if (mapData.settings.TriggerDifficulty &&
|
||||
this.values.Difficulty.indexOf(mapData.settings.TriggerDifficulty) != -1)
|
||||
g_GameAttributes.settings.TriggerDifficulty = mapData.settings.TriggerDifficulty;
|
||||
Engine.ProfileStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.values = undefined;
|
||||
this.defaultValue = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
onGameAttributesChange()
|
||||
{
|
||||
if (!g_GameAttributes.mapType)
|
||||
this.setHidden(!g_GameSettings.triggerDifficulty.available);
|
||||
if (!g_GameSettings.triggerDifficulty.available)
|
||||
return;
|
||||
|
||||
if (this.values)
|
||||
{
|
||||
if (this.values.Difficulty.indexOf(g_GameAttributes.settings.TriggerDifficulty || undefined) == -1)
|
||||
{
|
||||
g_GameAttributes.settings.TriggerDifficulty = this.values.Difficulty[this.values.Default];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
else if (g_GameAttributes.settings.TriggerDifficulty !== undefined)
|
||||
{
|
||||
delete g_GameAttributes.settings.TriggerDifficulty;
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
}
|
||||
}
|
||||
this.values = prepareForDropdown(g_GameSettings.triggerDifficulty.getAvailableSettings());
|
||||
|
||||
onGameAttributesBatchChange()
|
||||
{
|
||||
if (this.values)
|
||||
this.setSelectedValue(g_GameAttributes.settings.TriggerDifficulty);
|
||||
this.dropdown.list = this.values.Title;
|
||||
this.dropdown.list_data = this.values.Difficulty;
|
||||
|
||||
this.setSelectedValue(g_GameSettings.triggerDifficulty.value);
|
||||
}
|
||||
|
||||
onSelectionChange(itemIdx)
|
||||
{
|
||||
g_GameAttributes.settings.TriggerDifficulty = this.values.Difficulty[itemIdx];
|
||||
this.gameSettingsControl.updateGameAttributes();
|
||||
g_GameSettings.triggerDifficulty.setValue(this.values.Difficulty[itemIdx]);
|
||||
this.gameSettingsControl.setNetworkGameAttributes();
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user