Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapName.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapSize.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapSize.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapSize.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapSize.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Nomad.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Nomad.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Nomad.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Nomad.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js (revision 25077)
@@ -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");
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCiv.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCount.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCount.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCount.js (revision 25077)
@@ -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));
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerCount.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js (revision 25077)
@@ -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
+ });
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/autostart/autostart.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/autostart/autostart.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/autostart/autostart.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/autostart/autostart.xml (revision 25077)
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/autostart/autostart.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js (revision 25077)
@@ -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"];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerName.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Rating.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Population.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Population.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Population.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Population.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Relic.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Relic.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Relic.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Relic.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingCamera.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js (revision 25077)
@@ -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];
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerDifficulty.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js (revision 25077)
@@ -1,164 +1,186 @@
/**
* This is the main menu screen of the campaign.
* It shows you the currently available scenarios, scenarios you've already completed, etc.
* This particular variant is extremely simple and shows a list similar to Age 1's campaigns,
* but conceptually nothing really prevents more complex systems.
*/
class CampaignMenu extends AutoWatcher
{
constructor(campaignRun)
{
super("render");
this.run = campaignRun;
this.selectedLevel = -1;
this.levelSelection = Engine.GetGUIObjectByName("levelSelection");
this.levelSelection.onSelectionChange = () => { this.selectedLevel = this.levelSelection.selected; };
this.levelSelection.onMouseLeftDoubleClickItem = () => this.startScenario();
Engine.GetGUIObjectByName('startButton').onPress = () => this.startScenario();
Engine.GetGUIObjectByName('backToMain').onPress = () => this.goBackToMainMenu();
Engine.GetGUIObjectByName('savedGamesButton').onPress = () => Engine.PushGuiPage('page_loadgame.xml', {
'campaignRun': this.run.filename
});
this.mapCache = new MapCache();
this._ready = true;
}
goBackToMainMenu()
{
this.run.save();
Engine.SwitchGuiPage("page_pregame.xml", {});
}
startScenario()
{
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
});
}
getSelectedLevelData()
{
if (this.selectedLevel === -1)
return undefined;
return this.run.template.Levels[this.levelSelection.list_data[this.selectedLevel]];
}
shouldShowLevel(levelData)
{
if (this.run.template.ShowUnavailable)
return true;
return meetsRequirements(this.run, levelData);
}
getLevelName(levelData)
{
if (levelData.Name)
return translateWithContext("Campaign Template", levelData.Name);
return translate(this.mapCache.getTranslatableMapName(levelData.MapType, "maps/" + levelData.Map));
}
getLevelDescription(levelData)
{
if (levelData.Description)
return translateWithContext("Campaign Template", levelData.Description);
return this.mapCache.getTranslatedMapDescription(levelData.MapType, "maps/" + levelData.Map);
}
displayLevelsList()
{
let list = [];
for (let key in this.run.template.Levels)
{
let level = this.run.template.Levels[key];
if (!this.shouldShowLevel(level))
continue;
let status = "";
let name = this.getLevelName(level);
if (isCompleted(this.run, key))
status = translateWithContext("campaign status", "Completed");
else if (meetsRequirements(this.run, level))
status = coloredText(translateWithContext("campaign status", "Available"), "green");
else
name = coloredText(name, "gray");
list.push({ "ID": key, "name": name, "status": status });
}
list.sort((a, b) => this.run.template.Order.indexOf(a.ID) - this.run.template.Order.indexOf(b.ID));
list = prepareForDropdown(list);
this.levelSelection.list_name = list.name || [];
this.levelSelection.list_status = list.status || [];
// COList needs these changed last or crashes.
this.levelSelection.list = list.ID || [];
this.levelSelection.list_data = list.ID || [];
}
displayLevelDetails()
{
if (this.selectedLevel === -1)
{
Engine.GetGUIObjectByName("startButton").enabled = false;
Engine.GetGUIObjectByName("startButton").hidden = false;
return;
}
let level = this.getSelectedLevelData();
Engine.GetGUIObjectByName("scenarioName").caption = this.getLevelName(level);
Engine.GetGUIObjectByName("scenarioDesc").caption = this.getLevelDescription(level);
if (level.Preview)
Engine.GetGUIObjectByName('levelPreviewBox').sprite = "cropped:" + 400/512 + "," + 300/512 + ":" + level.Preview;
else
Engine.GetGUIObjectByName('levelPreviewBox').sprite = "cropped:" + 400/512 + "," + 300/512 + ":session/icons/mappreview/nopreview.png";
Engine.GetGUIObjectByName("startButton").enabled = meetsRequirements(this.run, level);
Engine.GetGUIObjectByName("startButton").hidden = false;
Engine.GetGUIObjectByName("loadSavedButton").hidden = true;
}
render()
{
Engine.GetGUIObjectByName("campaignTitle").caption = this.run.getLabel();
this.displayLevelDetails();
this.displayLevelsList();
}
}
var g_CampaignMenu;
function init(initData)
{
let run = initData?.filename || CampaignRun.getCurrentRunFilename();
try {
run = new CampaignRun(run).load();
if (!run.isCurrent())
run.setCurrent();
g_CampaignMenu = new CampaignMenu(run);
} catch (err) {
error(sprintf(translate("Error loading campaign run %s: %s."), CampaignRun.getCurrentRunFilename(), err));
Engine.SwitchGuiPage("page_pregame.xml", {});
}
}
Index: ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.xml (revision 25077)
@@ -1,72 +1,75 @@
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/common/Observable.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/Observable.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/common/Observable.js (revision 25077)
@@ -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();
Property changes on: ps/trunk/binaries/data/mods/public/gui/common/Observable.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/common/Profilable.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/Profilable.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/common/Profilable.js (revision 25077)
@@ -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();
Property changes on: ps/trunk/binaries/data/mods/public/gui/common/Profilable.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js (revision 25077)
@@ -1,446 +1,449 @@
/**
* Highlights the victory condition in the game-description.
*/
var g_DescriptionHighlight = "orange";
/**
* The rating assigned to lobby players who didn't complete a ranked 1v1 yet.
*/
var g_DefaultLobbyRating = 1200;
/**
* XEP-0172 doesn't restrict nicknames, but our lobby policy does.
* So use this human readable delimiter to separate buddy names in the config file.
*/
var g_BuddyListDelimiter = ",";
/**
* Returns the nickname without the lobby rating.
*/
function splitRatingFromNick(playerName)
{
let result = /^(\S+)\ \((\d+)\)$/g.exec(playerName);
return { "nick": result ? result[1] : playerName, "rating": result ? +result[2] : "" };
}
/**
* Array of playernames that the current user has marked as buddies.
*/
var g_Buddies = Engine.ConfigDB_GetValue("user", "lobby.buddies").split(g_BuddyListDelimiter);
/**
* Denotes which players are a lobby buddy of the current user.
*/
var g_BuddySymbol = '•';
/**
* Returns a formatted string describing the player assignments.
* Needs g_CivData to translate!
*
* @param {Object} playerDataArray - As known from game setup and sim state.
* @param {(string[]|false)} playerStates - One of "won", "defeated", "active" for each player.
* @returns {string}
*/
function formatPlayerInfo(playerDataArray, playerStates)
{
let playerDescriptions = {};
let playerIdx = 0;
for (let playerData of playerDataArray)
{
if (playerData == null || playerData.Civ && playerData.Civ == "gaia")
continue;
++playerIdx;
let teamIdx = playerData.Team;
let isAI = playerData.AI && playerData.AI != "";
let playerState = playerStates && playerStates[playerIdx] || playerData.State;
let isActive = !playerState || playerState == "active";
let playerDescription;
if (isAI)
{
if (playerData.Civ)
{
if (isActive)
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(civ)s, %(AIdescription)s)");
else
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(civ)s, %(AIdescription)s, %(state)s)");
}
else
{
if (isActive)
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(AIdescription)s)");
else
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(AIdescription)s, %(state)s)");
}
}
else
{
if (playerData.Offline)
{
// Can only occur in the lobby for now, so no strings with civ needed
if (isActive)
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (OFFLINE)");
else
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (OFFLINE, %(state)s)");
}
else
{
if (playerData.Civ)
if (isActive)
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(civ)s)");
else
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(civ)s, %(state)s)");
else
if (isActive)
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s");
else
// Translation: Describe a player in a selected game, f.e. in the replay- or savegame menu
playerDescription = translate("%(playerName)s (%(state)s)");
}
}
// Sort player descriptions by team
if (!playerDescriptions[teamIdx])
playerDescriptions[teamIdx] = [];
let playerNick = splitRatingFromNick(playerData.Name).nick;
playerDescriptions[teamIdx].push(sprintf(playerDescription, {
"playerName":
coloredText(
(g_Buddies.indexOf(playerNick) != -1 ? g_BuddySymbol + " " : "") +
escapeText(playerData.Name),
(typeof getPlayerColor == 'function' ?
(isAI ? "white" : getPlayerColor(playerNick)) :
rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color))),
"civ":
!playerData.Civ ?
translate("Unknown Civilization") :
g_CivData && g_CivData[playerData.Civ] && g_CivData[playerData.Civ].Name ?
translate(g_CivData[playerData.Civ].Name) :
playerData.Civ,
"state":
playerState == "defeated" ?
translateWithContext("playerstate", "defeated") :
translateWithContext("playerstate", "won"),
"AIdescription": translateAISettings(playerData)
}));
}
let teams = Object.keys(playerDescriptions);
if (teams.indexOf("observer") > -1)
teams.splice(teams.indexOf("observer"), 1);
let teamDescription = [];
// If there are no teams, merge all playersDescriptions
if (teams.length == 1)
teamDescription.push(playerDescriptions[teams[0]].join("\n"));
// If there are teams, merge "Team N:" + playerDescriptions
else
teamDescription = teams.map(team => {
let teamCaption = team == -1 ?
translate("No Team") :
sprintf(translate("Team %(team)s"), { "team": +team + 1 });
// Translation: Describe players of one team in a selected game, f.e. in the replay- or savegame menu or lobby
return sprintf(translate("%(team)s:\n%(playerDescriptions)s"), {
"team": '[font="sans-bold-14"]' + teamCaption + "[/font]",
"playerDescriptions": playerDescriptions[team].join("\n")
});
});
if (playerDescriptions.observer)
teamDescription.push(sprintf(translate("%(team)s:\n%(playerDescriptions)s"), {
"team": '[font="sans-bold-14"]' + translatePlural("Observer", "Observers", playerDescriptions.observer.length) + "[/font]",
"playerDescriptions": playerDescriptions.observer.join("\n")
}));
return teamDescription.join("\n\n");
}
/**
* 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.")
});
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",
"Wonder (%(min)s minute)",
"Wonder (%(min)s minutes)",
wonderDuration
),
{ "min": wonderDuration });
}
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",
"Capture the Relic (%(min)s minute)",
"Capture the Relic (%(min)s minutes)",
relicDuration
),
{ "min": relicDuration });
}
titles.push({
"label": title,
"value": victoryCondition.Description
});
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.")
});
else
titles.push({
"label": translate("Exposed Heroes"),
"value": translate("Heroes cannot be garrisoned and they are vulnerable to raids.")
});
}
- 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.")
});
else
titles.push({
"label": translate("Diplomacy"),
"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.")
});
else
titles.push({
"label": translate("Allied Victory"),
"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.",
"For the first %(min)s minutes, other players will stay neutral.",
ceasefire),
{ "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.")
});
else
{
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"),
"value": mapSize
});
}
- 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"), {
"label": coloredText(title.label, g_DescriptionHighlight),
"details":
title.value === true ? translateWithContext("game setup option", "enabled") :
title.value || translateWithContext("game setup option", "disabled")
})).join("\n");
}
/**
* Sets the win/defeat icon to indicate current player's state.
*/
function setOutcomeIcon(state, image)
{
if (state == "won")
{
image.sprite = "stretched:session/icons/victory.png";
image.tooltip = translate("Victorious");
}
else if (state == "defeated")
{
image.sprite = "stretched:session/icons/defeat.png";
image.tooltip = translate("Defeated");
}
}
function translateAISettings(playerData)
{
if (!playerData.AI)
return "";
return sprintf(translate("%(AIdifficulty)s %(AIbehavior)s %(AIname)s"), {
"AIname": translateAIName(playerData.AI),
"AIdifficulty": translateAIDifficulty(playerData.AIDiff),
"AIbehavior": translateAIBehavior(playerData.AIBehavior),
});
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSetting.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSetting.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSetting.js (revision 25077)
@@ -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];
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSetting.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js (revision 25077)
@@ -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,
+});
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/GameSettings.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Biome.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Biome.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Biome.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Biome.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CampaignData.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Ceasefire.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Cheats.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Cheats.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Cheats.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Cheats.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/CircularMap.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Daytime.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Daytime.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Daytime.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Daytime.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableSpies.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableSpies.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableSpies.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableSpies.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableTreasures.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableTreasures.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableTreasures.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisableTreasures.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTechnologies.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTechnologies.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTechnologies.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTechnologies.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTemplates.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTemplates.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTemplates.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/DisabledTemplates.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/GameSpeed.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Landscape.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Landscape.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Landscape.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Landscape.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LastManStanding.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LastManStanding.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LastManStanding.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LastManStanding.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LockedTeams.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LockedTeams.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LockedTeams.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/LockedTeams.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Map.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Map.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Map.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Map.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapExploration.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapExploration.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapExploration.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapExploration.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MapPreview.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/MatchID.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerAI.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerAI.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerAI.js (revision 25077)
@@ -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,
+ });
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerAI.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js (revision 25077)
@@ -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));
+ });
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerColor.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js (revision 25077)
@@ -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");
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/PlayerTeam.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/RegicideGarrison.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js (revision 25077)
@@ -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));
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Seeds.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js (revision 25077)
@@ -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."),
+ }
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TeamPlacement.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingTechnologies.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingTechnologies.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingTechnologies.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingTechnologies.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerSettings.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerSettings.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerSettings.js (nonexistent)
@@ -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"
-];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerSettings.js
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Seed.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Seed.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Seed.js (nonexistent)
@@ -1,12 +0,0 @@
-GameSettingControls.Seed = class extends GameSettingControl
-{
- onGameAttributesFinalize()
- {
- // The matchID is used for identifying rated game reports for the lobby and possibly when sharing replays.
- g_GameAttributes.matchID = Engine.GetMatchID();
-
- // Seed is used for map generation and simulation.
- g_GameAttributes.settings.Seed = randIntExclusive(0, Math.pow(2, 32));
- g_GameAttributes.settings.AISeed = randIntExclusive(0, Math.pow(2, 32));
- }
-};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Seed.js
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js (revision 25077)
@@ -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)));
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/SeaLevelRise.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingResources.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingResources.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingResources.js (revision 25077)
@@ -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;
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/StartingResources.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/TriggerScripts.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsFile.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsFile.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsFile.js (revision 25077)
@@ -1,62 +1,64 @@
/**
- * 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
{
constructor(GameSettingsControl)
{
this.filename = g_IsNetworked ?
this.GameAttributesFileMultiplayer :
this.GameAttributesFileSingleplayer;
+ this.gameSettingsControl = GameSettingsControl;
+
this.engineInfo = Engine.GetEngineInfo();
this.enabled = Engine.ConfigDB_GetValue("user", this.ConfigName) == "true";
}
loadFile()
{
Engine.ProfileStart("loadPersistMatchSettingsFile");
let data =
this.enabled &&
g_IsController &&
Engine.FileExists(this.filename) &&
Engine.ReadJSONFile(this.filename);
let gameAttributes =
data &&
data.attributes &&
data.engine_info &&
data.engine_info.engine_version == this.engineInfo.engine_version &&
hasSameMods(data.engine_info.mods, this.engineInfo.mods) &&
data.attributes || {};
Engine.ProfileStop();
return gameAttributes;
}
/**
* Delete settings if disabled, so that players are not confronted with old settings after enabling the setting again.
*/
saveFile()
{
if (!g_IsController)
return;
Engine.ProfileStart("savePersistMatchSettingsFile");
Engine.WriteJSONFile(this.filename, {
- "attributes": this.enabled ? g_GameAttributes : {},
+ "attributes": this.enabled ? this.gameSettingsControl.getSettings() : {},
"engine_info": this.engineInfo
});
Engine.ProfileStop();
}
}
GameSettingsFile.prototype.ConfigName =
"persistmatchsettings";
GameSettingsFile.prototype.GameAttributesFileSingleplayer =
"config/matchsettings.json";
GameSettingsFile.prototype.GameAttributesFileMultiplayer =
"config/matchsettings.mp.json";
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js (revision 25077)
@@ -1,299 +1,155 @@
/**
- * 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
{
constructor(setupWindow, netMessages, startGameControl, mapCache)
{
this.startGameControl = startGameControl;
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));
startGameControl.registerLaunchGameHandler(this.onLaunchGame.bind(this));
setupWindow.registerClosePageHandler(this.onClose.bind(this));
if (g_IsNetworked)
netMessages.registerNetMessageHandler("gamesetup", this.onGamesetupMessage.bind(this));
}
- registerMapChangeHandler(handler)
- {
- this.mapChangeHandlers.add(handler);
- }
-
- unregisterMapChangeHandler(handler)
- {
- this.mapChangeHandlers.delete(handler);
- }
-
- /**
- * This message is triggered everytime g_GameAttributes change.
- * Handlers may subsequently change g_GameAttributes and trigger this message again.
- */
- registerGameAttributesChangeHandler(handler)
- {
- this.gameAttributesChangeHandlers.add(handler);
- }
-
- unregisterGameAttributesChangeHandler(handler)
+ registerUpdateLayoutHandler(handler)
{
- this.gameAttributesChangeHandlers.delete(handler);
+ this.updateLayoutHandlers.add(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.
+ * @param handler will be called when any setting change.
+ * (this isn't exactly what happens but the behaviour should be similar).
*/
- 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)
+ registerSettingsChangeHandler(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");
-
- if (this.depth >= this.MaxDepth)
- {
- error("Infinite loop: " + new Error().stack);
- Engine.ProfileStop();
- 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();
+ let ret = g_GameSettings.toInitAttributes();
+ ret.guiData = this.guiData.Serialize();
+ return ret;
+ }
- --this.depth;
+ /**
+ * Parse the following settings.
+ */
+ parseSettings(settings)
+ {
+ if (settings.guiData)
+ this.guiData.Deserialize(settings.guiData);
+ g_GameSettings.fromInitAttributes(settings);
+ }
- if (this.depth == 0)
- {
- for (let handler of this.gameAttributesBatchChangeHandlers)
+ /**
+ * 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.layoutTimer = setTimeout(() => {
+ for (let handler of this.updateLayoutHandlers)
handler();
- Engine.ProfileStop();
- }
+ delete this.layoutTimer;
+ }, 0);
}
/**
* This function is to be called when a GUI control has initiated a value change.
*
* To avoid an infinite loop, do not call this function when a game setup message was
* received and the data had only been modified deterministically.
*
* This is run on a timer to avoid flooding the network with messages,
* e.g. when modifying a slider.
*/
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;
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Wonder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Wonder.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Wonder.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/Wonder.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/VictoryConditions.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/VictoryConditions.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/VictoryConditions.js (revision 25077)
@@ -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);
+ }
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesettings/attributes/VictoryConditions.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/StartGameControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/StartGameControl.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/StartGameControl.js (revision 25077)
@@ -1,53 +1,53 @@
/**
* Cheat prevention:
*
* 1. Ensure that the host cannot start the game unless all clients agreed on the game settings using the ready system.
*
* TODO:
* 2. Ensure that the host cannot start the game with GameAttributes different from the agreed ones.
* This may be achieved by:
* - Determining the seed collectively.
* - passing the agreed game settings to the engine when starting the game instance
* - rejecting new game settings from the server after the game launch event
*/
class StartGameControl
{
constructor(netMessages)
{
this.gameLaunchHandlers = new Set();
// 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));
}
registerLaunchGameHandler(handler)
{
this.gameLaunchHandlers.add(handler);
}
launchGame()
{
this.gameStarted = true;
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
});
}
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIGameSettingControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIGameSettingControl.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIGameSettingControl.js (revision 25077)
@@ -1,50 +1,31 @@
class AIGameSettingControlDropdown extends GameSettingControlDropdown
{
- constructor(...args)
+ onOpenPage(playerIndex)
{
- super(...args);
-
- this.gameSettingsControl.registerAssignPlayerHandler(this.onAssignPlayer.bind(this));
+ this.playerIndex = playerIndex;
+ this.render();
}
setControl(aiConfigPage)
{
aiConfigPage.registerOpenPageHandler(this.onOpenPage.bind(this));
let i = aiConfigPage.getRow();
this.frame = Engine.GetGUIObjectByName("aiSettingFrame[" + i + "]");
this.title = this.frame.children[0];
this.dropdown = this.frame.children[1];
this.label = this.frame.children[2];
let size = this.frame.size;
size.top = i * (this.Height + this.Margin);
size.bottom = size.top + this.Height;
this.frame.size = size;
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;
AIGameSettingControlDropdown.prototype.Margin= 7;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControl.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControl.js (revision 25077)
@@ -1,178 +1,145 @@
/**
* 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
{
this.category = category;
this.playerIndex = playerIndex;
this.setupWindow = setupWindow;
this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
this.mapCache = setupWindow.controls.mapCache;
this.mapFilters = setupWindow.controls.mapFilters;
this.netMessages = setupWindow.controls.netMessages;
this.playerAssignmentsControl = setupWindow.controls.playerAssignmentsControl;
}
// enabled and hidden should only be modified through their setters or
// by calling updateVisibility after modification.
this.enabled = true;
this.hidden = false;
if (this.setControl)
this.setControl(gameSettingControlManager);
// This variable also used for autocompleting chat.
this.autocompleteTitle = undefined;
if (this.title && this.TitleCaption)
this.setTitle(this.TitleCaption);
if (this.Tooltip)
this.setTooltip(this.Tooltip);
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));
}
setTitle(titleCaption)
{
this.autocompleteTitle = titleCaption;
this.title.caption = sprintf(this.TitleCaptionFormat, {
"setting": titleCaption
});
}
setTooltip(tooltip)
{
if (this.title)
this.title.tooltip = tooltip;
if (this.label)
this.label.tooltip = tooltip;
if (this.setControlTooltip)
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;
this.updateVisibility();
}
setHidden(hidden)
{
this.hidden = hidden;
- this.updateVisibility();
+ // Trigger a layout update to reposition items.
+ this.gameSettingsControl.updateLayout();
}
updateVisibility()
{
let hidden =
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;
if (hidden)
return;
let enabled = g_IsController && this.enabled;
if (this.setControlHidden)
this.setControlHidden(!enabled);
if (this.label)
this.label.hidden = !!enabled;
}
/**
* Returns whether the control specifies an order but didn't implement the function.
*/
addAutocompleteEntries(name, autocomplete)
{
if (this.autocompleteTitle)
autocomplete[0].push(this.autocompleteTitle);
if (!Number.isInteger(this.AutocompleteOrder))
return;
if (!this.getAutocompleteEntries)
{
error(name + " specifies AutocompleteOrder but didn't implement getAutocompleteEntries");
return;
}
let newEntries = this.getAutocompleteEntries();
if (newEntries)
autocomplete[this.AutocompleteOrder] =
(autocomplete[this.AutocompleteOrder] || []).concat(newEntries);
}
}
GameSettingControl.prototype.TitleCaptionFormat =
translateWithContext("Title for specific setting", "%(setting)s:");
/**
* Derived classes can set this to a number to enable chat autocompleting of setting values.
* Higher numbers are autocompleted first.
*/
GameSettingControl.prototype.AutocompleteOrder = undefined;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js (revision 25077)
@@ -1,269 +1,285 @@
-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)
{
super(...args);
this.clientItemFactory = new PlayerAssignmentItem.Client();
this.aiItemFactory = new PlayerAssignmentItem.AI();
this.unassignedItem = new PlayerAssignmentItem.Unassigned().createItem();
this.aiItems =
g_Settings.AIDescriptions.filter(ai => !ai.data.hidden).map(
this.aiItemFactory.createItem.bind(this.aiItemFactory));
this.values = undefined;
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));
}
setControl()
{
this.dropdown = Engine.GetGUIObjectByName("playerAssignment[" + this.playerIndex + "]");
this.label = Engine.GetGUIObjectByName("playerAssignmentText[" + this.playerIndex + "]");
}
onLoad(initData, hotloadData)
{
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)
{
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;
}
for (let guid in newAssignments)
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();
- }
-
- onMapChange(mapData)
- {
- let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
- this.fixedAI = mapPData && mapPData.AI || undefined;
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- 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);
+ this.setSelectedValue(this.assignedGUID);
+ return;
}
- }
-
- onGameAttributesBatchChange()
- {
- let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
- if (!pData)
+ 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()
{
Engine.ProfileStart("updatePlayerAssignmentsList");
this.values = prepareForDropdown([
...this.playerItems,
...this.aiItems,
this.unassignedItem
]);
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();
}
onSelectionChange(itemIdx)
{
this.values.Handler[itemIdx].onSelectionChange(
this.gameSettingsControl,
this.playerAssignmentsControl,
this.playerIndex,
this.values.Value[itemIdx]);
}
getAutocompleteEntries()
{
return this.values.Autocomplete;
}
};
PlayerSettingControls.PlayerAssignment.prototype.Tooltip =
translate("Select player.");
PlayerSettingControls.PlayerAssignment.prototype.AutocompleteOrder = 100;
PlayerSettingControls.PlayerAssignment.prototype.ConfigAssignPlayers =
"gui.gamesetup.assignplayers";
-class PlayerAssignmentItem
-{
-}
-
{
PlayerAssignmentItem.Client = class
{
createItem(guid)
{
return {
"Handler": this,
"Value": guid,
"Autocomplete": g_PlayerAssignments[guid].name,
"Caption": setStringTags(
g_PlayerAssignments[guid].name,
g_PlayerAssignments[guid].player == -1 ? this.ObserverTags : this.PlayerTags)
};
}
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)
{
return guid !== undefined && guid == value;
}
};
PlayerAssignmentItem.Client.prototype.PlayerTags =
{ "color": "white" };
PlayerAssignmentItem.Client.prototype.ObserverTags =
{ "color": "170 170 250" };
}
{
PlayerAssignmentItem.AI = class
{
createItem(ai)
{
let aiName = translate(ai.data.name);
return {
"Handler": this,
"Value": ai.id,
"Autocomplete": aiName,
"Caption": setStringTags(sprintf(this.Label, { "ai": aiName }), this.Tags)
};
}
onSelectionChange(gameSettingsControl, playerAssignmentsControl, playerIndex, value)
{
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();
}
isSelected(pData, guid, value)
{
return !guid && pData.AI && pData.AI == value;
}
};
PlayerAssignmentItem.AI.prototype.Label =
translate("AI: %(ai)s");
PlayerAssignmentItem.AI.prototype.Tags =
{ "color": "70 150 70" };
}
{
PlayerAssignmentItem.Unassigned = class
{
createItem()
{
return {
"Handler": this,
"Value": undefined,
"Autocomplete": this.Label,
"Caption": setStringTags(this.Label, this.Tags)
};
}
onSelectionChange(gameSettingsControl, playerAssignmentsControl, playerIndex)
{
playerAssignmentsControl.unassignClient(playerIndex + 1);
- g_GameAttributes.settings.PlayerData[playerIndex].AI = false;
- gameSettingsControl.updateGameAttributes();
+ g_GameSettings.playerAI.setAI(playerIndex, undefined);
+
gameSettingsControl.setNetworkGameAttributes();
}
isSelected(pData, guid, value)
{
return !guid && !pData.AI;
}
};
PlayerAssignmentItem.Unassigned.prototype.Label =
translate("Unassigned");
PlayerAssignmentItem.Unassigned.prototype.Tags =
{ "color": "140 140 140" };
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerFrame.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerFrame.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerFrame.js (revision 25077)
@@ -1,23 +1,26 @@
-PlayerSettingControls.PlayerFrame = class extends GameSettingControl
+PlayerSettingControls.PlayerFrame = class PlayerFrame extends GameSettingControl
{
constructor(...args)
{
super(...args);
this.playerFrame = Engine.GetGUIObjectByName("playerFrame[" + this.playerIndex + "]");
{
let size = this.playerFrame.size;
size.top = this.Height * this.playerIndex;
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;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Cheats.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Cheats.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Cheats.js (revision 25077)
@@ -1,43 +1,37 @@
/**
* 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();
}
};
GameSettingControls.Cheats.prototype.TitleCaption =
translate("Cheats");
GameSettingControls.Cheats.prototype.Tooltip =
translate("Toggle the usability of cheats.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Nomad.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Nomad.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Nomad.js (revision 25077)
@@ -1,64 +1,28 @@
-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();
}
};
GameSettingControls.Nomad.prototype.TitleCaption =
translate("Nomad");
GameSettingControls.Nomad.prototype.Tooltip =
translate("In Nomad mode, players start with only few units and have to find a suitable place to build their city. Ceasefire is recommended.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Spies.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Spies.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Spies.js (revision 25077)
@@ -1,46 +1,29 @@
-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");
- }
-
- onGameAttributesChange()
- {
- if (g_GameAttributes.settings.DisableSpies === undefined)
- {
- g_GameAttributes.settings.DisableSpies = false;
- this.gameSettingsControl.updateGameAttributes();
- }
+ g_GameSettings.disableSpies.watch(() => this.render(), ["enabled"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesBatchChange()
+ render()
{
- 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();
}
};
GameSettingControls.Spies.prototype.TitleCaption =
translate("Disable Spies");
GameSettingControls.Spies.prototype.Tooltip =
translate("Disable spies during the game.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/ReadyControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/ReadyControl.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/ReadyControl.js (revision 25077)
@@ -1,126 +1,126 @@
/**
* Ready system:
*
* The ready mechanism protects the players from being assigned to a match with settings they didn't explicitly agree with.
* It shall be technically possible to start a networked game until all participating players formally agree with the chosen settings.
*
* Therefore assume the readystate from the user interface rather than trusting the server whether the current player is ready.
* The server may set readiness to false but not to true.
*
* The ReadyControl class stores the ready state of the current player and fires an event if the agreed settings changed.
*/
class ReadyControl
{
constructor(netMessages, gameSettingsControl, startGameControl, playerAssignmentsControl)
{
this.startGameControl = startGameControl;
this.playerAssignmentsControl = playerAssignmentsControl;
this.resetReadyHandlers = new Set();
this.previousAssignments = {};
// This variable keeps track whether the local player is ready
// As part of cheat prevention, the server may set this to NotReady, but
// only the UI may set it to Ready or StayReady.
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));
}
registerResetReadyHandler(handler)
{
this.resetReadyHandlers.add(handler);
}
onClientJoin(newGUID, newAssignments)
{
if (newAssignments[newGUID].player != -1)
this.resetReady();
}
onClientLeave(guid)
{
if (g_PlayerAssignments[guid].player != -1)
this.resetReady();
}
onReadyMessage(message)
{
let playerAssignment = g_PlayerAssignments[message.guid];
if (playerAssignment)
{
playerAssignment.status = message.status;
this.playerAssignmentsControl.updatePlayerAssignments();
}
}
onPlayerAssignmentsChange()
{
// Don't let the host tell you that you're ready when you're not.
let playerAssignment = g_PlayerAssignments[Engine.GetPlayerGUID()];
if (playerAssignment && playerAssignment.status > this.readyState)
playerAssignment.status = this.readyState;
for (let guid in g_PlayerAssignments)
if (this.previousAssignments[guid] &&
this.previousAssignments[guid].player != g_PlayerAssignments[guid].player)
{
this.resetReady();
return;
}
}
- onGameAttributesBatchChange()
+ onSettingsChange()
{
this.resetReady();
}
setReady(ready, sendMessage)
{
this.readyState = ready;
if (sendMessage)
Engine.SendNetworkReady(ready);
// Update GUI objects instantly if relevant settingchange was detected
let playerAssignment = g_PlayerAssignments[Engine.GetPlayerGUID()];
if (playerAssignment)
{
playerAssignment.status = ready;
this.playerAssignmentsControl.updatePlayerAssignments();
}
}
resetReady()
{
// The gameStarted check is only necessary to allow the host to
// determine the Seed and random items after clicking start
if (!g_IsNetworked || this.startGameControl.gameStarted)
return;
for (let handler of this.resetReadyHandlers)
handler();
if (g_IsController)
{
Engine.ClearAllPlayerReady();
this.playerAssignmentsControl.updatePlayerAssignments();
}
else if (this.readyState != this.StayReady)
this.setReady(this.NotReady, false);
}
getLocalReadyState()
{
return this.readyState;
}
}
ReadyControl.prototype.NotReady = 0;
ReadyControl.prototype.Ready = 1;
ReadyControl.prototype.StayReady = 2;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIDescription.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIDescription.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIDescription.js (revision 25077)
@@ -1,37 +1,30 @@
class AIDescription
{
constructor(aiConfigPage, setupWindow)
{
this.playerIndex = undefined;
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();
- }
-
- onGameAttributesBatchChange()
- {
- this.updateSelectedValue();
+ this.render();
}
- updateSelectedValue()
+ render()
{
- 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;
}
}
AIDescription.prototype.NoAIDescription =
translate("AI will be disabled for this player.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AISelection.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AISelection.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AISelection.js (revision 25077)
@@ -1,91 +1,44 @@
AIGameSettingControls.AISelection = class extends AIGameSettingControlDropdown
{
constructor(...args)
{
super(...args);
- this.fixedAI = [];
+ g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
this.values = prepareForDropdown([
this.NoAI,
...g_Settings.AIDescriptions.map(AI => ({
"Title": AI.data.name,
"Id": AI.id
}))
]);
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 =
translate("AI Player");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/AIConfigButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/AIConfigButton.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/AIConfigButton.js (revision 25077)
@@ -1,32 +1,32 @@
-PlayerSettingControls.AIConfigButton = class extends GameSettingControl
+PlayerSettingControls.AIConfigButton = class AIConfigButton extends GameSettingControl
{
constructor(...args)
{
super(...args);
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()
{
let aiConfigPage = this.setupWindow.pages.AIConfigPage;
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;
}
};
PlayerSettingControls.AIConfigButton.prototype.Tooltip =
translate("Configure AI: %(description)s.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js (revision 25077)
@@ -1,78 +1,52 @@
-PlayerSettingControls.PlayerTeam = class extends GameSettingControlDropdown
+PlayerSettingControls.PlayerTeam = class PlayerTeam extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.values = prepareForDropdown([
{
"label": this.NoTeam,
"id": this.NoTeamId
},
...Array.from(
new Array(g_MaxTeams),
(v, i) => ({
"label": i + 1,
"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()
{
this.label = Engine.GetGUIObjectByName("playerTeamText[" + this.playerIndex + "]");
this.dropdown = Engine.GetGUIObjectByName("playerTeam[" + this.playerIndex + "]");
}
- onMapChange(mapData)
- {
- 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()
+ render()
{
- 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();
}
};
PlayerSettingControls.PlayerTeam.prototype.Tooltip =
translate("Select player's team.");
PlayerSettingControls.PlayerTeam.prototype.NoTeam =
translateWithContext("team", "None");
PlayerSettingControls.PlayerTeam.prototype.NoTeamId = -1;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Buttons/MapBrowser.js (revision 25077)
@@ -1,29 +1,29 @@
-GameSettingControls.MapBrowser = class extends GameSettingControlButton
+GameSettingControls.MapBrowser = class MapBrowser extends GameSettingControlButton
{
constructor(...args)
{
super(...args);
this.button.tooltip = colorizeHotkey(this.HotkeyTooltip, this.HotkeyConfig);
Engine.SetGlobalHotkey(this.HotkeyConfig, "Press", this.onPress.bind(this));
}
setControlHidden()
{
this.button.hidden = false;
}
onPress()
{
this.setupWindow.pages.MapBrowserPage.openPage();
}
};
GameSettingControls.MapBrowser.prototype.HotkeyConfig =
"gamesetup.mapbrowser.open";
GameSettingControls.MapBrowser.prototype.Caption =
translate("Browse Maps");
GameSettingControls.MapBrowser.prototype.HotkeyTooltip =
translate("Press %(hotkey)s to view the list of available maps.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/LockedTeams.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/LockedTeams.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/LockedTeams.js (revision 25077)
@@ -1,63 +1,40 @@
-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();
}
};
GameSettingControls.LockedTeams.prototype.TitleCaption =
translate("Teams Locked");
GameSettingControls.LockedTeams.prototype.Tooltip =
translate("Toggle locked teams.");
/**
* In multiplayer mode, players negotiate teams before starting the match and
* expect to play the match with these teams unless explicitly stated otherwise during the match settings.
* For singleplayermode, preserve the historic default of open diplomacies.
*/
GameSettingControls.LockedTeams.prototype.DefaultValue = Engine.HasNetClient();
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/RevealedMap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/RevealedMap.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/RevealedMap.js (revision 25077)
@@ -1,54 +1,30 @@
-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();
}
};
GameSettingControls.RevealedMap.prototype.TitleCaption =
// Translation: Make sure to differentiate between the revealed map and explored map settings!
translate("Revealed Map");
GameSettingControls.RevealedMap.prototype.Tooltip =
// Translation: Make sure to differentiate between the revealed map and explored map settings!
translate("Toggle revealed map (see everything).");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/PlayerAssignmentsControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/PlayerAssignmentsControl.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/PlayerAssignmentsControl.js (revision 25077)
@@ -1,168 +1,166 @@
/**
* This class provides a property independent interface to g_PlayerAssignment events and actions.
*/
class PlayerAssignmentsControl
{
constructor(setupWindow, netMessages, gameRegisterStanza)
{
this.clientJoinHandlers = new Set();
this.clientLeaveHandlers = new Set();
this.playerAssignmentsChangeHandlers = new Set();
this.gameRegisterStanza = gameRegisterStanza;
if (!g_IsNetworked)
{
let name = singleplayerName();
// Replace empty player name when entering a single-player match for the first time.
Engine.ConfigDB_CreateAndWriteValueToFile("user", this.ConfigNameSingleplayer, name, "config/user.cfg");
g_PlayerAssignments = {
"local": {
"name": name,
"player": -1
}
};
}
+ 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));
}
registerPlayerAssignmentsChangeHandler(handler)
{
this.playerAssignmentsChangeHandlers.add(handler);
}
unregisterPlayerAssignmentsChangeHandler(handler)
{
this.playerAssignmentsChangeHandlers.delete(handler);
}
registerClientJoinHandler(handler)
{
this.clientJoinHandlers.add(handler);
}
unregisterClientJoinHandler(handler)
{
this.clientJoinHandlers.delete(handler);
}
registerClientLeaveHandler(handler)
{
this.clientLeaveHandlers.add(handler);
}
unregisterClientLeaveHandler(handler)
{
this.clientLeaveHandlers.delete(handler);
}
onLoad(initData, hotloadData)
{
if (hotloadData)
{
g_PlayerAssignments = hotloadData.playerAssignments;
this.updatePlayerAssignments();
}
}
onGetHotloadData(object)
{
object.playerAssignments = g_PlayerAssignments;
}
/**
* To be called when g_PlayerAssignments is modified.
*/
updatePlayerAssignments()
{
Engine.ProfileStart("updatePlayerAssignments");
for (let handler of this.playerAssignmentsChangeHandlers)
handler();
Engine.ProfileStop();
}
/**
* Called whenever a client joins or leaves or any game setting is changed.
*/
onPlayerAssignmentMessage(message)
{
let newAssignments = message.newAssignments;
for (let guid in newAssignments)
if (!g_PlayerAssignments[guid])
for (let handler of this.clientJoinHandlers)
handler(guid, message.newAssignments);
for (let guid in g_PlayerAssignments)
if (!newAssignments[guid])
for (let handler of this.clientLeaveHandlers)
handler(guid);
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();
}
/**
* If both clients are assigned players, this will swap their assignments.
*/
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)
{
if (g_IsNetworked)
Engine.AssignNetworkPlayer(playerID, "");
else if (g_PlayerAssignments.local.player == playerID)
{
g_PlayerAssignments.local.player = -1;
this.updatePlayerAssignments();
}
}
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();
}
}
}
PlayerAssignmentsControl.prototype.ConfigNameSingleplayer =
"playername.singleplayer";
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIConfigPage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIConfigPage.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/AIConfigPage.js (revision 25077)
@@ -1,69 +1,67 @@
/**
* This class contains all controls modifying the AI settings of a player.
*/
class AIGameSettingControls
{
}
SetupWindowPages.AIConfigPage = class
{
constructor(setupWindow)
{
this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
this.playerIndex = undefined;
this.row = 0;
this.openPageHandlers = new Set();
this.AIGameSettingControls = {};
for (let name of this.AIGameSettingControlOrder)
this.AIGameSettingControls[name] =
new AIGameSettingControls[name](this, undefined, undefined, setupWindow);
this.aiDescription = new AIDescription(this, setupWindow);
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)
{
this.openPageHandlers.add(handler);
}
getRow()
{
return this.row++;
}
openPage(playerIndex)
{
this.playerIndex = playerIndex;
for (let handler of this.openPageHandlers)
handler(playerIndex);
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();
}
closePage()
{
this.aiConfigPage.hidden = true;
}
-}
+};
SetupWindowPages.AIConfigPage.prototype.AIGameSettingControlOrder = [
"AISelection",
"AIDifficulty",
"AIBehavior"
];
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AIDifficulty.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AIDifficulty.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AIDifficulty.js (revision 25077)
@@ -1,89 +1,32 @@
AIGameSettingControls.AIDifficulty = class extends AIGameSettingControlDropdown
{
constructor(...args)
{
super(...args);
- this.fixedAIDiff = [];
- this.defaultAIDiff = +Engine.ConfigDB_GetValue("user", this.ConfigDifficulty);
-
- 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();
- }
+ g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
}
- updateSelectedValue()
+ render()
{
- let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
- this.setHidden(!pData || !pData.AI);
+ this.dropdown.list = g_Settings.AIDifficulties.map(AI => AI.Title);
+ this.dropdown.list_data = g_Settings.AIDifficulties.map((AI, i) => i);
- 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();
}
};
AIGameSettingControls.AIDifficulty.prototype.ConfigDifficulty =
"gui.gamesetup.aidifficulty";
AIGameSettingControls.AIDifficulty.prototype.TitleCaption =
translate("AI Difficulty");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingsLayout.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingsLayout.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingsLayout.js (revision 25077)
@@ -1,53 +1,53 @@
/**
* This array determines the order in which the GUI controls are shown in the GameSettingTabs panel.
* The names correspond to property names of the GameSettingControls prototype.
*/
var g_GameSettingsLayout = [
{
"label": translateWithContext("Match settings tab name", "Map"),
"settings": [
"MapType",
"MapFilter",
"MapSelection",
"MapBrowser",
"MapSize",
"TeamPlacement",
"Landscape",
"Biome",
"SeaLevelRiseTime",
"Daytime",
"TriggerDifficulty",
"Nomad",
"Treasures",
"ExploredMap",
"RevealedMap"
]
},
{
"label": translateWithContext("Match settings tab name", "Player"),
"settings": [
"PlayerCount",
"WorldPopulation",
"PopulationCap",
"WorldPopulationCap",
"StartingResources",
"Spies",
"Cheats"
]
},
{
"label": translateWithContext("Match settings tab name", "Game Type"),
"settings": [
...g_VictoryConditions.map(victoryCondition => victoryCondition.Name),
"RelicCount",
"RelicDuration",
- "WonderDuration",
"RegicideGarrison",
+ "WonderDuration",
"GameSpeed",
"Ceasefire",
"LockedTeams",
"LastManStanding",
"Rating"
]
}
];
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerColor.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerColor.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerColor.js (revision 25077)
@@ -1,175 +1,47 @@
-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()
{
this.dropdown = Engine.GetGUIObjectByName("playerColor[" + this.playerIndex + "]");
this.playerBackgroundColor = Engine.GetGUIObjectByName("playerBackgroundColor[" + this.playerIndex + "]");
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 =
translate("Pick a color.");
PlayerSettingControls.PlayerColor.prototype.ColorIcon =
"■";
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PlayerSettingControlManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PlayerSettingControlManager.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PlayerSettingControlManager.js (revision 25077)
@@ -1,27 +1,27 @@
/**
* Each property of this class is a class that inherits GameSettingControl and is
* instantiated by the PlayerSettingControlManager.
*/
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
{
constructor(...args)
{
this.playerSettingControls = {};
for (let name in PlayerSettingControls)
this.playerSettingControls[name] = new PlayerSettingControls[name](undefined, undefined, ...args);
}
addAutocompleteEntries(autocomplete)
{
for (let name in this.playerSettingControls)
this.playerSettingControls[name].addAutocompleteEntries(name, autocomplete);
}
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/LastManStanding.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/LastManStanding.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/LastManStanding.js (revision 25077)
@@ -1,62 +1,29 @@
-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();
- }
- }
-
- onGameAttributesChange()
- {
- 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();
- }
+ super(...args);
+ g_GameSettings.lastManStanding.watch(() => this.render(), ["enabled", "available"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
}
- onGameAttributesBatchChange()
+ render()
{
- 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();
}
};
GameSettingControls.LastManStanding.prototype.TitleCaption =
translate("Last Man Standing");
GameSettingControls.LastManStanding.prototype.Tooltip =
translate("Toggle whether the last remaining player or the last remaining set of allies wins.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/RegicideGarrison.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/RegicideGarrison.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/RegicideGarrison.js (revision 25077)
@@ -1,68 +1,30 @@
-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();
}
};
GameSettingControls.RegicideGarrison.prototype.TitleCaption =
translate("Hero Garrison");
GameSettingControls.RegicideGarrison.prototype.Tooltip =
translate("Toggle whether heroes can be garrisoned.");
-
-GameSettingControls.RegicideGarrison.prototype.RegicideName =
- "regicide";
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/WorldPopulation.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/WorldPopulation.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/WorldPopulation.js (revision 25077)
@@ -1,52 +1,28 @@
-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");
- }
-
- onGameAttributesChange()
- {
- if (!g_GameAttributes.mapType)
- return;
-
- if (g_GameAttributes.settings.WorldPopulation !== undefined)
- return;
-
- g_GameAttributes.settings.WorldPopulation = false;
- this.gameSettingsControl.updateGameAttributes();
+ super(...args);
+ g_GameSettings.population.watch(() => this.render(), ["useWorldPop"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesBatchChange()
+ render()
{
- 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();
}
};
GameSettingControls.WorldPopulation.prototype.TitleCaption =
translate("World population");
GameSettingControls.WorldPopulation.prototype.Tooltip =
translate("When checked the Population Cap will be evenly distributed over all living players.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GuiData.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GuiData.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GuiData.js (revision 25077)
@@ -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;
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GuiData.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js (revision 25077)
@@ -1,139 +1,142 @@
/**
* If there is an XmppClient, this class informs the XPartaMuPP lobby bot that
* this match is being setup so that others can join.
* It informs of the lobby of some setting values and the participating clients.
*/
class GameRegisterStanza
{
constructor(initData, setupWindow, netMessages, gameSettingsControl, mapCache)
{
this.mapCache = mapCache;
this.serverName = initData.serverName;
this.hasPassword = initData.hasPassword;
this.mods = JSON.stringify(Engine.GetEngineInfo().mods);
this.timer = undefined;
// Only send a lobby update when its data changed
this.lastStanza = undefined;
// 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();
else
this.sendImmediately();
}
onGameStart()
{
if (!g_IsController || !Engine.HasXmppClient())
return;
this.sendImmediately();
let clients = this.formatClientsForStanza();
Engine.SendChangeStateGame(clients.connectedPlayers, clients.list);
}
onClosePage()
{
if (g_IsController && Engine.HasXmppClient())
Engine.SendUnregisterGame();
}
/**
* Send the relevant game settings to the lobby bot in a deferred manner.
*/
sendDelayed()
{
if (!g_IsController || !Engine.HasXmppClient())
return;
if (this.timer !== undefined)
clearTimeout(this.timer);
this.timer = setTimeout(this.sendImmediately.bind(this), this.Timeout);
}
/**
* Send the relevant game settings to the lobby bot immediately.
*/
sendImmediately()
{
if (!g_IsController || !Engine.HasXmppClient())
return;
Engine.ProfileStart("sendRegisterGameStanza");
if (this.timer !== undefined)
{
clearTimeout(this.timer);
this.timer = undefined;
}
let clients = this.formatClientsForStanza();
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 || ""
};
// Only send the stanza if one of these properties changed
if (this.lastStanza && Object.keys(stanza).every(prop => this.lastStanza[prop] == stanza[prop]))
return;
this.lastStanza = stanza;
Engine.SendRegisterGame(stanza);
Engine.ProfileStop();
}
/**
* 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()
{
let connectedPlayers = 0;
let playerData = [];
for (let guid in g_PlayerAssignments)
{
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";
playerData.push(pData);
}
return {
"list": playerDataToStringifiedTeamList(playerData),
"connectedPlayers": connectedPlayers
};
}
}
/**
* Send the current game settings to the lobby bot if the settings didn't change for this number of milliseconds.
*/
GameRegisterStanza.prototype.Timeout = 2000;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AIBehavior.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AIBehavior.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/AIConfigPage/Controls/AIBehavior.js (revision 25077)
@@ -1,95 +1,29 @@
AIGameSettingControls.AIBehavior = class extends AIGameSettingControlDropdown
{
constructor(...args)
{
super(...args);
- this.fixedAIBehavior = [];
- this.defaultBehavior = Engine.ConfigDB_GetValue("user", this.ConfigBehavior);
-
- this.dropdown.list = g_Settings.AIBehaviors.map(AIBehavior => AIBehavior.Title);
- this.dropdown.list_data = g_Settings.AIBehaviors.map(AIBehavior => AIBehavior.Name);
+ g_GameSettings.playerAI.watch(() => this.render(), ["values"]);
}
- onAssignPlayer(source, target)
+ render()
{
- 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();
- }
- }
+ this.dropdown.list = g_Settings.AIBehaviors.map(AIBehavior => AIBehavior.Title);
+ this.dropdown.list_data = g_Settings.AIBehaviors.map(AIBehavior => AIBehavior.Name);
- 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");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControlManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControlManager.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/GameSettingControlManager.js (revision 25077)
@@ -1,64 +1,64 @@
/**
* Each property of this class is a class that inherits GameSettingControl and is
* instantiated by the GameSettingControlManager.
*/
class GameSettingControls
{
}
/**
- * The GameSettingControlManager owns all classes that handle a logical property of g_GameAttributes.
+ * The GameSettingControlManager owns all GUI controls.
*/
class GameSettingControlManager
{
constructor(setupWindow)
{
this.setupWindow = setupWindow;
this.rows = {};
this.gameSettingControls = {};
let getCategory = name =>
g_GameSettingsLayout.findIndex(category => category.settings.indexOf(name) != -1);
for (let name in GameSettingControls)
this.gameSettingControls[name] =
new GameSettingControls[name](
this, getCategory(name), undefined, setupWindow);
for (let victoryCondition of g_VictoryConditions)
this.gameSettingControls[victoryCondition.Name] =
new VictoryConditionCheckbox(
victoryCondition, this, getCategory(victoryCondition.Name), undefined, setupWindow);
this.playerSettingControlManagers = Array.from(
new Array(g_MaxPlayers),
(value, playerIndex) =>
new PlayerSettingControlManager(playerIndex, setupWindow));
}
getNextRow(name)
{
if (this.rows[name] === undefined)
this.rows[name] = 0;
else
++this.rows[name];
return this.rows[name];
}
updateSettingVisibility()
{
for (let name in this.gameSettingControls)
this.gameSettingControls[name].updateVisibility();
}
addAutocompleteEntries(entries)
{
for (let name in this.gameSettingControls)
this.gameSettingControls[name].addAutocompleteEntries(name, entries);
for (let playerSettingControlManager of this.playerSettingControlManagers)
playerSettingControlManager.addAutocompleteEntries(entries);
}
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js (revision 25077)
@@ -1,142 +1,82 @@
-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()
{
this.label = Engine.GetGUIObjectByName("playerCivText[" + this.playerIndex + "]");
this.dropdown = Engine.GetGUIObjectByName("playerCiv[" + this.playerIndex + "]");
}
onHoverChange()
{
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()
{
let values = [];
for (let civ in g_CivData)
if (g_CivData[civ].SelectableInGameSetup)
values.push({
"name": g_CivData[civ].Name,
"autocomplete": g_CivData[civ].Name,
"tooltip": g_CivData[civ].History,
"civ": civ
});
values.sort(sortNameIgnoreCase);
values.unshift({
"name": setStringTags(this.RandomCivCaption, this.RandomItemTags),
"autocomplete": this.RandomCivCaption,
"tooltip": this.RandomCivTooltip,
"civ": this.RandomCivId
});
return values;
}
getAutocompleteEntries()
{
return this.values.autocomplete;
}
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 =
translate("Choose the civilization for this player.");
PlayerSettingControls.PlayerCiv.prototype.RandomCivCaption =
translateWithContext("civilization", "Random");
PlayerSettingControls.PlayerCiv.prototype.RandomCivId =
"random";
PlayerSettingControls.PlayerCiv.prototype.RandomCivTooltip =
translate("Picks one civilization at random when the game starts.");
PlayerSettingControls.PlayerCiv.prototype.AutocompleteOrder = 90;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerName.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerName.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/PerPlayer/PlayerName.js (revision 25077)
@@ -1,129 +1,55 @@
// 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()
{
this.guid = undefined;
for (let guid in g_PlayerAssignments)
if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
{
this.guid = guid;
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",
},
{
"color": "green",
},
{
"color": "150 150 250",
}
];
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/ExploredMap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/ExploredMap.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/ExploredMap.js (revision 25077)
@@ -1,59 +1,30 @@
-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();
- }
- }
-
- onGameAttributesChange()
- {
- 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();
- }
+ super(...args);
+ g_GameSettings.mapExploration.watch(() => this.render(), ["explored"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesBatchChange()
+ render()
{
- 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();
}
};
GameSettingControls.ExploredMap.prototype.TitleCaption =
// Translation: Make sure to differentiate between the revealed map and explored map settings!
translate("Explored Map");
GameSettingControls.ExploredMap.prototype.Tooltip =
// Translation: Make sure to differentiate between the revealed map and explored map settings!
translate("Toggle explored map (see initial map).");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Rating.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Rating.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Rating.js (revision 25077)
@@ -1,54 +1,30 @@
-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 =
translate("Rated Game");
GameSettingControls.Rating.prototype.Tooltip =
translate("Toggle if this game will be rated for the leaderboard.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Treasures.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Treasures.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Checkboxes/Treasures.js (revision 25077)
@@ -1,46 +1,28 @@
-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");
- }
-
- onGameAttributesChange()
- {
- if (g_GameAttributes.settings.DisableTreasures === undefined)
- {
- g_GameAttributes.settings.DisableTreasures = false;
- this.gameSettingsControl.updateGameAttributes();
- }
+ super(...args);
+ g_GameSettings.disableTreasures.watch(() => this.render(), ["enabled"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesBatchChange()
+ render()
{
- 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();
}
};
GameSettingControls.Treasures.prototype.TitleCaption =
translate("Disable Treasures");
GameSettingControls.Treasures.prototype.Tooltip =
translate("Do not add treasures to the map.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Daytime.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Daytime.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Daytime.js (revision 25077)
@@ -1,112 +1,68 @@
-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()
{
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()
{
return this.values && this.values.Name.slice(1);
}
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 =
translate("Daytime");
GameSettingControls.Daytime.prototype.Tooltip =
translate("Select whether the match takes place at daylight or night.");
GameSettingControls.Daytime.prototype.RandomTitle =
translateWithContext("daytime selection", "Random");
GameSettingControls.Daytime.prototype.RandomDescription =
translateWithContext("daytime selection", "Randomly pick a time of the day.");
GameSettingControls.Daytime.prototype.AutocompleteOrder = 0;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapSelection.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapSelection.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapSelection.js (revision 25077)
@@ -1,169 +1,116 @@
-GameSettingControls.MapSelection = class extends GameSettingControlDropdown
+GameSettingControls.MapSelection = class MapSelection extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
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,
"name": setStringTags(this.RandomMapCaption, this.RandomItemTags),
"description": this.RandomMapDescription
};
}
onHoverChange()
{
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);
}
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 =
translate("Select Map");
GameSettingControls.MapSelection.prototype.Tooltip =
translate("Select a map to play on.");
GameSettingControls.MapSelection.prototype.RandomMapId =
"random";
GameSettingControls.MapSelection.prototype.RandomMapCaption =
translateWithContext("map selection", "Random");
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"
-];
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PopulationCap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PopulationCap.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PopulationCap.js (revision 25077)
@@ -1,111 +1,73 @@
-GameSettingControls.PopulationCap = class extends GameSettingControlDropdown
+GameSettingControls.PopulationCap = class PopulationCap extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.perPlayer = false;
this.dropdown.list = g_PopulationCapacities.Title;
this.dropdown.list_data = g_PopulationCapacities.Population;
this.sprintfArgs = {};
- }
-
- onMapChange(mapData)
- {
- 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.label.caption = this.PerPlayerCaption;
+ g_GameSettings.population.watch(() => this.render(), ["useWorldPop", "cap", "perPlayer"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- if (g_GameAttributes.settings.WorldPopulation)
- {
- this.setHidden(true);
- g_GameAttributes.settings.PopulationCap = undefined;
- }
+ 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;
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()
{
let tooltip = this.Tooltip;
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;
this.sprintfArgs.popCap = popCap;
tooltip = setStringTags(sprintf(this.HoverTooltip, this.sprintfArgs), this.HoverTags);
}
}
this.dropdown.tooltip = tooltip;
}
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();
}
};
GameSettingControls.PopulationCap.prototype.TitleCaption =
translate("Population Cap");
GameSettingControls.PopulationCap.prototype.Tooltip =
translate("Select population limit.");
GameSettingControls.PopulationCap.prototype.PerPlayerCaption =
translateWithContext("population limit", "Per Player");
GameSettingControls.PopulationCap.prototype.HoverTooltip =
translate("Warning: There might be performance issues if all %(players)s players reach %(popCap)s population.");
GameSettingControls.PopulationCap.prototype.HoverTags = {
"color": "orange"
};
/**
* Total number of units that the engine can run with smoothly.
* It means a 4v4 with 150 population can still run nicely, but more than that might "lag".
*/
GameSettingControls.PopulationCap.prototype.PopulationCapacityRecommendation = 1200;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Biome.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Biome.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Biome.js (revision 25077)
@@ -1,142 +1,68 @@
-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();
- }
-
- let biomePreviewFile =
- basename(g_GameAttributes.map) + "_" +
- basename(g_GameAttributes.settings.Biome || "") + ".png";
-
- 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);
- }
+ "Title": setStringTags(this.RandomBiome, this.RandomItemTags),
+ "Id": "random"
+ },
+ ...g_GameSettings.biome.getAvailableBiomeData()
+ ]);
- filterBiome(available)
- {
- if (typeof available == "string")
- return biome => biome.Id.startsWith(available);
+ this.dropdown.list = values.Title;
+ this.dropdown.list_data = values.Id;
- 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 =
translate("Biome");
GameSettingControls.Biome.prototype.RandomBiomeId =
"random";
GameSettingControls.Biome.prototype.Tooltip =
translate("Select the flora and fauna.");
GameSettingControls.Biome.prototype.RandomBiome =
translateWithContext("biome", "Random");
GameSettingControls.Biome.prototype.RandomDescription =
translate("Pick a biome at random.");
GameSettingControls.Biome.prototype.AutocompleteOrder = 0;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapFilter.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapFilter.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapFilter.js (revision 25077)
@@ -1,94 +1,63 @@
-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()
{
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)
{
this.dropdown.list = values.Title;
this.dropdown.list_data = values.Name;
this.values = values;
}
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()
{
return this.values && this.values.Title;
}
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];
}
};
GameSettingControls.MapFilter.prototype.TitleCaption =
translate("Map Filter");
GameSettingControls.MapFilter.prototype.Tooltip =
translate("Select a map filter.");
GameSettingControls.MapFilter.prototype.AutocompleteOrder = 0;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PlayerCount.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PlayerCount.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/PlayerCount.js (revision 25077)
@@ -1,52 +1,36 @@
-GameSettingControls.PlayerCount = class extends GameSettingControlDropdown
+GameSettingControls.PlayerCount = class PlayerCount extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.values = Array.from(
new Array(g_MaxPlayers),
(v, i) => i + 1);
this.dropdown.list = this.values;
this.dropdown.list_data = this.values;
- }
-
- onMapChange(mapData)
- {
- 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");
+ g_GameSettings.playerCount.watch(() => this.render(), ["nbPlayers"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesBatchChange()
+ render()
{
- 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();
}
};
GameSettingControls.PlayerCount.prototype.TitleCaption =
translate("Number of Players");
GameSettingControls.PlayerCount.prototype.Tooltip =
translate("Select number of players.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Landscape.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Landscape.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/Landscape.js (revision 25077)
@@ -1,162 +1,83 @@
-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()
{
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;
+ items = items.concat(group.Items.map(this.translateItem).sort(sort));
+ }
- this.values = prepareForDropdown([
- ...randomItems,
- ...mapData.settings.Landscapes.land.map(this.translateItem).sort(sort),
- ...mapData.settings.Landscapes.naval.map(this.translateItem).sort(sort)
- ]);
+ this.values = prepareForDropdown(randomItems.concat(items));
- this.dropdown.list = this.values.Name;
- this.dropdown.list_data = this.values.Id;
- }
- else
- this.values = undefined;
+ this.dropdown.list = this.values.Name;
+ this.dropdown.list_data = this.values.Id;
- this.setHidden(!this.values);
+ this.setSelectedValue(g_GameSettings.landscape.value);
}
translateItem(item)
{
return {
"Id": item.Id,
"Name": translate(item.Name),
"Description": translate(item.Description),
"Preview": item.Preview
};
}
- 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)
return undefined;
let entries = [];
for (let i = 0; i < this.values.Id.length; ++i)
if (!this.values.Id[i].startsWith("random"))
entries.push(this.values.Name[i]);
return entries;
}
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 =
translate("Landscape");
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;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapType.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapType.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapType.js (revision 25077)
@@ -1,66 +1,56 @@
/**
* Maptype design:
* Scenario maps have fixed terrain and all settings predetermined.
* 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)
{
super(...args);
this.dropdown.list = g_MapTypes.Title;
this.dropdown.list_data = g_MapTypes.Name;
+
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onHoverChange()
+ onLoad()
{
- this.dropdown.tooltip = g_MapTypes.Description[this.dropdown.hovered] || this.Tooltip;
+ // 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]);
}
- onGameAttributesChange()
+ onHoverChange()
{
- if (g_MapTypes.Name.indexOf(g_GameAttributes.mapType || undefined) == -1)
- {
- g_GameAttributes.mapType = g_MapTypes.Name[g_MapTypes.Default];
- this.gameSettingsControl.updateGameAttributes();
- }
+ this.dropdown.tooltip = g_MapTypes.Description[this.dropdown.hovered] || this.Tooltip;
}
- onGameAttributesBatchChange()
+ render()
{
- this.setSelectedValue(g_GameAttributes.mapType);
+ this.setSelectedValue(g_GameSettings.map.type);
}
getAutocompleteEntries()
{
return g_MapTypes.Title;
}
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();
}
};
GameSettingControls.MapType.prototype.TitleCaption =
translate("Map Type");
GameSettingControls.MapType.prototype.Tooltip =
translate("Select a map type.");
GameSettingControls.MapType.prototype.AutocompleteOrder = 0;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/GameSpeed.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/GameSpeed.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/GameSpeed.js (revision 25077)
@@ -1,78 +1,52 @@
-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();
- }
-
- onGameAttributesChange()
- {
- this.update();
+ this.render();
}
- update()
+ render()
{
let allowFastForward = true;
for (let guid in g_PlayerAssignments)
if (g_PlayerAssignments[guid].player != -1)
{
allowFastForward = false;
break;
}
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();
- }
-
- if (this.values.Speed.indexOf(g_GameAttributes.gameSpeed || undefined) == -1)
- {
- g_GameAttributes.gameSpeed = this.values.Speed[this.values.Default];
- this.gameSettingsControl.updateGameAttributes();
+ this.dropdown.list = values.Title;
+ this.dropdown.list_data = values.Speed;
}
- }
- 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();
}
};
GameSettingControls.GameSpeed.prototype.TitleCaption =
translate("Game Speed");
GameSettingControls.GameSpeed.prototype.Tooltip =
translate("Select game speed.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapSize.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapSize.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/MapSize.js (revision 25077)
@@ -1,75 +1,44 @@
-GameSettingControls.MapSize = class extends GameSettingControlDropdown
+GameSettingControls.MapSize = class MapSize extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.dropdown.list = g_MapSizes.Name;
this.dropdown.list_data = g_MapSizes.Tiles;
+
+ g_GameSettings.mapSize.watch(() => this.render(), ["size", "available"]);
+ this.render();
}
onHoverChange()
{
this.dropdown.tooltip = g_MapSizes.Tooltip[this.dropdown.hovered] || this.Tooltip;
}
- onMapChange(mapData)
- {
- 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()
+ render()
{
- 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()
{
return g_MapSizes.Name;
}
onSelectionChange(itemIdx)
{
- g_GameAttributes.settings.Size = g_MapSizes.Tiles[itemIdx];
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.mapSize.setSize(g_MapSizes.Tiles[itemIdx]);
this.gameSettingsControl.setNetworkGameAttributes();
}
};
GameSettingControls.MapSize.prototype.TitleCaption =
translate("Map Size");
GameSettingControls.MapSize.prototype.Tooltip =
translate("Select map size. (Larger sizes may reduce performance.)");
GameSettingControls.MapSize.prototype.AutocompleteOrder = 0;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/StartingResources.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/StartingResources.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/StartingResources.js (revision 25077)
@@ -1,93 +1,61 @@
-GameSettingControls.StartingResources = class extends GameSettingControlDropdown
+GameSettingControls.StartingResources = class StartingResources extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
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()
{
let tooltip = this.Tooltip;
if (this.dropdown.hovered != -1)
{
this.sprintfArgs.resources = g_StartingResources.Resources[this.dropdown.hovered];
tooltip = sprintf(this.HoverTooltip, this.sprintfArgs);
}
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()
{
return g_StartingResources.Title;
}
onSelectionChange(itemIdx)
{
- g_GameAttributes.settings.StartingResources = g_StartingResources.Resources[itemIdx];
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.startingResources.setResources(g_StartingResources.Resources[itemIdx]);
this.gameSettingsControl.setNetworkGameAttributes();
}
};
GameSettingControls.StartingResources.prototype.TitleCaption =
translate("Starting Resources");
GameSettingControls.StartingResources.prototype.Tooltip =
translate("Select the game's starting resources.");
GameSettingControls.StartingResources.prototype.HoverTooltip =
translate("Initial amount of each resource: %(resources)s.");
GameSettingControls.StartingResources.prototype.PerPlayerCaption =
translateWithContext("starting resources", "Per Player");
GameSettingControls.StartingResources.prototype.AutocompleteOrder = 0;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/WorldPopulationCap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/WorldPopulationCap.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/WorldPopulationCap.js (revision 25077)
@@ -1,92 +1,62 @@
-GameSettingControls.WorldPopulationCap = class extends GameSettingControlDropdown
+GameSettingControls.WorldPopulationCap = class WorldPopulationCap extends GameSettingControlDropdown
{
constructor(...args)
{
super(...args);
this.dropdown.list = g_WorldPopulationCapacities.Title;
this.dropdown.list_data = g_WorldPopulationCapacities.Population;
this.sprintfArgs = {};
- }
-
- onMapChange(mapData)
- {
- let mapValue;
- if (mapData &&
- mapData.settings &&
- mapData.settings.WorldPopulationCap !== undefined)
- mapValue = mapData.settings.WorldPopulationCap;
-
- if (mapValue !== undefined && mapValue != g_GameAttributes.settings.WorldPopulationCap)
- {
- g_GameAttributes.settings.WorldPopulationCap = mapValue;
- this.gameSettingsControl.updateGameAttributes();
- }
-
- this.setEnabled(g_GameAttributes.mapType != "scenario");
- }
- onGameAttributesChange()
- {
- if (g_GameAttributes.settings.WorldPopulation)
- {
- this.setHidden(false);
- if (g_GameAttributes.settings.WorldPopulationCap === undefined)
- {
- g_GameAttributes.settings.WorldPopulationCap = g_WorldPopulationCapacities.Population[g_WorldPopulationCapacities.Default];
- this.gameSettingsControl.updateGameAttributes();
- }
- }
- else
- {
- this.setHidden(true);
- g_GameAttributes.settings.WorldPopulationCap = undefined;
- }
+ g_GameSettings.population.watch(() => this.render(), ["useWorldPop", "cap"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesBatchChange()
+ render()
{
- this.setSelectedValue(g_GameAttributes.settings.WorldPopulationCap);
+ this.setEnabled(g_GameSettings.map.type != "scenario");
+ this.setHidden(!g_GameSettings.population.useWorldPop);
+ this.setSelectedValue(g_GameSettings.population.cap);
}
onHoverChange()
{
let tooltip = this.Tooltip;
if (this.dropdown.hovered != -1)
{
let popCap = g_WorldPopulationCapacities.Population[this.dropdown.hovered];
if (popCap >= this.WorldPopulationCapacityRecommendation)
{
this.sprintfArgs.popCap = popCap;
tooltip = setStringTags(sprintf(this.HoverTooltip, this.sprintfArgs), this.HoverTags);
}
}
this.dropdown.tooltip = tooltip;
}
onSelectionChange(itemIdx)
{
- g_GameAttributes.settings.WorldPopulationCap = g_WorldPopulationCapacities.Population[itemIdx];
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.population.setPopCap(true, g_WorldPopulationCapacities.Population[itemIdx]);
this.gameSettingsControl.setNetworkGameAttributes();
}
};
GameSettingControls.WorldPopulationCap.prototype.TitleCaption =
translate("World Population Cap");
GameSettingControls.WorldPopulationCap.prototype.Tooltip =
translate("Select world population limit.");
GameSettingControls.WorldPopulationCap.prototype.HoverTooltip =
translate("Warning: There might be performance issues if %(popCap)s population is reached.");
GameSettingControls.WorldPopulationCap.prototype.HoverTags = {
"color": "orange"
};
/**
* Total number of units that the engine can run with smoothly.
*/
GameSettingControls.WorldPopulationCap.prototype.WorldPopulationCapacityRecommendation = 1200;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/SeaLevelRiseTime.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/SeaLevelRiseTime.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/SeaLevelRiseTime.js (revision 25077)
@@ -1,83 +1,45 @@
-GameSettingControls.SeaLevelRiseTime = class extends GameSettingControlSlider
+GameSettingControls.SeaLevelRiseTime = class SeaLevelRiseTime extends GameSettingControlSlider
{
constructor(...args)
{
super(...args);
this.values = undefined;
this.sprintfValue = {};
- }
-
- onMapChange(mapData)
- {
- this.values =
- mapData &&
- mapData.settings &&
- mapData.settings.SeaLevelRise || undefined;
-
- if (this.values)
- {
- this.slider.min_value = this.values.Min;
- this.slider.max_value = this.values.Max;
- }
- this.setHidden(!this.values);
- this.setEnabled(g_GameAttributes.mapType != "scenario");
+ g_GameSettings.seaLevelRise.watch(() => this.render(), ["value"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- if (this.values)
- {
- if (g_GameAttributes.settings.SeaLevelRiseTime === undefined)
- {
- g_GameAttributes.settings.SeaLevelRiseTime = this.values.Default;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
- else if (g_GameAttributes.settings.SeaLevelRiseTime !== undefined)
- {
- delete g_GameAttributes.settings.SeaLevelRiseTime;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
+ this.setHidden(g_GameSettings.seaLevelRise.value === undefined);
+ this.setEnabled(g_GameSettings.map.type != "scenario");
- onGameAttributesBatchChange()
- {
- if (!this.values)
- return;
-
- let value = Math.round(g_GameAttributes.settings.SeaLevelRiseTime);
+ let value = g_GameSettings.seaLevelRise.value;
this.sprintfValue.minutes = value;
this.setSelectedValue(
- g_GameAttributes.settings.SeaLevelRiseTime,
- sprintf(this.SeaLevelRiseTimeCaption(value), this.sprintfValue));
+ value, sprintf(this.SeaLevelRiseTimeCaption(value), this.sprintfValue));
}
onValueChange(value)
{
- g_GameAttributes.settings.SeaLevelRiseTime = value;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.seaLevelRise.setValue(value);
this.gameSettingsControl.setNetworkGameAttributes();
}
-
- onGameAttributesFinalize()
- {
- if (this.values)
- g_GameAttributes.settings.SeaLevelRiseTime = Math.round(g_GameAttributes.settings.SeaLevelRiseTime);
- }
};
GameSettingControls.SeaLevelRiseTime.prototype.TitleCaption =
translate("Sea Level Rise Time");
GameSettingControls.SeaLevelRiseTime.prototype.Tooltip =
translate("Set the time when the water will start to rise.");
GameSettingControls.SeaLevelRiseTime.prototype.SeaLevelRiseTimeCaption =
minutes => translatePluralWithContext("sea level rise time", "%(minutes)s minute", "%(minutes)s minutes", minutes);
GameSettingControls.SeaLevelRiseTime.prototype.MinValue = 0;
GameSettingControls.SeaLevelRiseTime.prototype.MaxValue = 60;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/ResetTeamsButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/ResetTeamsButton.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/ResetTeamsButton.js (revision 25077)
@@ -1,35 +1,29 @@
class ResetTeamsButton
{
constructor(setupWindow)
{
this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
- this.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
this.teamResetButton = Engine.GetGUIObjectByName("teamResetButton");
this.teamResetButton.tooltip = this.Tooltip;
this.teamResetButton.onPress = this.onPress.bind(this);
+
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
}
- onGameAttributesBatchChange()
+ render()
{
- if (!g_GameAttributes.mapType)
- return;
-
- this.teamResetButton.hidden = g_GameAttributes.mapType == "scenario" || !g_IsController;
+ this.teamResetButton.hidden = g_GameSettings.map.type == "scenario" || !g_IsController;
}
onPress()
{
- if (!g_GameAttributes.settings || !g_GameAttributes.settings.PlayerData)
- return;
-
- for (let pData of g_GameAttributes.settings.PlayerData)
- pData.Team = -1;
+ for (let i = 0; i < g_GameSettings.playerCount.nbPlayers; ++i)
+ g_GameSettings.playerTeam.setValue(i, -1);
- this.gameSettingsControl.updateGameAttributes();
this.gameSettingsControl.setNetworkGameAttributes();
}
}
ResetTeamsButton.prototype.Tooltip =
translate("Reset all teams to the default.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingWarning.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingWarning.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingWarning.js (revision 25077)
@@ -1,41 +1,43 @@
class GameSettingWarning
{
constructor(setupWindow, cancelButton)
{
if (!g_IsNetworked)
return;
this.gameSettingWarning = Engine.GetGUIObjectByName("gameSettingWarning");
- setupWindow.controls.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
cancelButton.registerCancelButtonResizeHandler(this.onCancelButtonResize.bind(this));
+
+ g_GameSettings.cheats.watch(() => this.onSettingsChange(), ["enabled"]);
+ g_GameSettings.rating.watch(() => this.onSettingsChange(), ["enabled"]);
}
- onGameAttributesBatchChange()
+ onSettingsChange()
{
let caption =
- g_GameAttributes.settings.CheatsEnabled ?
+ g_GameSettings.cheats.enabled ?
this.CheatsEnabled :
- g_GameAttributes.settings.RatingEnabled ?
- this.RatingEnabled :
- "";
+ g_GameSettings.rating.enabled ?
+ this.RatingEnabled :
+ "";
this.gameSettingWarning.caption = caption;
this.gameSettingWarning.hidden = !caption;
}
onCancelButtonResize(cancelButton)
{
let size = this.gameSettingWarning.size;
size.right = cancelButton.size.left - this.Margin;
this.gameSettingWarning.size = size;
}
}
GameSettingWarning.prototype.Margin = 10;
GameSettingWarning.prototype.CheatsEnabled =
translate("Cheats enabled.");
GameSettingWarning.prototype.RatingEnabled =
translate("Rated game.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/SetupWindow.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/SetupWindow.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/SetupWindow.js (revision 25077)
@@ -1,113 +1,113 @@
/**
* This class stores the GameSetupPage and every subpage that is shown in the game setup.
*/
class SetupWindowPages
{
}
/**
* The SetupWindow is the root class owning all other class instances.
* The class shall be ineligible to perform any GUI object logic and shall defer that task to owned classes.
*/
class SetupWindow
{
constructor(initData, hotloadData)
{
if (!g_Settings)
return;
Engine.ProfileStart("SetupWindow");
this.loadHandlers = new Set();
this.closePageHandlers = new Set();
this.getHotloadDataHandlers = new Set();
+
+ let mapCache = new MapCache();
+ g_GameSettings = new GameSettings().init(mapCache);
+
let netMessages = new NetMessages(this);
let startGameControl = new StartGameControl(netMessages);
- let mapCache = new MapCache();
let mapFilters = new MapFilters(mapCache);
let gameSettingsControl = new GameSettingsControl(this, netMessages, startGameControl, mapCache);
- let gameRegisterStanza = Engine.HasXmppClient() &&
+ let gameRegisterStanza = Engine.HasXmppClient() &&
new GameRegisterStanza(initData, this, netMessages, gameSettingsControl, mapCache);
let playerAssignmentsControl = new PlayerAssignmentsControl(this, netMessages, gameRegisterStanza);
let readyControl = new ReadyControl(netMessages, gameSettingsControl, startGameControl, playerAssignmentsControl);
// These class instances control central data and do not manage any GUI Object.
this.controls = {
"gameSettingsControl": gameSettingsControl,
"playerAssignmentsControl": playerAssignmentsControl,
"mapCache": mapCache,
"mapFilters": mapFilters,
"readyControl": readyControl,
"startGameControl": startGameControl,
"netMessages": netMessages,
- "gameRegisterStanza": gameRegisterStanza
+ "gameRegisterStanza": gameRegisterStanza
};
// These are the pages within the setup window that may use the controls defined above
this.pages = {};
for (let name in SetupWindowPages)
this.pages[name] = new SetupWindowPages[name](this);
setTimeout(displayGamestateNotifications, 1000);
Engine.GetGUIObjectByName("setupWindow").onTick = updateTimers;
// This event is triggered after all classes have been instantiated and subscribed to each others events
for (let handler of this.loadHandlers)
handler(initData, hotloadData);
Engine.ProfileStop();
-
- if (gameSettingsControl.autostart)
- startGameControl.launchGame();
}
registerLoadHandler(handler)
{
this.loadHandlers.add(handler);
}
unregisterLoadHandler(handler)
{
this.loadHandlers.delete(handler);
}
registerClosePageHandler(handler)
{
this.closePageHandlers.add(handler);
}
unregisterClosePageHandler(handler)
{
this.closePageHandlers.delete(handler);
}
registerGetHotloadDataHandler(handler)
{
this.getHotloadDataHandlers.add(handler);
}
unregisterGetHotloadDataHandler(handler)
{
this.getHotloadDataHandlers.delete(handler);
}
getHotloadData()
{
let object = {};
for (let handler of this.getHotloadDataHandlers)
handler(object);
return object;
}
closePage()
{
for (let handler of this.closePageHandlers)
handler();
if (Engine.HasXmppClient())
Engine.SwitchGuiPage("page_lobby.xml", { "dialog": false });
else
Engine.SwitchGuiPage("page_pregame.xml");
}
}
Index: ps/trunk/binaries/data/mods/public/gui/maps/MapCache.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/MapCache.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/MapCache.js (revision 25077)
@@ -1,100 +1,102 @@
/**
* This class obtains, caches, and provides the game settings from map XML and JSON files.
*/
class MapCache
{
constructor()
{
this.cache = {};
}
checkIfExists(mapType, mapPath)
{
if (!mapPath || mapPath == "random")
return true;
return g_Settings.MapTypes.find(type => type.Name == mapType).CheckIfExists(mapPath);
}
getMapData(mapType, mapPath)
{
if (!mapPath || mapPath == "random")
return undefined;
-
if (!this.cache[mapPath])
{
let mapData = g_Settings.MapTypes.find(type => type.Name == mapType).GetData(mapPath);
// Remove gaia, TODO: Maps should be consistent
if (mapData &&
mapData.settings &&
mapData.settings.PlayerData &&
mapData.settings.PlayerData.length &&
!mapData.settings.PlayerData[0])
{
mapData.settings.PlayerData.shift();
}
this.cache[mapPath] = mapData;
}
return this.cache[mapPath];
}
/**
* Doesn't translate, so that lobby page viewers can do that locally.
* The result is to be used with translateMapName.
*/
getTranslatableMapName(mapType, mapPath)
{
if (mapPath == "random")
return "random";
let mapData = this.getMapData(mapType, mapPath);
return mapData && mapData.settings && mapData.settings.Name || undefined;
}
translateMapName(mapName)
{
return mapName == "random" ?
translateWithContext("map selection", "Random") :
mapName ? translate(mapName) : "";
}
getTranslatedMapDescription(mapType, mapPath)
{
if (mapPath == "random")
return translate("A randomly selected map.");
let mapData = this.getMapData(mapType, mapPath);
return mapData && mapData.settings && translate(mapData.settings.Description) || "";
}
- getMapPreview(mapType, mapPath, gameAttributes = undefined)
+ previewExists(filename)
{
- let filename = gameAttributes && gameAttributes.settings && gameAttributes.settings.Preview || undefined;
+ return Engine.TextureExists(this.TexturesPath + this.PreviewsPath + filename);
+ }
+ getMapPreview(mapType, mapPath, filename = undefined)
+ {
if (!filename)
{
let mapData = this.getMapData(mapType, mapPath);
filename = mapData && mapData.settings && mapData.settings.Preview || this.DefaultPreview;
}
return "cropped:" + this.PreviewWidth + "," + this.PreviewHeight + ":" + this.PreviewsPath + filename;
}
}
MapCache.prototype.TexturesPath =
"art/textures/ui/";
MapCache.prototype.PreviewsPath =
"session/icons/mappreview/";
MapCache.prototype.DefaultPreview =
"nopreview.png";
MapCache.prototype.PreviewWidth =
400 / 512;
MapCache.prototype.PreviewHeight =
300 / 512;
Index: ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/controls/MapBrowserControls.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/controls/MapBrowserControls.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/controls/MapBrowserControls.js (revision 25077)
@@ -1,58 +1,57 @@
class MapBrowserPageControls
{
constructor(mapBrowserPage, gridBrowser)
{
for (let name in this)
this[name] = new this[name](mapBrowserPage, gridBrowser);
this.mapBrowserPage = mapBrowserPage;
this.gridBrowser = gridBrowser;
this.setupButtons();
}
setupButtons()
{
this.pickRandom = Engine.GetGUIObjectByName("mapBrowserPagePickRandom");
if (!g_IsController)
this.pickRandom.hidden = true;
this.pickRandom.onPress = () => {
let index = randIntInclusive(0, this.gridBrowser.itemCount - 1);
this.gridBrowser.setSelectedIndex(index);
this.gridBrowser.goToPageOfSelected();
};
this.select = Engine.GetGUIObjectByName("mapBrowserPageSelect");
this.select.onPress = () => this.onSelect();
this.close = Engine.GetGUIObjectByName("mapBrowserPageClose");
if (g_SetupWindow)
this.close.tooltip = colorizeHotkey(
translate("%(hotkey)s: Close map browser and discard the selection."), "cancel");
else
{
this.close.caption = translate("Close");
this.close.tooltip = colorizeHotkey(
translate("%(hotkey)s: Close map browser."), "cancel");
}
this.close.onPress = () => this.mapBrowserPage.closePage();
this.select.hidden = !g_IsController;
if (!g_IsController)
this.close.size = this.select.size;
this.gridBrowser.registerSelectionChangeHandler(() => this.onSelectionChange());
}
onSelectionChange()
{
this.select.enabled = this.gridBrowser.selected != -1;
}
onSelect()
{
- this.gridBrowser.submitMapSelection();
- this.mapBrowserPage.closePage();
+ this.mapBrowserPage.submitMapSelection();
}
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/TriggerDifficulty.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/TriggerDifficulty.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/TriggerDifficulty.js (revision 25077)
@@ -1,84 +1,44 @@
-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()
{
this.dropdown.tooltip =
this.values && this.values.Tooltip[this.dropdown.hovered] ||
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();
}
};
GameSettingControls.TriggerDifficulty.prototype.TitleCaption =
translate("Difficulty");
GameSettingControls.TriggerDifficulty.prototype.Tooltip =
translate("Select the difficulty of this scenario.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicDuration.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicDuration.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicDuration.js (revision 25077)
@@ -1,105 +1,54 @@
-GameSettingControls.RelicDuration = class extends GameSettingControlSlider
+GameSettingControls.RelicDuration = class RelicDuration extends GameSettingControlSlider
{
constructor(...args)
{
super(...args);
this.sprintfValue = {};
this.available = false;
- }
-
- onMapChange(mapData)
- {
- this.setEnabled(g_GameAttributes.mapType != "scenario");
-
- let mapValue;
- if (mapData &&
- mapData.settings &&
- mapData.settings.VictoryConditions &&
- mapData.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1 &&
- mapData.settings.RelicDuration !== undefined)
- mapValue = mapData.settings.RelicDuration;
-
- if (mapValue === undefined || mapValue == g_GameAttributes.settings.RelicDuration)
- return;
-
- if (!g_GameAttributes.settings.VictoryConditions)
- g_GameAttributes.settings.VictoryConditions = [];
- if (g_GameAttributes.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) == -1)
- g_GameAttributes.settings.VictoryConditions.push(this.NameCaptureTheRelic);
-
- g_GameAttributes.settings.RelicDuration = mapValue;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.relic.watch(() => this.render(), ["duration", "available"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- if (!g_GameAttributes.settings.VictoryConditions)
- return;
-
- this.available = g_GameAttributes.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1;
+ this.setHidden(!g_GameSettings.relic.available);
+ this.setEnabled(g_GameSettings.map.type != "scenario");
- if (this.available)
+ if (g_GameSettings.relic.available)
{
- if (g_GameAttributes.settings.RelicDuration === undefined)
- {
- g_GameAttributes.settings.RelicDuration = this.DefaultValue;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
- else if (g_GameAttributes.settings.RelicDuration !== undefined)
- {
- delete g_GameAttributes.settings.RelicDuration;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
-
- onGameAttributesBatchChange()
- {
- this.setHidden(!this.available);
-
- if (this.available)
- {
- let value = Math.round(g_GameAttributes.settings.RelicDuration);
+ let value = g_GameSettings.relic.duration;
this.sprintfValue.min = value;
this.setSelectedValue(
- g_GameAttributes.settings.RelicDuration,
+ g_GameSettings.relic.duration,
value == 0 ? this.InstantVictory : sprintf(this.CaptionVictoryTime(value), this.sprintfValue));
}
}
onValueChange(value)
{
- g_GameAttributes.settings.RelicDuration = value;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.relic.setDuration(value);
this.gameSettingsControl.setNetworkGameAttributes();
}
-
- onGameAttributesFinalize()
- {
- if (this.available)
- g_GameAttributes.settings.RelicDuration = Math.round(g_GameAttributes.settings.RelicDuration);
- }
};
GameSettingControls.RelicDuration.prototype.TitleCaption =
translate("Relic Duration");
GameSettingControls.RelicDuration.prototype.Tooltip =
translate("Minutes until the player has achieved Relic Victory.");
GameSettingControls.RelicDuration.prototype.NameCaptureTheRelic =
"capture_the_relic";
GameSettingControls.RelicDuration.prototype.CaptionVictoryTime =
min => translatePluralWithContext("victory duration", "%(min)s minute", "%(min)s minutes", min);
GameSettingControls.RelicDuration.prototype.InstantVictory =
translateWithContext("victory duration", "Immediate Victory.");
GameSettingControls.RelicDuration.prototype.MinValue = 0;
GameSettingControls.RelicDuration.prototype.MaxValue = 60;
-
-GameSettingControls.RelicDuration.prototype.DefaultValue = 20;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/ResetCivsButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/ResetCivsButton.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/ResetCivsButton.js (revision 25077)
@@ -1,36 +1,29 @@
class ResetCivsButton
{
constructor(setupWindow)
{
this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
- this.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
this.civResetButton = Engine.GetGUIObjectByName("civResetButton");
this.civResetButton.tooltip = this.Tooltip;
this.civResetButton.onPress = this.onPress.bind(this);
+
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
}
- onGameAttributesBatchChange()
+ render()
{
- if (g_GameAttributes.mapType)
- this.civResetButton.hidden = g_GameAttributes.mapType == "scenario" || !g_IsController;
+ this.civResetButton.hidden = g_GameSettings.map.type == "scenario" || !g_IsController;
}
onPress()
{
- if (!g_GameAttributes.settings || !g_GameAttributes.settings.PlayerData)
- return;
-
- for (let pData of g_GameAttributes.settings.PlayerData)
- pData.Civ = this.RandomCivId;
+ for (let i = 0; i < g_GameSettings.playerCount.nbPlayers; ++i)
+ g_GameSettings.playerCiv.setValue(i, "random");
- this.gameSettingsControl.updateGameAttributes();
this.gameSettingsControl.setNetworkGameAttributes();
}
}
ResetCivsButton.prototype.Tooltip =
translate("Reset any civilizations that have been selected to the default (random).");
-
-ResetCivsButton.prototype.RandomCivId =
- "random";
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameDescription.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameDescription.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameDescription.js (revision 25077)
@@ -1,27 +1,56 @@
class GameDescription
{
constructor(setupWindow, gameSettingTabs)
{
this.mapCache = setupWindow.controls.mapCache;
this.gameDescriptionFrame = Engine.GetGUIObjectByName("gameDescriptionFrame");
this.gameDescription = Engine.GetGUIObjectByName("gameDescription");
- setupWindow.controls.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
gameSettingTabs.registerTabsResizeHandler(this.onTabsResize.bind(this));
+ this.registerWatchers();
+ this.updateGameDescription();
+ }
+
+ registerWatchers()
+ {
+ let update = () => this.updateGameDescription();
+ g_GameSettings.biome.watch(update, ["biome"]);
+ g_GameSettings.ceasefire.watch(update, ["value"]);
+ g_GameSettings.cheats.watch(update, ["enabled"]);
+ g_GameSettings.disableTreasures.watch(update, ["enabled"]);
+ g_GameSettings.lastManStanding.watch(update, ["enabled"]);
+ g_GameSettings.lockedTeams.watch(update, ["enabled"]);
+ g_GameSettings.map.watch(update, ["map", "type"]);
+ g_GameSettings.mapExploration.watch(update, ["explored"]);
+ g_GameSettings.mapExploration.watch(update, ["revealed"]);
+ g_GameSettings.nomad.watch(update, ["enabled"]);
+ g_GameSettings.population.watch(update, ["perPlayer", "cap", "useWorldPop"]);
+ g_GameSettings.rating.watch(update, ["enabled"]);
+ g_GameSettings.regicideGarrison.watch(update, ["enabled"]);
+ g_GameSettings.relic.watch(update, ["count", "duration"]);
+ g_GameSettings.startingResources.watch(update, ["perPlayer", "resources"]);
+ g_GameSettings.triggerDifficulty.watch(update, ["value"]);
+ g_GameSettings.victoryConditions.watch(update, ["active"]);
+ g_GameSettings.wonder.watch(update, ["duration"]);
}
onTabsResize(settingsTabButtonsFrame)
{
let size = this.gameDescriptionFrame.size;
size.top = settingsTabButtonsFrame.size.bottom + this.Margin;
this.gameDescriptionFrame.size = size;
}
- onGameAttributesBatchChange()
+ updateGameDescription()
{
- this.gameDescription.caption = getGameDescription(this.mapCache);
+ if (this.timer)
+ clearTimeout(this.timer);
+ // Update the description on the next GUI tick.
+ // (multiple settings can change at once)
+ let updateCaption = () => { this.gameDescription.caption = getGameDescription(g_GameSettings.toInitAttributes(), this.mapCache); };
+ this.timer = setTimeout(updateCaption, 0);
}
}
GameDescription.prototype.Margin = 3;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/MapBrowserPage/MapBrowserPage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/MapBrowserPage/MapBrowserPage.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/MapBrowserPage/MapBrowserPage.js (revision 25077)
@@ -1,22 +1,48 @@
SetupWindowPages.MapBrowserPage = class extends MapBrowser
{
constructor(setupWindow)
{
super(setupWindow.controls.mapCache, setupWindow.controls.mapFilters, setupWindow);
this.mapBrowserPage.hidden = true;
+
+ this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
+ }
+
+ onSubmitMapSelection(map, type, filter)
+ {
+ if (!g_IsController)
+ return;
+
+ if (type)
+ g_GameSettings.map.setType(type);
+
+ if (filter)
+ this.gameSettingsControl.guiData.mapFilter.filter = filter;
+
+ if (map)
+ g_GameSettings.map.selectMap(map);
+
+ this.gameSettingsControl.setNetworkGameAttributes();
}
openPage()
{
super.openPage();
+ this.controls.MapFiltering.select(
+ this.gameSettingsControl.guiData.mapFilter.filter,
+ g_GameSettings.map.type || g_MapTypes.Name[g_MapTypes.Default]
+ );
+ if (g_GameSettings.map.map)
+ this.gridBrowser.select(g_GameSettings.map.map);
+
this.mapBrowserPage.hidden = false;
}
closePage()
{
super.closePage();
this.mapBrowserPage.hidden = true;
}
};
Index: ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameDetails.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameDetails.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameDetails.js (revision 25077)
@@ -1,49 +1,52 @@
/**
* Needed for formatPlayerInfo to show the player civs in the details.
*/
const g_CivData = loadCivData(false, false);
/**
* This class is responsible for showing the map preview, description and other details
* of the currently selected savegame, or showing a placeholder if no savegame is selected.
*/
class SavegameDetails
{
constructor()
{
this.mapCache = new MapCache();
this.onSelectionChange();
}
onSelectionChange(gameID, metadata, label)
{
Engine.GetGUIObjectByName("invalidGame").hidden = !!metadata;
Engine.GetGUIObjectByName("validGame").hidden = !metadata;
if (!metadata)
return;
Engine.GetGUIObjectByName("savedMapName").caption =
this.mapCache.translateMapName(
this.mapCache.getTranslatableMapName(metadata.initAttributes.mapType, metadata.initAttributes.map));
Engine.GetGUIObjectByName("savedInfoPreview").sprite =
- this.mapCache.getMapPreview(metadata.initAttributes.mapType, metadata.initAttributes.map, metadata.initAttributes);
+ this.mapCache.getMapPreview(
+ metadata.initAttributes.mapType,
+ metadata.initAttributes.map,
+ metadata.initAttributes?.mapPreview);
Engine.GetGUIObjectByName("savedPlayers").caption = metadata.initAttributes.settings.PlayerData.length - 1;
Engine.GetGUIObjectByName("savedPlayedTime").caption = timeToString(metadata.gui.timeElapsed ? metadata.gui.timeElapsed : 0);
Engine.GetGUIObjectByName("savedMapType").caption = translateMapType(metadata.initAttributes.mapType);
Engine.GetGUIObjectByName("savedMapSize").caption = translateMapSize(metadata.initAttributes.settings.Size || -1);
Engine.GetGUIObjectByName("savedVictory").caption = metadata.initAttributes.settings.VictoryConditions.map(victoryConditionName => translateVictoryCondition(victoryConditionName)).join(translate(", "));
let caption = sprintf(translate("Mods: %(mods)s"), { "mods": modsToString(metadata.mods) });
if (!hasSameMods(metadata.mods, Engine.GetEngineInfo().mods))
caption = coloredText(caption, "orange");
Engine.GetGUIObjectByName("savedMods").caption = caption;
Engine.GetGUIObjectByName("savedPlayersNames").caption = formatPlayerInfo(
metadata.initAttributes.settings.PlayerData,
metadata.gui.states);
}
}
Index: ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowserPage.js (revision 25077)
@@ -1,20 +1,17 @@
/**
* TODO: better global state handling in the GUI.
- * In particular a bunch of those shadow gamesetup stuff.
+ * In particular a bunch of those shadow gamesetup/gamesettings stuff.
*/
const g_IsController = false;
-const g_GameAttributes = {
- "mapType": "skirmish",
- "mapFilter": "default",
-};
const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes);
var g_SetupWindow;
function init()
{
let cache = new MapCache();
let filters = new MapFilters(cache);
let browser = new MapBrowser(cache, filters);
browser.registerClosePageHandler(() => Engine.PopGuiPage());
browser.openPage();
+ browser.controls.MapFiltering.select("default", "skirmish");
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/TeamPlacement.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/TeamPlacement.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Dropdowns/TeamPlacement.js (revision 25077)
@@ -1,125 +1,64 @@
-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()
{
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);
-
- let patterns = [randomItem];
-
- 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)
- });
-
- this.values = prepareForDropdown(patterns);
-
- this.dropdown.list = this.values.Name;
- this.dropdown.list_data = this.values.Id;
- }
- else
- this.values = undefined;
- }
+ this.setHidden(!g_GameSettings.teamPlacement.value);
+ if (!g_GameSettings.teamPlacement.value)
+ return;
- 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();
- }
- }
+ let randomItem = clone(this.RandomItem);
+ randomItem.Name = setStringTags(randomItem.Name, this.RandomItemTags);
- onGameAttributesBatchChange()
- {
- this.setHidden(!this.values);
+ let patterns = [randomItem];
+
+ for (let pattern of g_GameSettings.teamPlacement.available)
+ patterns.push(g_GameSettings.teamPlacement.StartingPositions
+ .find(pObj => pObj.Id == pattern));
+
+ this.values = prepareForDropdown(patterns);
- if (this.values)
- this.setSelectedValue(g_GameAttributes.settings.TeamPlacement);
+ this.dropdown.list = this.values.Name;
+ this.dropdown.list_data = this.values.Id;
+
+ this.setSelectedValue(g_GameSettings.teamPlacement.value);
}
getAutocompleteEntries()
{
return this.values && this.values.Name.slice(1);
}
onSelectionChange(itemIdx)
{
- g_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 =
translate("Team Placement");
GameSettingControls.TeamPlacement.prototype.Tooltip =
translate("Select one of the starting position patterns of this map.");
GameSettingControls.TeamPlacement.prototype.RandomItem = {
"Id": "random",
"Name": translateWithContext("team placement", "Random"),
"Description": translateWithContext("team placement", "Select a random team placement pattern when starting the game.")
};
-GameSettingControls.TeamPlacement.prototype.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;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicCount.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicCount.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/RelicCount.js (revision 25077)
@@ -1,102 +1,51 @@
-GameSettingControls.RelicCount = class extends GameSettingControlSlider
+GameSettingControls.RelicCount = class RelicCount extends GameSettingControlSlider
{
constructor(...args)
{
super(...args);
this.sprintfValue = {};
this.available = false;
- }
-
- onMapChange(mapData)
- {
- this.setEnabled(g_GameAttributes.mapType != "scenario");
-
- let mapValue;
- if (mapData &&
- mapData.settings &&
- mapData.settings.VictoryConditions &&
- mapData.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1 &&
- mapData.settings.RelicCount !== undefined)
- mapValue = mapData.settings.RelicCount;
-
- if (mapValue === undefined || mapValue == g_GameAttributes.settings.RelicCount)
- return;
-
- if (!g_GameAttributes.settings.VictoryConditions)
- g_GameAttributes.settings.VictoryConditions = [];
- if (g_GameAttributes.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) == -1)
- g_GameAttributes.settings.VictoryConditions.push(this.NameCaptureTheRelic);
-
- g_GameAttributes.settings.RelicCount = mapValue;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.relic.watch(() => this.render(), ["count", "available"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- if (!g_GameAttributes.settings.VictoryConditions)
- return;
-
- this.available = g_GameAttributes.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1;
+ this.setHidden(!g_GameSettings.relic.available);
+ this.setEnabled(g_GameSettings.map.type != "scenario");
- if (this.available)
+ if (g_GameSettings.relic.available)
{
- if (g_GameAttributes.settings.RelicCount === undefined)
- {
- g_GameAttributes.settings.RelicCount = this.DefaultValue;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
- else if (g_GameAttributes.settings.RelicCount !== undefined)
- {
- delete g_GameAttributes.settings.RelicCount;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
-
- onGameAttributesBatchChange()
- {
- this.setHidden(!this.available);
-
- if (this.available)
- {
- let value = Math.round(g_GameAttributes.settings.RelicCount);
+ let value = g_GameSettings.relic.count;
this.sprintfValue.number = value;
this.setSelectedValue(
- g_GameAttributes.settings.RelicCount,
+ g_GameSettings.relic.count,
value == 0 ? this.InstantVictory : sprintf(this.CaptionRelicCount(value), this.sprintfValue));
}
}
onValueChange(value)
{
- g_GameAttributes.settings.RelicCount = value;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.relic.setCount(value);
this.gameSettingsControl.setNetworkGameAttributes();
}
-
- onGameAttributesFinalize()
- {
- if (this.available)
- g_GameAttributes.settings.RelicCount = Math.round(g_GameAttributes.settings.RelicCount);
- }
};
GameSettingControls.RelicCount.prototype.TitleCaption =
translate("Relic Count");
GameSettingControls.RelicCount.prototype.CaptionRelicCount =
relicCount => translatePlural("%(number)s relic", "%(number)s relics", relicCount);
GameSettingControls.RelicCount.prototype.Tooltip =
translate("Total number of relics spawned on the map. Relic victory is most realistic with only one or two relics. With greater numbers, the relics are important to capture to receive aura bonuses.");
GameSettingControls.RelicCount.prototype.NameCaptureTheRelic =
"capture_the_relic";
GameSettingControls.RelicCount.prototype.MinValue = 1;
GameSettingControls.RelicCount.prototype.MaxValue = Object.keys(g_CivData).length;
-
-GameSettingControls.RelicCount.prototype.DefaultValue = 2;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/VictoryConditionCheckbox.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/VictoryConditionCheckbox.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/VictoryConditionCheckbox.js (revision 25077)
@@ -1,105 +1,31 @@
/**
* This is an abstract class instantiated per defined VictoryCondition.
*/
class VictoryConditionCheckbox extends GameSettingControlCheckbox
{
constructor(victoryCondition, ...args)
{
super(...args);
- this.victoryCondition = victoryCondition;
+ this.victoryCondition = victoryCondition.Name;
+ this.setTitle(victoryCondition.Title);
+ this.setTooltip(victoryCondition.Description);
- this.setTitle(this.victoryCondition.Title);
- this.setTooltip(this.victoryCondition.Description);
+ g_GameSettings.victoryConditions.watch(() => this.render(), ["active"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onMapChange(mapData)
+ render()
{
- let mapIndex =
- mapData &&
- mapData.settings &&
- mapData.settings.VictoryConditions &&
- mapData.settings.VictoryConditions.indexOf(this.victoryCondition.Name);
-
- if (mapIndex === undefined)
- return;
-
- let index =
- g_GameAttributes.settings &&
- g_GameAttributes.settings.VictoryConditions &&
- g_GameAttributes.settings.VictoryConditions.indexOf(this.victoryCondition.Name);
-
- if (index !== undefined && (mapIndex == -1) == (index == -1))
- return;
-
- if (!g_GameAttributes.settings.VictoryConditions)
- g_GameAttributes.settings.VictoryConditions = [];
-
- if (mapIndex == -1)
- {
- if (index !== undefined)
- g_GameAttributes.settings.VictoryConditions.splice(index, 1);
- }
- else
- g_GameAttributes.settings.VictoryConditions.push(this.victoryCondition.Name);
-
- this.gameSettingsControl.updateGameAttributes();
- }
-
- onGameAttributesChange()
- {
- if (!g_GameAttributes.settings.VictoryConditions)
- {
- g_GameAttributes.settings.VictoryConditions = [];
- for (let victoryCondition of g_VictoryConditions)
- if (victoryCondition.Default)
- g_GameAttributes.settings.VictoryConditions.push(victoryCondition.Name);
- this.gameSettingsControl.updateGameAttributes();
- }
- }
-
- onGameAttributesBatchChange()
- {
- this.setEnabled(
- g_GameAttributes.mapType != "scenario" &&
- (!this.victoryCondition.DisabledWhenChecked ||
- this.victoryCondition.DisabledWhenChecked.every(name =>
- g_GameAttributes.settings.VictoryConditions.indexOf(name) == -1)));
-
- this.setChecked(g_GameAttributes.settings.VictoryConditions.indexOf(this.victoryCondition.Name) != -1);
+ this.setEnabled(g_GameSettings.map.type != "scenario" &&
+ !g_GameSettings.victoryConditions.disabled.has(this.victoryCondition));
+ this.setChecked(g_GameSettings.victoryConditions.active.has(this.victoryCondition));
}
onPress(checked)
{
- let victoryCondition = new Set(g_GameAttributes.settings.VictoryConditions);
-
- if (checked)
- {
- victoryCondition.add(this.victoryCondition.Name);
-
- if (this.victoryCondition.ChangeOnChecked)
- for (let name in this.victoryCondition.ChangeOnChecked)
- if (this.victoryCondition.ChangeOnChecked[name])
- victoryCondition.add(name);
- else
- victoryCondition.delete(name);
- }
- else
- victoryCondition.delete(this.victoryCondition.Name);
-
- g_GameAttributes.settings.VictoryConditions = Array.from(victoryCondition);
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.victoryConditions.setEnabled(this.victoryCondition, checked);
this.gameSettingsControl.setNetworkGameAttributes();
}
-
- onGameAttributesFinalize()
- {
- if (!g_GameAttributes.settings.TriggerScripts)
- g_GameAttributes.settings.TriggerScripts = [];
-
- if (g_GameAttributes.settings.VictoryConditions.indexOf(this.victoryCondition.Name) != -1)
- for (let script of this.victoryCondition.Scripts)
- if (g_GameAttributes.settings.TriggerScripts.indexOf(script) == -1)
- g_GameAttributes.settings.TriggerScripts.push(script);
- }
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Chat/ChatMessages/ClientChat.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Chat/ChatMessages/ClientChat.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Chat/ChatMessages/ClientChat.js (revision 25077)
@@ -1,54 +1,54 @@
ChatMessageEvents.ClientChat = class
{
constructor(setupWindow, chatMessagesPanel)
{
this.chatMessagesPanel = chatMessagesPanel;
this.usernameArgs = {};
this.messageArgs = {};
// TODO: Remove this global required by gui/common/
global.colorizePlayernameByGUID = this.colorizePlayernameByGUID.bind(this);
setupWindow.controls.netMessages.registerNetMessageHandler("chat", this.onClientChat.bind(this));
}
onClientChat(message)
{
this.usernameArgs.username = this.colorizePlayernameByGUID(message.guid);
this.messageArgs.username = setStringTags(sprintf(this.SenderFormat, this.usernameArgs), this.SenderTags);
this.messageArgs.message = escapeText(message.text);
this.chatMessagesPanel.addText(sprintf(this.MessageFormat, this.messageArgs));
}
colorizePlayernameByGUID(guid)
{
// TODO: Controllers should have the moderator-prefix
let username = g_PlayerAssignments[guid] ? escapeText(g_PlayerAssignments[guid].name) : translate("Unknown Player");
let playerID = g_PlayerAssignments[guid] ? g_PlayerAssignments[guid].player : -1;
let color = "white";
if (playerID > 0)
{
- color = g_GameAttributes.settings.PlayerData[playerID - 1].Color;
+ color = g_GameSettings.playerColor.values[playerID - 1];
// Enlighten playercolor to improve readability
let [h, s, l] = rgbToHsl(color.r, color.g, color.b);
let [r, g, b] = hslToRgb(h, s, Math.max(0.6, l));
color = rgbToGuiColor({ "r": r, "g": g, "b": b });
}
return coloredText(username, color);
}
};
ChatMessageEvents.ClientChat.prototype.SenderFormat =
translate("<%(username)s>");
ChatMessageEvents.ClientChat.prototype.MessageFormat =
translate("%(username)s %(message)s");
ChatMessageEvents.ClientChat.prototype.SenderTags = {
"font": "sans-bold-13"
};
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/MapPreview.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/MapPreview.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/MapPreview.js (revision 25077)
@@ -1,47 +1,47 @@
class MapPreview
{
constructor(setupWindow)
{
this.setupWindow = setupWindow;
this.gameSettingsControl = setupWindow.controls.gameSettingsControl;
this.mapCache = setupWindow.controls.mapCache;
this.mapInfoName = Engine.GetGUIObjectByName("mapInfoName");
this.mapPreview = Engine.GetGUIObjectByName("mapPreview");
this.mapPreview.onMouseLeftPress = this.onPress.bind(this); // TODO: Why does onPress not work? CGUI.cpp seems to support it
this.mapPreview.tooltip = this.Tooltip;
- this.gameSettingsControl.registerMapChangeHandler(this.onMapChange.bind(this));
- this.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+ g_GameSettings.map.watch(() => this.renderName(), ["map"]);
+ g_GameSettings.mapPreview.watch(() => this.renderPreview(), ["value"]);
}
onPress()
{
this.setupWindow.pages.MapBrowserPage.openPage();
}
- onMapChange(mapData)
+ renderName()
{
- let preview = mapData && mapData.settings && mapData.settings.Preview;
- if (!g_GameAttributes.settings.Preview || g_GameAttributes.settings.Preview != preview)
+ if (!g_GameSettings.map.map)
{
- g_GameAttributes.settings.Preview = preview;
- this.gameSettingsControl.updateGameAttributes();
+ this.mapInfoName.caption = translate("No selected map");
+ return;
}
+
+ this.mapInfoName.caption = this.mapCache.translateMapName(
+ this.mapCache.getTranslatableMapName(g_GameSettings.map.type, g_GameSettings.map.map));
}
- onGameAttributesBatchChange()
+ renderPreview()
{
- if (!g_GameAttributes.map || !g_GameAttributes.mapType)
+ if (!g_GameSettings.mapPreview.value)
+ {
+ this.mapPreview.sprite = this.mapCache.getMapPreview();
return;
-
- this.mapInfoName.caption = this.mapCache.translateMapName(
- this.mapCache.getTranslatableMapName(g_GameAttributes.mapType, g_GameAttributes.map));
-
- this.mapPreview.sprite =
- this.mapCache.getMapPreview(g_GameAttributes.mapType, g_GameAttributes.map, g_GameAttributes);
+ }
+ this.mapPreview.sprite = g_GameSettings.mapPreview.value;
}
}
MapPreview.prototype.Tooltip =
translate("Click to view the list of available maps.");
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 25077)
@@ -1,22 +1,25 @@
+
+
+
Match Setup
Index: ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowser.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowser.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/MapBrowser.js (revision 25077)
@@ -1,48 +1,58 @@
class MapBrowser
{
constructor(mapCache, mapFilters, setupWindow = undefined)
{
this.openPageHandlers = new Set();
this.closePageHandlers = new Set();
this.mapCache = mapCache;
this.mapFilters = mapFilters;
this.mapBrowserPage = Engine.GetGUIObjectByName("mapBrowserPage");
this.mapBrowserPageDialog = Engine.GetGUIObjectByName("mapBrowserPageDialog");
this.gridBrowser = new MapGridBrowser(this, setupWindow);
this.controls = new MapBrowserPageControls(this, this.gridBrowser);
this.open = false;
}
+ submitMapSelection()
+ {
+ this.onSubmitMapSelection(
+ this.gridBrowser.getSelectedFile(),
+ this.controls.MapFiltering.getSelectedMapType(),
+ this.controls.MapFiltering.getSelectedMapFilter()
+ );
+ this.closePage();
+ }
+
// TODO: this is mostly gamesetup specific stuff.
registerOpenPageHandler(handler)
{
this.openPageHandlers.add(handler);
}
registerClosePageHandler(handler)
{
this.closePageHandlers.add(handler);
}
openPage()
{
if (this.open)
return;
for (let handler of this.openPageHandlers)
handler();
this.open = true;
}
closePage()
{
if (!this.open)
return;
for (let handler of this.closePageHandlers)
handler();
this.open = false;
}
}
Index: ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/grid/MapGridBrowser.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/grid/MapGridBrowser.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/grid/MapGridBrowser.js (revision 25077)
@@ -1,103 +1,94 @@
class MapGridBrowser extends GridBrowser
{
constructor(mapBrowserPage, setupWindow)
{
super(Engine.GetGUIObjectByName("mapBrowserContainer"));
this.setupWindow = setupWindow;
this.mapBrowserPage = mapBrowserPage;
this.mapCache = mapBrowserPage.mapCache;
this.mapFilters = mapBrowserPage.mapFilters;
this.mapList = [];
this.items = this.container.children.map((imageObject, itemIndex) =>
new MapGridBrowserItem(mapBrowserPage, this, imageObject, itemIndex));
this.mapBrowserPage.registerOpenPageHandler(this.onOpenPage.bind(this));
this.mapBrowserPage.registerClosePageHandler(this.onClosePage.bind(this));
this.mapBrowserPage.mapBrowserPageDialog.onMouseWheelUp = () => this.previousPage(false);
this.mapBrowserPage.mapBrowserPageDialog.onMouseWheelDown = () => this.nextPage(false);
}
onOpenPage()
{
this.updateMapList();
- this.setSelectedIndex(this.mapList.findIndex(map => map.file == g_GameAttributes.map));
this.goToPageOfSelected();
this.container.onWindowResized = this.onWindowResized.bind(this);
Engine.SetGlobalHotkey(this.HotkeyConfigNext, "Press", this.nextPage.bind(this));
Engine.SetGlobalHotkey(this.HotkeyConfigPrevious, "Press", this.previousPage.bind(this));
}
onClosePage()
{
delete this.container.onWindowResized;
Engine.UnsetGlobalHotkey(this.HotkeyConfigNext, "Press");
Engine.UnsetGlobalHotkey(this.HotkeyConfigPrevious, "Press");
}
+ getSelectedFile()
+ {
+ return this.mapList[this.selected].file || undefined;
+ }
+
+ select(mapFile)
+ {
+ this.setSelectedIndex(this.mapList.findIndex(map => map.file == mapFile));
+ this.goToPageOfSelected();
+ }
+
updateMapList()
{
let selectedMap =
this.mapList[this.selected] &&
this.mapList[this.selected].file || undefined;
-
let mapList = this.mapFilters.getFilteredMaps(
this.mapBrowserPage.controls.MapFiltering.getSelectedMapType(),
this.mapBrowserPage.controls.MapFiltering.getSelectedMapFilter());
let filterText = this.mapBrowserPage.controls.MapFiltering.getSearchText();
if (filterText)
{
mapList = MatchSort.get(filterText, mapList, "name");
if (!mapList.length)
{
let filter = "all";
for (let type of g_MapTypes.Name)
for (let map of this.mapFilters.getFilteredMaps(type, filter))
mapList.push(Object.assign({ "type": type, "filter": filter }, map));
mapList = MatchSort.get(filterText, mapList, "name");
}
}
if (this.mapBrowserPage.controls.MapFiltering.getSelectedMapType() == "random")
{
mapList = [{
"file": "random",
"name": "Random",
"description": "Pick a map at random.",
}, ...mapList];
}
this.mapList = mapList;
this.itemCount = this.mapList.length;
this.resizeGrid();
this.setSelectedIndex(this.mapList.findIndex(map => map.file == selectedMap));
}
-
- submitMapSelection()
- {
- if (!g_IsController)
- return;
-
- let map = this.mapList[this.selected] || undefined;
- if (!map)
- return;
-
- g_GameAttributes.mapType = map.type ? map.type :
- this.mapBrowserPage.controls.MapFiltering.getSelectedMapType();
- g_GameAttributes.mapFilter = map.filter ? map.filter :
- this.mapBrowserPage.controls.MapFiltering.getSelectedMapFilter();
- g_GameAttributes.map = map.file;
- this.setupWindow.controls.gameSettingsControl.updateGameAttributes();
- this.setupWindow.controls.gameSettingsControl.setNetworkGameAttributes();
- }
}
MapGridBrowser.prototype.ItemRatio = 4 / 3;
MapGridBrowser.prototype.DefaultItemWidth = 200;
MapGridBrowser.prototype.MinItemWidth = 100;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/Ceasefire.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/Ceasefire.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/Ceasefire.js (revision 25077)
@@ -1,77 +1,50 @@
-GameSettingControls.Ceasefire = class extends GameSettingControlSlider
+GameSettingControls.Ceasefire = class Ceasefire extends GameSettingControlSlider
{
constructor(...args)
{
super(...args);
this.sprintfValue = {};
- }
-
- onMapChange(mapData)
- {
- let mapValue;
- if (mapData &&
- mapData.settings &&
- mapData.settings.Ceasefire !== undefined)
- mapValue = mapData.settings.Ceasefire;
-
- if (mapValue !== undefined && mapValue != g_GameAttributes.settings.Ceasefire)
- {
- g_GameAttributes.settings.Ceasefire = mapValue;
- this.gameSettingsControl.updateGameAttributes();
- }
- this.setEnabled(g_GameAttributes.mapType != "scenario");
+ g_GameSettings.ceasefire.watch(() => this.render(), ["value"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- if (g_GameAttributes.settings.Ceasefire == undefined)
- {
- g_GameAttributes.settings.Ceasefire = this.DefaultValue;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
+ this.setEnabled(g_GameSettings.map.type != "scenario");
- onGameAttributesBatchChange()
- {
- let value = Math.round(g_GameAttributes.settings.Ceasefire);
+ let value = Math.round(g_GameSettings.ceasefire.value);
this.sprintfValue.minutes = value;
- this.setSelectedValue(
- g_GameAttributes.settings.Ceasefire,
+ this.setSelectedValue(g_GameSettings.ceasefire.value,
value == 0 ?
this.NoCeasefireCaption :
sprintf(this.CeasefireCaption(value), this.sprintfValue));
}
onValueChange(value)
{
- g_GameAttributes.settings.Ceasefire = value;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.ceasefire.setValue(value);
this.gameSettingsControl.setNetworkGameAttributes();
}
-
- onGameAttributesFinalize()
- {
- g_GameAttributes.settings.Ceasefire = Math.round(g_GameAttributes.settings.Ceasefire);
- }
};
GameSettingControls.Ceasefire.prototype.TitleCaption =
translate("Ceasefire");
GameSettingControls.Ceasefire.prototype.Tooltip =
translate("Set time where no attacks are possible.");
GameSettingControls.Ceasefire.prototype.NoCeasefireCaption =
translateWithContext("ceasefire", "No ceasefire");
GameSettingControls.Ceasefire.prototype.CeasefireCaption =
minutes => translatePluralWithContext("ceasefire", "%(minutes)s minute", "%(minutes)s minutes", minutes);
GameSettingControls.Ceasefire.prototype.DefaultValue = 0;
GameSettingControls.Ceasefire.prototype.MinValue = 0;
GameSettingControls.Ceasefire.prototype.MaxValue = 45;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/WonderDuration.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/WonderDuration.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/GameSettings/Single/Sliders/WonderDuration.js (revision 25077)
@@ -1,106 +1,53 @@
-GameSettingControls.WonderDuration = class extends GameSettingControlSlider
+GameSettingControls.WonderDuration = class WonderDuration extends GameSettingControlSlider
{
constructor(...args)
{
super(...args);
this.sprintfValue = {};
this.available = false;
- }
-
- onMapChange(mapData)
- {
- this.setEnabled(g_GameAttributes.mapType != "scenario");
-
- let mapValue;
- if (mapData &&
- mapData.settings &&
- mapData.settings.VictoryConditions &&
- mapData.settings.VictoryConditions.indexOf(this.NameWonderVictory) != -1 &&
- mapData.settings.WonderDuration !== undefined)
- mapValue = mapData.settings.WonderDuration;
-
- if (mapValue === undefined || mapValue == g_GameAttributes.settings.WonderDuration)
- return;
-
- if (!g_GameAttributes.settings.VictoryConditions)
- g_GameAttributes.settings.VictoryConditions = [];
- if (g_GameAttributes.settings.VictoryConditions.indexOf(this.NameWonderVictory) == -1)
- g_GameAttributes.settings.VictoryConditions.push(this.NameWonderVictory);
-
- g_GameAttributes.settings.WonderDuration = mapValue;
-
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.wonder.watch(() => this.render(), ["duration", "available"]);
+ g_GameSettings.map.watch(() => this.render(), ["type"]);
+ this.render();
}
- onGameAttributesChange()
+ render()
{
- if (!g_GameAttributes.settings.VictoryConditions)
- return;
-
- this.available = g_GameAttributes.settings.VictoryConditions.indexOf(this.NameWonderVictory) != -1;
+ this.setHidden(!g_GameSettings.wonder.available);
+ this.setEnabled(g_GameSettings.map.type != "scenario");
- if (this.available)
- {
- if (g_GameAttributes.settings.WonderDuration === undefined)
- {
- g_GameAttributes.settings.WonderDuration = this.DefaultValue;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
- else if (g_GameAttributes.settings.WonderDuration !== undefined)
+ if (g_GameSettings.wonder.available)
{
- delete g_GameAttributes.settings.WonderDuration;
- this.gameSettingsControl.updateGameAttributes();
- }
- }
-
- onGameAttributesBatchChange()
- {
- this.setHidden(!this.available);
-
- if (this.available)
- {
- let value = Math.round(g_GameAttributes.settings.WonderDuration);
+ let value = g_GameSettings.wonder.duration;
this.sprintfValue.min = value;
this.setSelectedValue(
- g_GameAttributes.settings.WonderDuration,
+ g_GameSettings.wonder.duration,
value == 0 ? this.InstantVictory : sprintf(this.CaptionVictoryTime(value), this.sprintfValue));
}
}
onValueChange(value)
{
- g_GameAttributes.settings.WonderDuration = value;
- this.gameSettingsControl.updateGameAttributes();
+ g_GameSettings.wonder.setDuration(value);
this.gameSettingsControl.setNetworkGameAttributes();
}
-
- onGameAttributesFinalize()
- {
- if (this.available)
- g_GameAttributes.settings.WonderDuration = Math.round(g_GameAttributes.settings.WonderDuration);
- }
};
GameSettingControls.WonderDuration.prototype.TitleCaption =
translate("Wonder Duration");
GameSettingControls.WonderDuration.prototype.Tooltip =
translate("Minutes until the player has achieved Wonder Victory");
-GameSettingControls.WonderDuration.prototype.NameWonderVictory =
- "wonder";
-
GameSettingControls.WonderDuration.prototype.CaptionVictoryTime =
min => translatePluralWithContext("victory duration", "%(min)s minute", "%(min)s minutes", min);
GameSettingControls.WonderDuration.prototype.InstantVictory =
translateWithContext("victory duration", "Immediate Victory.");
GameSettingControls.WonderDuration.prototype.MinValue = 0;
GameSettingControls.WonderDuration.prototype.MaxValue = 60;
GameSettingControls.WonderDuration.prototype.DefaultValue = 20;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Chat/ChatInputAutocomplete.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Chat/ChatInputAutocomplete.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Chat/ChatInputAutocomplete.js (revision 25077)
@@ -1,37 +1,37 @@
class ChatInputAutocomplete
{
constructor(gameSettingControlManager, gameSettingsControl, playerAssignmentsControl)
{
this.gameSettingControlManager = gameSettingControlManager;
this.entries = undefined;
playerAssignmentsControl.registerPlayerAssignmentsChangeHandler(this.onAutocompleteChange.bind(this));
- gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onAutocompleteChange.bind(this));
+ gameSettingsControl.registerSettingsChangeHandler(this.onAutocompleteChange.bind(this));
}
onAutocompleteChange()
{
this.entries = undefined;
}
// Collects all strings that can be autocompleted and
// sorts them by priority (so that playernames are always autocompleted first).
getAutocompleteEntries()
{
if (this.entries)
return this.entries;
// Maps from priority to autocompletable strings
let entries = { "0": [] };
this.gameSettingControlManager.addAutocompleteEntries(entries);
let allEntries = Object.keys(entries).sort((a, b) => +b - +a).reduce(
(all, priority) => all.concat(entries[priority]),
[]);
this.entries = Array.from(new Set(allEntries));
return this.entries;
}
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingsPanel.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingsPanel.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/GameSettingsPanel.js (revision 25077)
@@ -1,188 +1,188 @@
class GameSettingsPanel
{
constructor(setupWindow, gameSettingTabs, gameSettingControlManager)
{
this.centerRightPanel = Engine.GetGUIObjectByName("centerRightPanel");
this.settingTabButtonsFrame = Engine.GetGUIObjectByName("settingTabButtonsFrame");
this.settingsPanelFrame = Engine.GetGUIObjectByName("settingsPanelFrame");
this.gameSettingControlManager = gameSettingControlManager;
this.gameSettingsPanelResizeHandlers = new Set();
this.setupWindow = Engine.GetGUIObjectByName("setupWindow");
this.setupWindow.onWindowResized = this.onWindowResized.bind(this);
this.settingsPanel = Engine.GetGUIObjectByName("settingsPanel");
this.enabled = Engine.ConfigDB_GetValue("user", this.ConfigNameSlide) == "true";
this.slideSpeed = this.enabled ? this.SlideSpeed : Infinity;
this.lastTickTime = undefined;
gameSettingTabs.registerTabSelectHandler(this.updateSize.bind(this));
- setupWindow.controls.gameSettingsControl.registerGameAttributesBatchChangeHandler(this.updateSize.bind(this));
+ setupWindow.controls.gameSettingsControl.registerUpdateLayoutHandler(this.updateSize.bind(this));
setupWindow.registerLoadHandler(this.triggerResizeHandlers.bind(this));
}
registerGameSettingsPanelResizeHandler(handler)
{
this.gameSettingsPanelResizeHandlers.add(handler);
}
triggerResizeHandlers()
{
for (let handler of this.gameSettingsPanelResizeHandlers)
handler(this.settingsPanelFrame);
}
onWindowResized()
{
this.updateSize();
this.triggerResizeHandlers();
}
updateSize()
{
this.gameSettingControlManager.updateSettingVisibility();
this.positionSettings();
this.lastTickTime = undefined;
this.settingsPanelFrame.onTick = this.onTick.bind(this);
}
onTick()
{
let now = Date.now();
let tickLength = now - this.lastTickTime;
let previousTime = this.lastTickTime;
this.lastTickTime = now;
if (previousTime === undefined)
return;
let distance = this.slideSpeed * tickLength;
let rightBorder = this.settingTabButtonsFrame.size.left;
let offset = 0;
if (g_TabCategorySelected === undefined)
{
let maxOffset = rightBorder - this.settingsPanelFrame.size.left;
if (maxOffset > 0)
offset = Math.min(distance, maxOffset);
}
else if (rightBorder > this.settingsPanelFrame.size.right)
{
offset = Math.min(distance, rightBorder - this.settingsPanelFrame.size.right);
}
else
{
let maxOffset = this.settingsPanelFrame.size.left - rightBorder + (this.settingsPanelFrame.size.right - this.settingsPanelFrame.size.left);
if (maxOffset > 0)
offset = -Math.min(distance, maxOffset);
}
if (offset)
this.changePanelWidth(offset);
else
{
delete this.settingsPanelFrame.onTick;
this.lastTickTime = undefined;
}
}
changePanelWidth(offset)
{
if (!offset)
return;
let size = this.settingsPanelFrame.size;
size.left += offset;
size.right += offset;
this.settingsPanelFrame.size = size;
this.triggerResizeHandlers();
}
/**
* Distribute the currently visible settings over the settings panel.
* First calculate the number of columns required, then place the setting frames.
*/
positionSettings()
{
let setupWindowSize = this.setupWindow.getComputedSize();
let columnWidth = Math.min(
this.MaxColumnWidth,
(setupWindowSize.right - setupWindowSize.left + this.centerRightPanel.size.left) / 2);
let settingsPerColumn;
{
let settingPanelSize = this.settingsPanel.getComputedSize();
let maxSettingsPerColumn = Math.floor((settingPanelSize.bottom - settingPanelSize.top) / this.SettingHeight);
let settingCount = this.settingsPanel.children.filter(child => !child.children[0].hidden).length;
settingsPerColumn = settingCount / Math.ceil(settingCount / maxSettingsPerColumn);
}
let yPos = this.SettingMarginBottom;
let column = 0;
let settingsThisColumn = 0;
let selectedTab = g_GameSettingsLayout[g_TabCategorySelected];
if (!selectedTab)
return;
for (let name of selectedTab.settings)
{
let settingFrame = this.gameSettingControlManager.gameSettingControls[name].frame;
if (settingFrame.hidden)
continue;
if (settingsThisColumn >= settingsPerColumn)
{
yPos = this.SettingMarginBottom;
++column;
settingsThisColumn = 0;
}
settingFrame.size = new GUISize(
columnWidth * column,
yPos,
columnWidth * (column + 1) - this.SettingMarginRight,
yPos + this.SettingHeight - this.SettingMarginBottom);
yPos += this.SettingHeight;
++settingsThisColumn;
}
{
let size = this.settingsPanelFrame.size;
size.right = size.left + (column + 1) * columnWidth;
this.settingsPanelFrame.size = size;
}
}
}
GameSettingsPanel.prototype.ConfigNameSlide =
"gui.gamesetup.settingsslide";
/**
* Maximum width of a column in the settings panel.
*/
GameSettingsPanel.prototype.MaxColumnWidth = 470;
/**
* Pixels per millisecond the settings panel slides when opening/closing.
*/
GameSettingsPanel.prototype.SlideSpeed = 1.2;
/**
* Vertical size of a setting frame.
*/
GameSettingsPanel.prototype.SettingHeight = 36;
/**
* Horizontal space between two setting frames.
*/
GameSettingsPanel.prototype.SettingMarginRight = 10;
/**
* Vertical space between two setting frames.
*/
GameSettingsPanel.prototype.SettingMarginBottom = 2;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 25077)
@@ -1,51 +1,53 @@
// TODO: Remove these globals by rewriting gamedescription.js
const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes);
const g_MapTypes = prepareForDropdown(g_Settings && g_Settings.MapTypes);
const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities);
const g_WorldPopulationCapacities = prepareForDropdown(g_Settings && g_Settings.WorldPopulationCapacities);
const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources);
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
/**
* Offer users to select playable civs only.
* Load unselectable civs as they could appear in scenario maps.
*/
const g_CivData = loadCivData(false, false);
/**
- * Whether this is a single- or multiplayer match.
+ * Remembers which clients are assigned to which player slots and whether they are ready.
+ * The keys are GUIDs or "local" in single-player.
*/
-const g_IsNetworked = Engine.HasNetClient();
+var g_PlayerAssignments = {};
/**
- * Is this user in control of game settings (i.e. is a network server, or offline player).
+ * Holds the actual settings & related logic.
+ * Global out of convenience in GUI controls.
*/
-const g_IsController = !g_IsNetworked || Engine.IsNetController();
+var g_GameSettings;
/**
- * Central data storing all settings relevant to the map generation and simulation.
+ * Whether this is a single- or multiplayer match.
*/
-var g_GameAttributes = {};
+const g_IsNetworked = Engine.HasNetClient();
/**
- * Remembers which clients are assigned to which player slots and whether they are ready.
- * The keys are GUIDs or "local" in single-player.
+ * Is this user in control of game settings (i.e. is a network server, or offline player).
*/
-var g_PlayerAssignments = {};
+const g_IsController = !g_IsNetworked || Engine.IsNetController();
/**
- * This instance owns all handlers that control the two synchronized states g_GameAttributes and g_PlayerAssignments.
+ * This instance owns all handlers that control
+ * the two synchronized states g_GameSettings and g_PlayerAssignments.
*/
var g_SetupWindow;
// TODO: Remove these two global functions by specifying the JS class name in the XML of the GUI page.
function init(initData, hotloadData)
{
g_SetupWindow = new SetupWindow(initData, hotloadData);
}
function getHotloadData()
{
return g_SetupWindow.getHotloadData();
}
Index: ps/trunk/binaries/data/mods/public/gui/maps/MapFilters.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/MapFilters.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/MapFilters.js (revision 25077)
@@ -1,119 +1,122 @@
class MapFilters
{
constructor(mapCache)
{
this.mapCache = mapCache;
}
/**
* Some map filters may reject every map of a particular mapType.
* This function allows identifying which map filters have any matches for that maptype.
*/
getAvailableMapFilters(mapTypeName)
{
return this.Filters.filter(filter =>
this.getFilteredMaps(mapTypeName, filter.Name, true));
}
/**
* This function identifies all maps matching the given mapType and mapFilter.
* If existence is true, it will only test if there is at least one file for that mapType and mapFilter.
* Otherwise it returns an array with filename, translated map title and map description.
*/
getFilteredMaps(mapTypeName, filterName, existence)
{
+ if (!mapTypeName || !filterName)
+ return existence ? false : [];
+
let index = g_MapTypes.Name.findIndex(name => name == mapTypeName);
if (index == -1)
{
error("Can't get filtered maps for invalid maptype: " + mapTypeName);
return undefined;
}
let mapFilter = this.Filters.find(filter => filter.Name == filterName);
if (!mapFilter)
{
error("Invalid mapfilter name: " + filterName);
return undefined;
}
Engine.ProfileStart("getFilteredMaps");
let maps = [];
let mapTypePath = g_MapTypes.Path[index];
for (let filename of listFiles(mapTypePath, g_MapTypes.Suffix[index], false))
{
if (filename.startsWith(this.HiddenFilesPrefix))
continue;
let mapPath = mapTypePath + filename;
let mapData = this.mapCache.getMapData(mapTypeName, mapPath);
// Map files may come with custom json files
if (!mapData || !mapData.settings)
continue;
if (MatchesClassList(mapData.settings.Keywords || [], mapFilter.Match))
{
if (existence)
{
Engine.ProfileStop();
return true;
}
maps.push({
"file": mapPath,
"name": translate(mapData.settings.Name),
"description": translate(mapData.settings.Description)
});
}
}
Engine.ProfileStop();
return existence ? false : maps.sort((a, b) => a.name.localeCompare(b.name));
}
}
/**
* When maps start with this prefix, they will not appear in the maplist.
* Used for the Atlas _default.xml for instance.
*/
MapFilters.prototype.HiddenFilesPrefix = "_";
MapFilters.prototype.Filters = [
{
"Name": "default",
"Title": translate("Default"),
"Description": translate("All maps except naval and demo maps."),
"Match": ["!naval !demo !hidden"]
},
{
"Name": "naval",
"Title": translate("Naval Maps"),
"Description": translate("Maps where ships are needed to reach the enemy."),
"Match": ["naval"]
},
{
"Name": "demo",
"Title": translate("Demo Maps"),
"Description": translate("These maps are not playable but for demonstration purposes only."),
"Match": ["demo"]
},
{
"Name": "new",
"Title": translate("New Maps"),
"Description": translate("Maps that are brand new in this release of the game."),
"Match": ["new"]
},
{
"Name": "trigger",
"Title": translate("Trigger Maps"),
"Description": translate("Maps that come with scripted events and potentially spawn enemy units."),
"Match": ["trigger"]
},
{
"Name": "all",
"Title": translate("All Maps"),
"Description": translate("Every map of the chosen maptype."),
"Match": "!"
}
];
Index: ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/controls/MapFiltering.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/controls/MapFiltering.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/controls/MapFiltering.js (revision 25077)
@@ -1,70 +1,76 @@
MapBrowserPageControls.prototype.MapFiltering = class
{
constructor(mapBrowserPage, gridBrowser)
{
this.mapBrowserPage = mapBrowserPage;
this.gridBrowser = gridBrowser;
this.mapFilters = mapBrowserPage.mapFilters;
this.searchBox = new LabelledInput("mapBrowserSearchBox")
.setupEvents(() => this.onChange());
this.mapType = new LabelledDropdown("mapBrowserMapType")
.setupEvents(() => this.onMapTypeChange());
this.mapFilter = new LabelledDropdown("mapBrowserMapFilter")
.setupEvents(() => this.onChange());
mapBrowserPage.registerOpenPageHandler(() => this.onOpenPage());
mapBrowserPage.registerClosePageHandler(() => this.onClosePage());
this.searchBox.blur();
}
onOpenPage()
{
// setTimeout avoids having the hotkey key inserted into the input text.
setTimeout(() => this.searchBox.focus(), 0);
- this.renderMapFilter();
- this.mapFilter.select(g_GameAttributes.mapFilter);
- this.mapType.render(g_MapTypes.Title, g_MapTypes.Name);
- this.mapType.select(g_GameAttributes.mapType);
}
onClosePage()
{
this.searchBox.blur();
}
onMapTypeChange()
{
this.renderMapFilter();
this.onChange();
}
onChange()
{
this.gridBrowser.updateMapList();
this.gridBrowser.goToPageOfSelected();
}
+ select(filter, type)
+ {
+ this.mapType.render(g_MapTypes.Title, g_MapTypes.Name);
+ this.mapType.select(type);
+ this.renderMapFilter();
+ this.mapFilter.select(filter);
+ this.gridBrowser.updateMapList();
+ this.gridBrowser.goToPageOfSelected();
+ }
+
renderMapFilter()
{
let filters = this.mapFilters.getAvailableMapFilters(this.getSelectedMapType());
this.mapFilter.render(filters.map(f => f.Title), filters.map(f => f.Name));
}
// TODO: would be nicer to store this state somewhere else.
getSearchText()
{
return this.searchBox.getText() || "";
}
getSelectedMapType()
{
- return this.mapType.getSelected() || g_GameAttributes.mapType;
+ return this.mapType.getSelected() || "";
}
getSelectedMapFilter()
{
- return this.mapFilter.getSelected() || g_GameAttributes.mapFilter;
+ return this.mapFilter.getSelected() || "";
}
};
Index: ps/trunk/binaries/data/mods/public/gui/page_autostart.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/page_autostart.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/page_autostart.xml (revision 25077)
@@ -0,0 +1,8 @@
+
+
+
+ autostart/autostart.xml
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/page_autostart.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/maps/random/unknown.json
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/unknown.json (revision 25076)
+++ ps/trunk/binaries/data/mods/public/maps/random/unknown.json (revision 25077)
@@ -1,88 +1,98 @@
{
"settings" : {
"Name" : "Unknown",
"Script" : "unknown.js",
"Description" : "The unknown.",
"Preview" : "unknown.png",
"SupportedBiomes": "generic/",
"CircularMap" : true,
- "Landscapes": {
- "land" : [
- {
- "Id": "Continent",
- "Name": "Continent",
- "Description": "All players starts on a continent surrounded by water.",
- "Preview": "unknown_continent.png"
- },
- {
- "Id": "Isthmus",
- "Name": "Isthmus",
- "Description": "Two Mediterranean land masses connected by a narrow spit of land, called an 'Isthmus'.",
- "Preview": "unknown_isthmus.png"
- },
- {
- "Id": "CentralRiver",
- "Name": "Central River",
- "Description": "A small central river.",
- "Preview": "unknown_central_river.png"
- },
- {
- "Id": "EdgeSeas",
- "Name": "Edge Seas",
- "Description": "Players are aligned on a strip of land with seas bordering on one or both sides that may hold islands.",
- "Preview": "unknown_edge_seas.png"
- },
- {
- "Id": "Gulf",
- "Name": "Gulf",
- "Description": "Land shaped like a concrescent moon around a central lake.",
- "Preview": "unknown_gulf.png"
- },
- {
- "Id": "Lakes",
- "Name": "Lakes",
- "Description": "Mainland style with some small random lakes.",
- "Preview": "unknown_lakes.png"
- },
- {
- "Id": "Passes",
- "Name": "Passes",
- "Description": "A large hill encompasses the map and leaves players only a small passage to the two neighboring players.",
- "Preview": "unknown_passes.png"
- },
- {
- "Id": "Lowlands",
- "Name": "Lowlands",
- "Description": "The land is enclosed by a hill that leaves a small area per player connected to the large central place.",
- "Preview": "unknown_lowlands.png"
- },
- {
- "Id": "Mainland",
- "Name": "Mainland",
- "Description": "A typical map without any water.",
- "Preview": "unknown_mainland.png"
- }
- ],
- "naval": [
- {
- "Id": "CentralSea",
- "Name": "Naval: Central Sea",
- "Description": "A huge sea is dividing the map into two halves.",
- "Preview": "unknown_central_sea.png"
- },
- {
- "Id": "Archipelago",
- "Name": "Naval: Archipelago",
- "Description": "Players are scattered across island chains and disconnected islands.",
- "Preview": "unknown_archipelago.png"
- },
- {
- "Id": "RiversAndLake",
- "Name": "Naval: Lake and rivers",
- "Description": "A central lake with rivers possibly separating neighboring players.",
- "Preview": "unknown_rivers_and_lake.png"
- }
- ]
- }
+ "Landscapes": [
+ {
+ "Id": "random_land",
+ "Name": "Random Land",
+ "Description": "Choose a random land landscape",
+ "Items": [
+ {
+ "Id": "Continent",
+ "Name": "Continent",
+ "Description": "All players starts on a continent surrounded by water.",
+ "Preview": "unknown_continent.png"
+ },
+ {
+ "Id": "Isthmus",
+ "Name": "Isthmus",
+ "Description": "Two Mediterranean land masses connected by a narrow spit of land, called an 'Isthmus'.",
+ "Preview": "unknown_isthmus.png"
+ },
+ {
+ "Id": "CentralRiver",
+ "Name": "Central River",
+ "Description": "A small central river.",
+ "Preview": "unknown_central_river.png"
+ },
+ {
+ "Id": "EdgeSeas",
+ "Name": "Edge Seas",
+ "Description": "Players are aligned on a strip of land with seas bordering on one or both sides that may hold islands.",
+ "Preview": "unknown_edge_seas.png"
+ },
+ {
+ "Id": "Gulf",
+ "Name": "Gulf",
+ "Description": "Land shaped like a concrescent moon around a central lake.",
+ "Preview": "unknown_gulf.png"
+ },
+ {
+ "Id": "Lakes",
+ "Name": "Lakes",
+ "Description": "Mainland style with some small random lakes.",
+ "Preview": "unknown_lakes.png"
+ },
+ {
+ "Id": "Passes",
+ "Name": "Passes",
+ "Description": "A large hill encompasses the map and leaves players only a small passage to the two neighboring players.",
+ "Preview": "unknown_passes.png"
+ },
+ {
+ "Id": "Lowlands",
+ "Name": "Lowlands",
+ "Description": "The land is enclosed by a hill that leaves a small area per player connected to the large central place.",
+ "Preview": "unknown_lowlands.png"
+ },
+ {
+ "Id": "Mainland",
+ "Name": "Mainland",
+ "Description": "A typical map without any water.",
+ "Preview": "unknown_mainland.png"
+ }
+ ]
+ },
+ {
+ "Id": "random_naval",
+ "Name": "Random Naval",
+ "Description": "Choose a random naval landscape",
+ "Items": [
+ {
+ "Id": "CentralSea",
+ "Name": "Naval: Central Sea",
+ "Description": "A huge sea is dividing the map into two halves.",
+ "Preview": "unknown_central_sea.png"
+ },
+ {
+ "Id": "Archipelago",
+ "Name": "Naval: Archipelago",
+ "Description": "Players are scattered across island chains and disconnected islands.",
+ "Preview": "unknown_archipelago.png"
+ },
+ {
+ "Id": "RiversAndLake",
+ "Name": "Naval: Lake and rivers",
+ "Description": "A central lake with rivers possibly separating neighboring players.",
+ "Preview": "unknown_rivers_and_lake.png"
+ }
+ ]
+ }
+ ]
}
}
Index: ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/grid/MapGridBrowserItem.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/grid/MapGridBrowserItem.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/maps/mapbrowser/grid/MapGridBrowserItem.js (revision 25077)
@@ -1,64 +1,63 @@
class MapGridBrowserItem extends GridBrowserItem
{
constructor(mapBrowserPage, mapGridBrowser, imageObject, itemIndex)
{
super(mapGridBrowser, imageObject, itemIndex);
this.mapBrowserPage = mapBrowserPage;
this.mapCache = mapBrowserPage.mapCache;
this.mapPreview = Engine.GetGUIObjectByName("mapPreview[" + itemIndex + "]");
mapGridBrowser.registerSelectionChangeHandler(this.onSelectionChange.bind(this));
mapGridBrowser.registerPageChangeHandler(this.onGridResize.bind(this));
if (g_IsController)
this.imageObject.onMouseLeftDoubleClick = this.onMouseLeftDoubleClick.bind(this);
}
onSelectionChange()
{
this.updateSprite();
}
onGridResize()
{
super.onGridResize();
this.updateMapAssignment();
this.updateSprite();
}
updateSprite()
{
this.imageObject.sprite =
this.gridBrowser.selected == this.itemIndex + this.gridBrowser.currentPage * this.gridBrowser.itemsPerRow ?
this.SelectedSprite :
"";
}
updateMapAssignment()
{
let map = this.gridBrowser.mapList[
this.itemIndex + this.gridBrowser.currentPage * this.gridBrowser.itemsPerRow] || undefined;
if (!map)
return;
this.mapPreview.caption = map.name;
this.imageObject.tooltip =
map.description + "\n" +
this.gridBrowser.container.tooltip;
this.mapPreview.sprite =
this.mapCache.getMapPreview(this.mapBrowserPage.controls.MapFiltering.getSelectedMapType(), map.file);
}
onMouseLeftDoubleClick()
{
- this.gridBrowser.submitMapSelection();
- this.mapBrowserPage.closePage();
+ this.mapBrowserPage.submitMapSelection();
}
}
MapGridBrowserItem.prototype.SelectedSprite = "color: 120 0 0 255";
Index: ps/trunk/binaries/data/mods/public/gui/session/objectives/ObjectivesDialog.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/objectives/ObjectivesDialog.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/session/objectives/ObjectivesDialog.js (revision 25077)
@@ -1,59 +1,59 @@
class ObjectivesDialog
{
constructor(playerViewControl, mapCache)
{
this.gameDescription = Engine.GetGUIObjectByName("gameDescription");
this.objectivesPlayerstate = Engine.GetGUIObjectByName("objectivesPlayerstate");
this.objectivesPanel = Engine.GetGUIObjectByName("objectivesPanel");
this.objectivesTitle = Engine.GetGUIObjectByName("objectivesTitle");
// TODO: atlas should support this
if (!Engine.IsAtlasRunning())
- Engine.GetGUIObjectByName("gameDescriptionText").caption = getGameDescription(mapCache);
+ Engine.GetGUIObjectByName("gameDescriptionText").caption = getGameDescription(g_GameAttributes, mapCache);
Engine.GetGUIObjectByName("closeObjectives").onPress = this.close.bind(this);
registerPlayersInitHandler(this.rebuild.bind(this));
registerPlayersFinishedHandler(this.rebuild.bind(this));
playerViewControl.registerPlayerIDChangeHandler(this.rebuild.bind(this));
}
open()
{
this.objectivesPanel.hidden = false;
}
close()
{
this.objectivesPanel.hidden = true;
}
isOpen()
{
return !this.objectivesPanel.hidden;
}
toggle()
{
let open = this.isOpen();
closeOpenDialogs();
if (!open)
this.open();
}
rebuild()
{
let player = g_Players[Engine.GetPlayerID()];
let playerState = player && player.state;
let isActive = !playerState || playerState == "active";
this.objectivesPlayerstate.hidden = isActive;
this.objectivesPlayerstate.caption = g_PlayerStateMessages[playerState] || "";
let size = this.gameDescription.size;
size.top = (isActive ? this.objectivesTitle : this.objectivesPlayerstate).size.bottom;
this.gameDescription.size = size;
}
}
Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js (revision 25077)
@@ -1,364 +1,364 @@
/**
* Used for checking replay compatibility.
*/
const g_EngineInfo = Engine.GetEngineInfo();
/**
* Needed for formatPlayerInfo to show the player civs in the details.
*/
const g_CivData = loadCivData(false, false);
/**
* Used for creating the mapsize filter.
*/
const g_MapSizes = prepareForDropdown(g_Settings && g_Settings.MapSizes);
/**
* All replays found in the directory.
*/
var g_Replays = [];
/**
* List of replays after applying the display filter.
*/
var g_ReplaysFiltered = [];
/**
* Array of unique usernames of all replays. Used for autocompleting usernames.
*/
var g_Playernames = [];
/**
* Sorted list of unique maptitles. Used by mapfilter.
*/
var g_MapNames = [];
/**
* Sorted list of the victory conditions occuring in the replays
*/
var g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
/**
* Directory name of the currently selected replay. Used to restore the selection after changing filters.
*/
var g_SelectedReplayDirectory = "";
/**
* Skip duplicate expensive GUI updates before init is complete.
*/
var g_ReplaysLoaded = false;
/**
* Remember last viewed summary panel and charts.
*/
var g_SummarySelection;
var g_MapCache = new MapCache();
/**
* Initializes globals, loads replays and displays the list.
*/
function init(data)
{
if (!g_Settings)
{
Engine.SwitchGuiPage("page_pregame.xml");
return;
}
g_SummarySelection = data && data.summarySelection;
loadReplays(data && data.replaySelectionData, false);
if (!g_Replays)
{
Engine.SwitchGuiPage("page_pregame.xml");
return;
}
initHotkeyTooltips();
displayReplayList();
}
/**
* Store the list of replays loaded in C++ in g_Replays.
* Check timestamp and compatibility and extract g_Playernames, g_MapNames, g_VictoryConditions.
* Restore selected filters and item.
* @param replaySelectionData - Currently selected filters and item to be restored after the loading.
* @param compareFiles - If true, compares files briefly (which might be slow with optical harddrives),
* otherwise blindly trusts the replay cache.
*/
function loadReplays(replaySelectionData, compareFiles)
{
g_Replays = Engine.GetReplays(compareFiles);
if (!g_Replays)
return;
g_Playernames = [];
for (let replay of g_Replays)
{
let nonAIPlayers = 0;
// Check replay for compatibility
replay.isCompatible = isReplayCompatible(replay);
sanitizeGameAttributes(replay.attribs);
// Extract map names
if (g_MapNames.indexOf(replay.attribs.settings.Name) == -1 && replay.attribs.settings.Name != "")
g_MapNames.push(replay.attribs.settings.Name);
// Extract playernames
for (let playerData of replay.attribs.settings.PlayerData)
{
if (!playerData || playerData.AI)
continue;
// Remove rating from nick
let playername = playerData.Name;
let ratingStart = playername.indexOf(" (");
if (ratingStart != -1)
playername = playername.substr(0, ratingStart);
if (g_Playernames.indexOf(playername) == -1)
g_Playernames.push(playername);
++nonAIPlayers;
}
replay.isMultiplayer = nonAIPlayers > 1;
replay.isRated = nonAIPlayers == 2 &&
replay.attribs.settings.PlayerData.length == 2 &&
replay.attribs.settings.RatingEnabled;
}
g_MapNames.sort();
// Reload filters (since they depend on g_Replays and its derivatives)
initFilters(replaySelectionData && replaySelectionData.filters);
// Restore user selection
if (replaySelectionData)
{
if (replaySelectionData.directory)
g_SelectedReplayDirectory = replaySelectionData.directory;
let replaySelection = Engine.GetGUIObjectByName("replaySelection");
if (replaySelectionData.column)
replaySelection.selected_column = replaySelectionData.column;
if (replaySelectionData.columnOrder)
replaySelection.selected_column_order = replaySelectionData.columnOrder;
}
g_ReplaysLoaded = true;
}
/**
* We may encounter malformed replays.
*/
function sanitizeGameAttributes(attribs)
{
if (!attribs.settings)
attribs.settings = {};
if (!attribs.settings.Size)
attribs.settings.Size = -1;
if (!attribs.settings.Name)
attribs.settings.Name = "";
if (!attribs.settings.PlayerData)
attribs.settings.PlayerData = [];
if (!attribs.settings.PopulationCap)
attribs.settings.PopulationCap = 300;
if (!attribs.mapType)
attribs.mapType = "skirmish";
// Remove gaia
if (attribs.settings.PlayerData.length && attribs.settings.PlayerData[0] == null)
attribs.settings.PlayerData.shift();
attribs.settings.PlayerData.forEach((pData, index) => {
if (!pData.Name)
pData.Name = "";
});
}
function initHotkeyTooltips()
{
Engine.GetGUIObjectByName("playersFilter").tooltip =
translate("Filter replays by typing one or more, partial or complete player names.") +
" " + colorizeAutocompleteHotkey();
let deleteTooltip = colorizeHotkey(
translate("Delete the selected replay using %(hotkey)s."),
"session.savedgames.delete");
if (deleteTooltip)
deleteTooltip += colorizeHotkey(
"\n" + translate("Hold %(hotkey)s to skip the confirmation dialog while deleting."),
"session.savedgames.noconfirmation");
Engine.GetGUIObjectByName("deleteReplayButton").tooltip = deleteTooltip;
}
/**
* Filter g_Replays, fill the GUI list with that data and show the description of the current replay.
*/
function displayReplayList()
{
if (!g_ReplaysLoaded)
return;
// Remember previously selected replay
var replaySelection = Engine.GetGUIObjectByName("replaySelection");
if (replaySelection.selected != -1)
g_SelectedReplayDirectory = g_ReplaysFiltered[replaySelection.selected].directory;
filterReplays();
var list = g_ReplaysFiltered.map(replay => {
let works = replay.isCompatible;
return {
"directories": replay.directory,
"months": compatibilityColor(getReplayDateTime(replay), works),
"popCaps": compatibilityColor(translatePopulationCapacity(replay.attribs.settings.PopulationCap, !!replay.attribs.settings.WorldPopulation), works),
"mapNames": compatibilityColor(getReplayMapName(replay), works),
"mapSizes": compatibilityColor(translateMapSize(replay.attribs.settings.Size), works),
"durations": compatibilityColor(getReplayDuration(replay), works),
"playerNames": compatibilityColor(getReplayPlayernames(replay), works)
};
});
if (list.length)
list = prepareForDropdown(list);
// Push to GUI
replaySelection.selected = -1;
replaySelection.list_months = list.months || [];
replaySelection.list_players = list.playerNames || [];
replaySelection.list_mapName = list.mapNames || [];
replaySelection.list_mapSize = list.mapSizes || [];
replaySelection.list_popCapacity = list.popCaps || [];
replaySelection.list_duration = list.durations || [];
// Change these last, otherwise crash
replaySelection.list = list.directories || [];
replaySelection.list_data = list.directories || [];
replaySelection.selected = replaySelection.list.findIndex(directory => directory == g_SelectedReplayDirectory);
displayReplayDetails();
}
/**
* Shows preview image, description and player text in the right panel.
*/
function displayReplayDetails()
{
let selected = Engine.GetGUIObjectByName("replaySelection").selected;
let replaySelected = selected > -1;
Engine.GetGUIObjectByName("replayInfo").hidden = !replaySelected;
Engine.GetGUIObjectByName("replayInfoEmpty").hidden = replaySelected;
Engine.GetGUIObjectByName("startReplayButton").enabled = replaySelected;
Engine.GetGUIObjectByName("deleteReplayButton").enabled = replaySelected;
Engine.GetGUIObjectByName("replayFilename").hidden = !replaySelected;
Engine.GetGUIObjectByName("summaryButton").hidden = true;
if (!replaySelected)
return;
let replay = g_ReplaysFiltered[selected];
Engine.GetGUIObjectByName("sgMapName").caption = translate(replay.attribs.settings.Name);
Engine.GetGUIObjectByName("sgMapSize").caption = translateMapSize(replay.attribs.settings.Size);
Engine.GetGUIObjectByName("sgMapType").caption = translateMapType(replay.attribs.mapType);
Engine.GetGUIObjectByName("sgVictory").caption = replay.attribs.settings.VictoryConditions.map(victoryConditionName =>
translateVictoryCondition(victoryConditionName)).join(translate(", "));
Engine.GetGUIObjectByName("sgNbPlayers").caption = sprintf(translate("Players: %(numberOfPlayers)s"),
{ "numberOfPlayers": replay.attribs.settings.PlayerData.length });
Engine.GetGUIObjectByName("replayFilename").caption = Engine.GetReplayDirectoryName(replay.directory);
let metadata = Engine.GetReplayMetadata(replay.directory);
Engine.GetGUIObjectByName("sgPlayersNames").caption =
formatPlayerInfo(
replay.attribs.settings.PlayerData,
Engine.GetGUIObjectByName("showSpoiler").checked &&
metadata &&
metadata.playerStates &&
metadata.playerStates.map(pState => pState.state));
- Engine.GetGUIObjectByName("sgMapPreview").sprite = g_MapCache.getMapPreview(replay.attribs.mapType, replay.attribs.map, replay.attribs);
+ Engine.GetGUIObjectByName("sgMapPreview").sprite = g_MapCache.getMapPreview(replay.attribs.mapType, replay.attribs.map, replay.attribs?.mapPreview);
Engine.GetGUIObjectByName("sgMapDescription").caption = g_MapCache.getTranslatedMapDescription(replay.attribs.mapType, replay.attribs.map);
Engine.GetGUIObjectByName("summaryButton").hidden = !Engine.HasReplayMetadata(replay.directory);
}
/**
* Returns a human-readable version of the replay date.
*/
function getReplayDateTime(replay)
{
return Engine.FormatMillisecondsIntoDateStringLocal(replay.attribs.timestamp * 1000, translate("yyyy-MM-dd HH:mm"));
}
/**
* Returns a human-readable list of the playernames of that replay.
*
* @returns {string}
*/
function getReplayPlayernames(replay)
{
return replay.attribs.settings.PlayerData.map(pData => pData.Name).join(", ");
}
/**
* Returns the name of the map of the given replay.
*
* @returns {string}
*/
function getReplayMapName(replay)
{
return translate(replay.attribs.settings.Name);
}
/**
* Returns the month of the given replay in the format "yyyy-MM".
*
* @returns {string}
*/
function getReplayMonth(replay)
{
return Engine.FormatMillisecondsIntoDateStringLocal(replay.attribs.timestamp * 1000, translate("yyyy-MM"));
}
/**
* Returns a human-readable version of the time when the replay started.
*
* @returns {string}
*/
function getReplayDuration(replay)
{
return timeToString(replay.duration * 1000);
}
/**
* True if we can start the given replay with the currently loaded mods.
*/
function isReplayCompatible(replay)
{
return replayHasSameEngineVersion(replay) && hasSameMods(replay.attribs.mods, g_EngineInfo.mods);
}
/**
* True if we can start the given replay with the currently loaded mods.
*/
function replayHasSameEngineVersion(replay)
{
return replay.attribs.engine_version && replay.attribs.engine_version == g_EngineInfo.engine_version;
}
Index: ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js (revision 25076)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js (revision 25077)
@@ -1,277 +1,279 @@
var g_MainMenuItems = [
{
"caption": translate("Learn to Play"),
"tooltip": translate("Learn how to play, start the tutorial, discover the technology trees, and the history behind the civilizations."),
"submenu": [
{
"caption": translate("Manual"),
"tooltip": translate("Open the 0 A.D. Game Manual."),
"onPress": () => {
Engine.PushGuiPage("page_manual.xml");
}
},
{
"caption": translate("Tutorial"),
"tooltip": translate("Start the economic tutorial."),
"onPress": () => {
- Engine.SwitchGuiPage("page_gamesetup.xml", {
- "autostart": true,
+ Engine.SwitchGuiPage("page_autostart.xml", {
"mapType": "scenario",
- "map": "maps/tutorials/starting_economy_walkthrough"
+ "map": "maps/tutorials/starting_economy_walkthrough",
+ "settings": {
+ "CheatsEnabled": true
+ }
});
}
},
{
"caption": translate("Structure Tree"),
"tooltip": colorizeHotkey(translate("%(hotkey)s: View the structure tree of civilizations featured in 0 A.D."), "structree"),
"hotkey": "structree",
"onPress": () => {
let callback = data => {
if (data.nextPage)
Engine.PushGuiPage(data.nextPage, { "civ": data.civ }, callback);
};
Engine.PushGuiPage("page_structree.xml", {}, callback);
},
},
{
"caption": translate("Civilization Overview"),
"tooltip": colorizeHotkey(translate("%(hotkey)s: Learn about the civilizations featured in 0 A.D."), "civinfo"),
"hotkey": "civinfo",
"onPress": () => {
let callback = data => {
if (data.nextPage)
Engine.PushGuiPage(data.nextPage, { "civ": data.civ }, callback);
};
Engine.PushGuiPage("page_civinfo.xml", {}, callback);
}
},
{
"caption": translate("Catafalque Overview"),
"tooltip": translate("Compare the bonuses of catafalques featured in 0 A.D."),
"onPress": () => {
Engine.PushGuiPage("page_catafalque.xml");
}
},
{
"caption": translate("Map Overview"),
"tooltip": translate("View the different maps featured in 0 A.D."),
"onPress": () => {
Engine.PushGuiPage("page_mapbrowser.xml");
},
}
]
},
{
"caption": translate("Continue Campaign"),
"tooltip": translate("Relive history through historical military campaigns."),
"onPress": () => {
try
{
Engine.SwitchGuiPage(CampaignRun.getCurrentRun().getMenuPath());
}
catch(err)
{
error(translate("Error opening campaign run:"));
error(err);
}
},
"enabled": () => CampaignRun.hasCurrentRun()
},
{
"caption": translate("Single-player"),
"tooltip": translate("Start, load, or replay a single-player game."),
"submenu": [
{
"caption": translate("Matches"),
"tooltip": translate("Start a new single-player game."),
"onPress": () => {
Engine.SwitchGuiPage("page_gamesetup.xml");
}
},
{
"caption": translate("Load Game"),
"tooltip": translate("Load a saved game."),
"onPress": () => {
Engine.PushGuiPage("page_loadgame.xml");
}
},
{
"caption": translate("Continue Campaign"),
"tooltip": translate("Relive history through historical military campaigns."),
"onPress": () => {
try
{
Engine.SwitchGuiPage(CampaignRun.getCurrentRun().getMenuPath());
}
catch(err)
{
error(translate("Error opening campaign run:"));
error(err);
}
},
"enabled": () => CampaignRun.hasCurrentRun()
},
{
"caption": translate("New Campaign"),
"tooltip": translate("Relive history through historical military campaigns."),
"onPress": () => {
Engine.SwitchGuiPage("campaigns/setup/page.xml");
}
},
{
"caption": translate("Load Campaign"),
"tooltip": translate("Relive history through historical military campaigns."),
"onPress": () => {
// Switch instead of push, otherwise the 'continue'
// button might remain enabled.
// TODO: find a better solution.
Engine.SwitchGuiPage("campaigns/load_modal/page.xml");
}
},
{
"caption": translate("Replays"),
"tooltip": translate("Playback previous games."),
"onPress": () => {
Engine.SwitchGuiPage("page_replaymenu.xml", {
"replaySelectionData": {
"filters": {
"singleplayer": "Single-player"
}
}
});
}
}
]
},
{
"caption": translate("Multiplayer"),
"tooltip": translate("Fight against one or more human players in a multiplayer game."),
"submenu": [
{
// Translation: Join a game by specifying the host's IP address.
"caption": translate("Join Game"),
"tooltip": translate("Joining an existing multiplayer game."),
"onPress": () => {
Engine.PushGuiPage("page_gamesetup_mp.xml", {
"multiplayerGameType": "join"
});
}
},
{
"caption": translate("Host Game"),
"tooltip": translate("Host a multiplayer game."),
"onPress": () => {
Engine.PushGuiPage("page_gamesetup_mp.xml", {
"multiplayerGameType": "host"
});
}
},
{
"caption": translate("Game Lobby"),
"tooltip":
colorizeHotkey(translate("%(hotkey)s: Launch the multiplayer lobby to join and host publicly visible games and chat with other players."), "lobby") +
(Engine.StartXmppClient ? "" : translate("Launch the multiplayer lobby. \\[DISABLED BY BUILD]")),
"enabled": () => !!Engine.StartXmppClient,
"hotkey": "lobby",
"onPress": () => {
if (Engine.StartXmppClient)
Engine.PushGuiPage("page_prelobby_entrance.xml");
}
},
{
"caption": translate("Replays"),
"tooltip": translate("Playback previous games."),
"onPress": () => {
Engine.SwitchGuiPage("page_replaymenu.xml", {
"replaySelectionData": {
"filters": {
"singleplayer": "Multiplayer"
}
}
});
}
}
]
},
{
"caption": translate("Settings"),
"tooltip": translate("Change game options."),
"submenu": [
{
"caption": translate("Options"),
"tooltip": translate("Adjust game settings."),
"onPress": () => {
Engine.PushGuiPage(
"page_options.xml",
{},
fireConfigChangeHandlers);
}
},
{
"caption": translate("Hotkeys"),
"tooltip": translate("Adjust hotkeys."),
"onPress": () => {
Engine.PushGuiPage("hotkeys/page_hotkeys.xml");
}
},
{
"caption": translate("Language"),
"tooltip": translate("Choose the language of the game."),
"onPress": () => {
Engine.PushGuiPage("page_locale.xml");
}
},
{
"caption": translate("Mod Selection"),
"tooltip": translate("Select and download mods for the game."),
"onPress": () => {
Engine.SwitchGuiPage("page_modmod.xml");
}
},
{
"caption": translate("Welcome Screen"),
"tooltip": translate("Show the Welcome Screen again. Useful if you hid it by mistake."),
"onPress": () => {
Engine.PushGuiPage("page_splashscreen.xml");
}
}
]
},
{
"caption": translate("Scenario Editor"),
"tooltip": translate('Open the Atlas Scenario Editor in a new window. You can run this more reliably by starting the game with the command-line argument "-editor".'),
"onPress": () => {
if (Engine.AtlasIsAvailable())
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D. and open the Scenario Editor?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.RestartInAtlas]);
else
messageBox(
400, 200,
translate("The scenario editor is not available or failed to load. See the game logs for additional information."),
translate("Error"));
}
},
{
"caption": translate("Credits"),
"tooltip": translate("Show the 0 A.D. credits."),
"onPress": () => {
Engine.PushGuiPage("page_credits.xml");
}
},
{
"caption": translate("Exit"),
"tooltip": translate("Exit the game."),
"onPress": () => {
messageBox(
400, 200,
translate("Are you sure you want to quit 0 A.D.?"),
translate("Confirmation"),
[translate("No"), translate("Yes")],
[null, Engine.Exit]);
}
}
];
Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest.json
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest.json (revision 25076)
+++ ps/trunk/binaries/data/mods/public/simulation/data/settings/victory_conditions/conquest.json (revision 25077)
@@ -1,20 +1,17 @@
{
"TranslatedKeys": ["Title", "Description"],
"Data":
{
"Title": "Conquest",
"Description": "Defeat opponents by killing all their units and destroying all their structures.",
"Scripts":
[
"scripts/TriggerHelper.js",
"scripts/ConquestCommon.js",
"scripts/Conquest.js"
],
"Default": true,
- "ChangeOnChecked": {
- "conquest_units": false,
- "conquest_structures": false
- },
+ "DisabledWhenChecked": ["conquest_units", "conquest_structures"],
"GUIOrder": 0
}
}