Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/ai.txt
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/ai.txt (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/ai.txt (nonexistent)
@@ -1 +0,0 @@
-Although reasonably easy for an experienced player, the default AI level is quite challenging for new players yet to master the basic mechanisms of the game. Beginners are advised to start by playing against a lower level AI (Sandbox or Very Easy). Change the AI level by clicking the gear icon next to the player you want to modify in the selection panel above.
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/ai.txt
___________________________________________________________________
Deleted: svn:eol-style
## -1 +0,0 ##
-native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js (revision 23374)
@@ -0,0 +1,78 @@
+PlayerSettingControls.PlayerTeam = class 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;
+ }
+
+ 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()
+ {
+ 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);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.PlayerData[this.playerIndex].Team = itemIdx - 1;
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+PlayerSettingControls.PlayerTeam.prototype.Tooltip =
+ translate("Select player's team.");
+
+PlayerSettingControls.PlayerTeam.prototype.NoTeam =
+ translateWithContext("team", "None");
+
+PlayerSettingControls.PlayerTeam.prototype.NoTeamId = -1;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerTeam.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerName.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerName.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerName.js (revision 23374)
@@ -0,0 +1,129 @@
+// 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
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.playerName = Engine.GetGUIObjectByName("playerName[" + this.playerIndex + "]");
+
+ 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();
+ }
+
+ onPlayerAssignmentsChange()
+ {
+ this.guid = undefined;
+
+ for (let guid in g_PlayerAssignments)
+ if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
+ {
+ this.guid = guid;
+ break;
+ }
+
+ this.rebuild();
+ }
+
+ rebuild()
+ {
+ let name = this.displayedName;
+ if (!name)
+ return;
+
+ if (g_IsNetworked)
+ {
+ let status = this.guid ? g_PlayerAssignments[this.guid].status : this.ReadyTags.length - 1;
+ name = setStringTags(this.displayedName, 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",
+ }
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerName.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LockedTeams.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LockedTeams.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LockedTeams.js (revision 23374)
@@ -0,0 +1,62 @@
+GameSettingControls.LockedTeams = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ !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;
+
+ if (g_GameAttributes.settings.LockTeams === undefined ||
+ g_GameAttributes.settings.RatingEnabled && !g_GameAttributes.settings.LockTeams)
+ {
+ g_GameAttributes.settings.LockTeams = g_IsNetworked;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (!g_GameAttributes.mapType)
+ return;
+
+ this.setChecked(g_GameAttributes.settings.LockTeams);
+
+ this.setEnabled(
+ g_GameAttributes.mapType != "scenario" &&
+ !g_GameAttributes.settings.RatingEnabled);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.LockTeams = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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();
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LockedTeams.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RegicideGarrison.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RegicideGarrison.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RegicideGarrison.js (revision 23374)
@@ -0,0 +1,67 @@
+GameSettingControls.RegicideGarrison = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ this.setEnabled(g_GameAttributes.mapType != "scenario");
+
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.VictoryConditions &&
+ mapData.settings.VictoryConditions.indexOf(this.RegicideName) != -1 &&
+ 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();
+ }
+
+ onGameAttributesChange()
+ {
+ 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();
+ }
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.RegicideGarrison = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RegicideGarrison.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Treasures.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Treasures.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Treasures.js (revision 23374)
@@ -0,0 +1,45 @@
+GameSettingControls.Treasures = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ 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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setChecked(g_GameAttributes.settings.DisableTreasures);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.DisableTreasures = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.Treasures.prototype.TitleCaption =
+ translate("Disable Treasures");
+
+GameSettingControls.Treasures.prototype.Tooltip =
+ translate("Do not add treasures to the map.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Treasures.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/GameSpeed.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/GameSpeed.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/GameSpeed.js (revision 23374)
@@ -0,0 +1,76 @@
+GameSettingControls.GameSpeed = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.previousAllowFastForward = undefined;
+ }
+
+ onMapChange(mapData)
+ {
+ let mapValue = mapData && mapData.gameSpeed || undefined;
+ if (mapValue !== undefined && mapValue != g_GameAttributes.gameSpeed)
+ {
+ g_GameAttributes.gameSpeed = mapValue;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onPlayerAssignmentsChange()
+ {
+ this.update();
+ }
+
+ onGameAttributesChange()
+ {
+ this.update();
+ }
+
+ update()
+ {
+ 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(
+ 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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setSelectedValue(g_GameAttributes.gameSpeed);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.gameSpeed = this.values.Speed[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.GameSpeed.prototype.TitleCaption =
+ translate("Game Speed");
+
+GameSettingControls.GameSpeed.prototype.Tooltip =
+ translate("Select game speed.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/GameSpeed.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayersPanel.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayersPanel.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayersPanel.xml (revision 23374)
@@ -0,0 +1,55 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayersPanel.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Cheats.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Cheats.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Cheats.js (revision 23374)
@@ -0,0 +1,43 @@
+/**
+ * Cheats are always enabled in singleplayer mode, since they are the choice of that one player.
+ */
+GameSettingControls.Cheats = class extends GameSettingControlCheckbox
+{
+ constructor(...args)
+ {
+ super(...args);
+ this.setHidden(!g_IsNetworked);
+ }
+
+ onGameAttributesChange()
+ {
+ 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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setChecked(g_GameAttributes.settings.CheatsEnabled);
+ this.setEnabled(!g_GameAttributes.settings.RatingEnabled);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.CheatsEnabled =
+ !g_IsNetworked ||
+ checked && !g_GameAttributes.settings.RatingEnabled;
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.Cheats.prototype.TitleCaption =
+ translate("Cheats");
+
+GameSettingControls.Cheats.prototype.Tooltip =
+ translate("Toggle the usability of cheats.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Cheats.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LastManStanding.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LastManStanding.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LastManStanding.js (revision 23374)
@@ -0,0 +1,61 @@
+GameSettingControls.LastManStanding = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ !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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (!g_GameAttributes.mapType)
+ return;
+
+ // Always display this, so that players are aware that there is this gamemode
+ this.setChecked(!!g_GameAttributes.settings.LastManStanding);
+ this.setEnabled(g_GameAttributes.mapType != "scenario" && !g_GameAttributes.settings.LockTeams);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.LastManStanding = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/LastManStanding.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Rating.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Rating.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Rating.js (revision 23374)
@@ -0,0 +1,54 @@
+GameSettingControls.Rating = class extends GameSettingControlCheckbox
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.hasXmppClient = Engine.HasXmppClient();
+ this.available = false;
+ }
+
+ onGameAttributesChange()
+ {
+ 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);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.RatingEnabled = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Rating.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Spies.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Spies.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Spies.js (revision 23374)
@@ -0,0 +1,45 @@
+GameSettingControls.Spies = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.DisableSpies;
+
+ 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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setChecked(g_GameAttributes.settings.DisableSpies);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.DisableSpies = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.Spies.prototype.TitleCaption =
+ translate("Disable Spies");
+
+GameSettingControls.Spies.prototype.Tooltip =
+ translate("Disable spies during the game.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Spies.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Ceasefire.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Ceasefire.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Ceasefire.js (revision 23374)
@@ -0,0 +1,55 @@
+GameSettingControls.Ceasefire = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = prepareForDropdown(g_Settings.Ceasefire);
+
+ this.dropdown.list = this.values.Title;
+ this.dropdown.list_data = this.values.Duration;
+ }
+
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.Ceasefire || undefined;
+
+ if (mapValue !== undefined && mapValue != g_GameAttributes.settings.Ceasefire)
+ {
+ g_GameAttributes.settings.Ceasefire = mapValue;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+
+ this.setEnabled(g_GameAttributes.mapType != "scenario");
+ }
+
+ onGameAttributesChange()
+ {
+ if (g_GameAttributes.settings.Ceasefire == undefined)
+ {
+ g_GameAttributes.settings.Ceasefire = this.values.Default;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setSelectedValue(g_GameAttributes.settings.Ceasefire);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.Ceasefire = this.values.Duration[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.Ceasefire.prototype.TitleCaption =
+ translate("Ceasefire");
+
+GameSettingControls.Ceasefire.prototype.Tooltip =
+ translate("Set time where no attacks are possible.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Ceasefire.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/config/default.cfg
===================================================================
--- ps/trunk/binaries/data/config/default.cfg (revision 23373)
+++ ps/trunk/binaries/data/config/default.cfg (revision 23374)
@@ -1,524 +1,524 @@
; Global Configuration Settings
;
; **************************************************************
; * DO NOT EDIT THIS FILE if you want personal customisations: *
; * create a text file called "local.cfg" instead, and copy *
; * the lines from this file that you want to change. *
; * *
; * If a setting is part of a section (for instance [hotkey]) *
; * you need to append the section name at the beginning of *
; * your custom line (for instance you need to write *
; * "hotkey.pause = Space" if you want to change the pausing *
; * hotkey to the spacebar). *
; * *
; * On Linux, create: *
; * $XDG_CONFIG_HOME/0ad/config/local.cfg *
; * (Note: $XDG_CONFIG_HOME defaults to ~/.config) *
; * *
; * On OS X, create: *
; * ~/Library/Application\ Support/0ad/config/local.cfg *
; * *
; * On Windows, create: *
; * %appdata%\0ad\config\local.cfg *
; * *
; **************************************************************
; Enable/disable windowed mode by default. (Use Alt+Enter to toggle in the game.)
windowed = false
; Show detailed tooltips (Unit stats)
showdetailedtooltips = false
; Pause the game on window focus loss (Only applicable to single player mode)
pauseonfocusloss = true
; Persist settings after leaving the game setup screen
persistmatchsettings = true
; Default player name to use in multiplayer
; playername = "anonymous"
; Default server name or IP to use in multiplayer
multiplayerserver = "127.0.0.1"
; Force a particular resolution. (If these are 0, the default is
; to keep the current desktop resolution in fullscreen mode or to
; use 1024x768 in windowed mode.)
xres = 0
yres = 0
; Force a non-standard bit depth (if 0 then use the current desktop bit depth)
bpp = 0
; Preferred display (for multidisplay setups, only works with SDL 2.0)
display = 0
; Emulate right-click with Ctrl+Click on Mac mice
macmouse = false
; System settings:
; if false, actors won't be rendered but anything entity will be.
renderactors = true
watereffects=true ; When disabled, force usage of the fixed pipeline water. This is faster, but really, really ugly.
waterfancyeffects = false
waterrealdepth = true
waterrefraction = true
waterreflection = true
shadowsonwater = false
shadows = true
shadowquality = 0 ; Shadow map resolution. (-2 - Very Low, -1 - Low, 0 - Medium, 1 - High, 2 - Very High)
; High values can crash the game when using a graphics card with low memory!
shadowpcf = true
shadowsfixed = false ; When enabled shadows are rendered only on the
shadowsfixeddistance = 300.0 ; fixed distance and without swimming effect.
vsync = false
particles = true
fog = true
silhouettes = true
showsky = true
nos3tc = false
noautomipmap = true
novbo = false
noframebufferobject = false
; Disable hardware cursors
nohwcursor = false
; Linux only: Set the driconf force_s3tc_enable option at startup,
; for compressed texture support
force_s3tc_enable = true
; Specify the render path. This can be one of:
; default Automatically select one of the below, depending on system capabilities
; fixed Only use OpenGL fixed function pipeline
; shader Use vertex/fragment shaders for transform and lighting where possible
; Using 'fixed' instead of 'default' may work around some graphics-related problems,
; but will reduce performance and features when a modern graphics card is available.
renderpath = default
;;;;; EXPERIMENTAL ;;;;;
; Prefer GLSL shaders over ARB shaders. Allows fancier graphical effects.
preferglsl = false
; Experimental probably-non-working GPU skinning support; requires preferglsl; use at own risk
gpuskinning = false
; Use smooth LOS interpolation
smoothlos = false
; Use screen-space postprocessing filters (HDR, bloom, DOF, etc). Incompatible with fixed renderpath.
postproc = false
; Quality level of shader effects (set to 10 to display all effects)
materialmgr.quality = 2.0
; Maximum distance to display parallax effect. Set to 0 to disable parallax.
materialmgr.PARALLAX_DIST.max = 150
; Maximum distance to display high quality parallax effect.
materialmgr.PARALLAX_HQ_DIST.max = 75
; Maximum distance to display very high quality parallax effect. Set to 30 to enable.
materialmgr.PARALLAX_VHQ_DIST.max = 0
;;;;;;;;;;;;;;;;;;;;;;;;
; Replace alpha-blending with alpha-testing, for performance experiments
forcealphatest = false
; Color of the sky (in "r g b" format)
skycolor = "0 0 0"
[adaptivefps]
session = 60 ; Throttle FPS in running games (prevents 100% CPU workload).
-menu = 30 ; Throttle FPS in menus only.
+menu = 60 ; Throttle FPS in menus only.
[hotkey]
; Each one of the specified keys will trigger the action on the left
; for multiple-key combinations, separate keys with '+'.
; See keys.txt for the list of key names.
; > SYSTEM SETTINGS
exit = "Ctrl+Break", "Super+Q" ; Exit to desktop
cancel = Escape ; Close or cancel the current dialog box/popup
confirm = Return ; Confirm the current command
pause = Pause ; Pause/unpause game
screenshot = F2 ; Take PNG screenshot
bigscreenshot = "Shift+F2" ; Take large BMP screenshot
togglefullscreen = "Alt+Return" ; Toggle fullscreen/windowed mode
screenshot.watermark = "Alt+K" ; Toggle product/company watermark for official screenshots
wireframe = "Alt+Shift+W" ; Toggle wireframe mode
silhouettes = "Alt+Shift+S" ; Toggle unit silhouettes
showsky = "Alt+Z" ; Toggle sky
; > DIALOG HOTKEYS
summary = "Ctrl+Tab" ; Toggle in-game summary
lobby = "Alt+L" ; Show the multiplayer lobby in a dialog window.
structree = "Alt+Shift+T" ; Show structure tree
civinfo = "Alt+Shift+H" ; Show civilization info
; > CLIPBOARD CONTROLS
copy = "Ctrl+C" ; Copy to clipboard
paste = "Ctrl+V" ; Paste from clipboard
cut = "Ctrl+X" ; Cut selected text and copy to the clipboard
; > CONSOLE SETTINGS
console.toggle = BackQuote, F9 ; Open/close console
; > OVERLAY KEYS
fps.toggle = "Alt+F" ; Toggle frame counter
realtime.toggle = "Alt+T" ; Toggle current display of computer time
session.devcommands.toggle = "Alt+D" ; Toggle developer commands panel
timeelapsedcounter.toggle = "F12" ; Toggle time elapsed counter
ceasefirecounter.toggle = unused ; Toggle ceasefire counter
session.showstatusbars = Tab ; Toggle display of status bars
session.highlightguarding = PgDn ; Toggle highlight of guarding units
session.highlightguarded = PgUp ; Toggle highlight of guarded units
session.toggleattackrange = "Alt+C" ; Toggle display of attack range overlays of selected defensive structures
session.toggleaurasrange = "Alt+V" ; Toggle display of aura range overlays of selected units and structures
session.togglehealrange = "Alt+B" ; Toggle display of heal range overlays of selected units
session.diplomacycolors = "Alt+X" ; Toggle diplomacy colors
; > HOTKEYS ONLY
chat = Return ; Toggle chat window
teamchat = "T" ; Toggle chat window in team chat mode
privatechat = "L" ; Toggle chat window and select the previous private chat partner
; > QUICKSAVE
quicksave = "Shift+F5"
quickload = "Shift+F8"
[hotkey.camera]
reset = "R" ; Reset camera rotation to default.
follow = "F" ; Follow the first unit in the selection
rallypointfocus = unused ; Focus the camera on the rally point of the selected building
zoom.in = Plus, Equals, NumPlus ; Zoom camera in (continuous control)
zoom.out = Minus, NumMinus ; Zoom camera out (continuous control)
zoom.wheel.in = WheelUp ; Zoom camera in (stepped control)
zoom.wheel.out = WheelDown ; Zoom camera out (stepped control)
rotate.up = "Ctrl+UpArrow", "Ctrl+W" ; Rotate camera to look upwards
rotate.down = "Ctrl+DownArrow", "Ctrl+S" ; Rotate camera to look downwards
rotate.cw = "Ctrl+LeftArrow", "Ctrl+A", Q ; Rotate camera clockwise around terrain
rotate.ccw = "Ctrl+RightArrow", "Ctrl+D", E ; Rotate camera anticlockwise around terrain
rotate.wheel.cw = "Shift+WheelUp", MouseX1 ; Rotate camera clockwise around terrain (stepped control)
rotate.wheel.ccw = "Shift+WheelDown", MouseX2 ; Rotate camera anticlockwise around terrain (stepped control)
pan = MouseMiddle ; Enable scrolling by moving mouse
left = A, LeftArrow ; Scroll or rotate left
right = D, RightArrow ; Scroll or rotate right
up = W, UpArrow ; Scroll or rotate up/forwards
down = S, DownArrow ; Scroll or rotate down/backwards
scroll.speed.increase = "Ctrl+Shift+S" ; Increase scroll speed
scroll.speed.decrease = "Ctrl+Alt+S" ; Decrease scroll speed
rotate.speed.increase = "Ctrl+Shift+R" ; Increase rotation speed
rotate.speed.decrease = "Ctrl+Alt+R" ; Decrease rotation speed
zoom.speed.increase = "Ctrl+Shift+Z" ; Increase zoom speed
zoom.speed.decrease = "Ctrl+Alt+Z" ; Decrease zoom speed
[hotkey.camera.jump]
1 = F5 ; Jump to position N
2 = F6
3 = F7
4 = F8
;5 =
;6 =
;7 =
;8 =
;9 =
;10 =
[hotkey.camera.jump.set]
1 = "Ctrl+F5" ; Set jump position N
2 = "Ctrl+F6"
3 = "Ctrl+F7"
4 = "Ctrl+F8"
;5 =
;6 =
;7 =
;8 =
;9 =
;10 =
[hotkey.profile]
toggle = "F11" ; Enable/disable real-time profiler
save = "Shift+F11" ; Save current profiler data to logs/profile.txt
[hotkey.profile2]
toggle = "Ctrl+F11" ; Enable/disable HTTP/GPU modes for new profiler
[hotkey.selection]
add = Shift ; Add units to selection
militaryonly = Alt ; Add only military units to the selection
nonmilitaryonly = "Alt+Y" ; Add only non-military units to the selection
idleonly = "I" ; Select only idle units
woundedonly = "O" ; Select only wounded units
remove = Ctrl ; Remove units from selection
cancel = Esc ; Un-select all units and cancel building placement
idleworker = Period ; Select next idle worker
idlewarrior = ForwardSlash ; Select next idle warrior
idleunit = BackSlash ; Select next idle unit
offscreen = Alt ; Include offscreen units in selection
[hotkey.selection.group.add]
0 = "Shift+0"
1 = "Shift+1"
2 = "Shift+2"
3 = "Shift+3"
4 = "Shift+4"
5 = "Shift+5"
6 = "Shift+6"
7 = "Shift+7"
8 = "Shift+8"
9 = "Shift+9"
[hotkey.selection.group.save]
0 = "Ctrl+0"
1 = "Ctrl+1"
2 = "Ctrl+2"
3 = "Ctrl+3"
4 = "Ctrl+4"
5 = "Ctrl+5"
6 = "Ctrl+6"
7 = "Ctrl+7"
8 = "Ctrl+8"
9 = "Ctrl+9"
[hotkey.selection.group.select]
0 = 0
1 = 1
2 = 2
3 = 3
4 = 4
5 = 5
6 = 6
7 = 7
8 = 8
9 = 9
[hotkey.session]
kill = Delete ; Destroy selected units
stop = "H" ; Stop the current action
backtowork = "Y" ; The unit will go back to work
unload = "U" ; Unload garrisoned units when a building/mechanical unit is selected
move = unused ; Modifier to move to a point instead of another action (e.g. gather)
attack = Ctrl ; Modifier to attack instead of another action (e.g. capture)
attackmove = Ctrl ; Modifier to attackmove when clicking on a point
attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when clicking on a point (should contain the attackmove keys)
garrison = Ctrl ; Modifier to garrison when clicking on building
autorallypoint = Ctrl ; Modifier to set the rally point on the building itself
guard = "G" ; Modifier to escort/guard when clicking on unit/building
patrol = "P" ; Modifier to patrol a unit
repair = "J" ; Modifier to repair when clicking on building/mechanical unit
queue = Shift ; Modifier to queue unit orders instead of replacing
orderone = Alt ; Modifier to order only one entity in selection.
batchtrain = Shift ; Modifier to train units in batches
massbarter = Shift ; Modifier to barter bunch of resources
masstribute = Shift ; Modifier to tribute bunch of resources
noconfirmation = Shift ; Do not ask confirmation when deleting a building/unit
fulltradeswap = Shift ; Modifier to put the desired trade resource to 100%
unloadtype = Shift ; Modifier to unload all units of type
deselectgroup = Ctrl ; Modifier to deselect units when clicking group icon, instead of selecting
rotate.cw = RightBracket ; Rotate building placement preview clockwise
rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise
snaptoedges = Ctrl ; Modifier to align new structures with nearby existing structure
[hotkey.session.gui]
toggle = "Alt+G" ; Toggle visibility of session GUI
menu.toggle = "F10" ; Toggle in-game menu
diplomacy.toggle = "Ctrl+H" ; Toggle in-game diplomacy page
barter.toggle = "Ctrl+B" ; Toggle in-game barter/trade page
objectives.toggle = "Ctrl+O" ; Toggle in-game objectives page
tutorial.toggle = "Ctrl+P" ; Toggle in-game tutorial panel
[hotkey.session.savedgames]
delete = Delete ; Delete the selected saved game asking confirmation
noconfirmation = Shift ; Do not ask confirmation when deleting a game
[hotkey.session.queueunit] ; > UNIT TRAINING
1 = "Z" ; add first unit type to queue
2 = "X" ; add second unit type to queue
3 = "C" ; add third unit type to queue
4 = "V" ; add fourth unit type to queue
5 = "B" ; add fivth unit type to queue
6 = "N" ; add sixth unit type to queue
7 = "M" ; add seventh unit type to queue
8 = Comma ; add eighth unit type to queue
[hotkey.session.timewarp]
fastforward = Space ; If timewarp mode enabled, speed up the game
rewind = Backspace ; If timewarp mode enabled, go back to earlier point in the game
[hotkey.tab]
next = "Tab", "Alt+S" ; Show the next tab
prev = "Shift+Tab", "Alt+W" ; Show the previous tab
[hotkey.text] ; > GUI TEXTBOX HOTKEYS
delete.left = "Ctrl+Backspace" ; Delete word to the left of cursor
delete.right = "Ctrl+Del" ; Delete word to the right of cursor
move.left = "Ctrl+LeftArrow" ; Move cursor to start of word to the left of cursor
move.right = "Ctrl+RightArrow" ; Move cursor to start of word to the right of cursor
[gui]
cursorblinkrate = 0.5 ; Cursor blink rate in seconds (0.0 to disable blinking)
scale = 1.0 ; GUI scaling factor, for improved compatibility with 4K displays
[gui.gamesetup]
enabletips = true ; Enable/Disable tips during gamesetup (for newcomers)
assignplayers = everyone ; Whether to assign joining clients to free playerslots. Possible values: everyone, buddies, disabled.
aidifficulty = 3 ; Difficulty level, from 0 (easiest) to 5 (hardest)
aibehavior = "random" ; Default behavior of the AI (random, balanced, aggressive or defensive)
settingsslide = true ; Enable/Disable settings panel slide
[gui.loadingscreen]
progressdescription = false ; Whether to display the progress percent or a textual description
[gui.session]
camerajump.threshold = 40 ; How close do we have to be to the actual location in order to jump back to the previous one?
timeelapsedcounter = false ; Show the game duration in the top right corner
ceasefirecounter = false ; Show the remaining ceasefire time in the top right corner
batchtrainingsize = 5 ; Number of units to be trained per batch by default (when pressing the hotkey)
scrollbatchratio = 1 ; Number of times you have to scroll to increase/decrease the batchsize by 1
woundedunithotkeythreshold = 33 ; The wounded unit hotkey considers the selected units as wounded if their health percentage falls below this number
attackrange = true ; Display attack range overlays of selected defensive structures
aurasrange = true ; Display aura range overlays of selected units and structures
healrange = true ; Display heal range overlays of selected units
rankabovestatusbar = true ; Show rank icons above status bars
experiencestatusbar = true ; Show an experience status bar above each selected unit
respoptooltipsort = 0 ; Sorting players in the resources and population tooltip by value (0 - no sort, -1 - ascending, 1 - descending)
snaptoedgesdistancethreshold = 15 ; On which distance we don't snap to edges
[gui.session.minimap]
blinkduration = 1.7 ; The blink duration while pinging
pingduration = 50.0 ; The duration for which an entity will be pinged after an attack notification
[gui.session.notifications]
attack = true ; Show a chat notification if you are attacked by another player
tribute = true ; Show a chat notification if an ally tributes resources to another team member if teams are locked, and all tributes in observer mode
barter = true ; Show a chat notification to observers when a player bartered resources
phase = completed ; Show a chat notification if you or an ally have started, aborted or completed a new phase, and phases of all players in observer mode. Possible values: none, completed, all.
[gui.splashscreen]
enable = true ; Enable/disable the splashscreen
version = 0 ; Splashscreen version (date of last modification). By default, 0 to force splashscreen to appear at first launch
[gui.session.diplomacycolors]
self = "21 55 149" ; Color of your units when diplomacy colors are enabled
ally = "86 180 31" ; Color of allies when diplomacy colors are enabled
neutral = "231 200 5" ; Color of neutral players when diplomacy colors are enabled
enemy = "150 20 20" ; Color of enemies when diplomacy colors are enabled
[joystick] ; EXPERIMENTAL: joystick/gamepad settings
enable = false
deadzone = 8192
[joystick.camera]
pan.x = 0
pan.y = 1
rotate.x = 3
rotate.y = 2
zoom.in = 5
zoom.out = 4
[chat]
timestamp = true ; Show at which time chat messages have been sent
[chat.session]
extended = true ; Whether to display the chat history
[lobby]
history = 0 ; Number of past messages to display on join
room = "arena24" ; Default MUC room to join
server = "lobby.wildfiregames.com" ; Address of lobby server
tls = true ; Whether to use TLS encryption when connecting to the server.
verify_certificate = false ; Whether to reject connecting to the lobby if the TLS certificate is invalid (TODO: wait for Gloox GnuTLS trust implementation to be fixed)
terms_url = "https://trac.wildfiregames.com/browser/ps/trunk/binaries/data/mods/public/gui/prelobby/common/terms/"; Allows the user to save the text and print the terms
terms_of_service = "0" ; Version (hash) of the Terms of Service that the user has accepted
terms_of_use = "0" ; Version (hash) of the Terms of Use that the user has accepted
privacy_policy = "0" ; Version (hash) of the Privacy Policy that the user has accepted
xpartamupp = "wfgbot24" ; Name of the server-side XMPP-account that manage games
echelon = "echelon24" ; Name of the server-side XMPP-account that manages ratings
buddies = "," ; Comma separated list of playernames that the current user has marked as buddies
rememberpassword = true ; Whether to store the encrypted password in the user config
[lobby.columns]
gamerating = false ; Show the average rating of the participating players in a column of the gamelist
[lobby.stun]
enabled = true ; The STUN protocol allows hosting games without configuring the firewall and router.
; If STUN is disabled, the game relies on direct connection, UPnP and port forwarding.
server = "lobby.wildfiregames.com" ; Address of the STUN server.
port = 3478 ; Port of the STUN server.
delay = 200 ; Duration in milliseconds that is waited between STUN messages.
; Smaller numbers speed up joins but also become less stable.
[mod]
enabledmods = "mod public"
[modio]
public_key = "RWTsHxQMrRq4xwHisyBa2rNQfAedcINzbTT83jeX4/ZcfVxqLfWB4y8w" ; Public key corresponding to the private key valid mods are signed with
disclaimer = "0" ; Version (hash) of the Disclaimer that the user has accepted
[modio.v1]
baseurl = "https://api.mod.io/v1"
api_key = "23df258a71711ea6e4b50893acc1ba55"
name_id = "0ad"
[network]
duplicateplayernames = false ; Rename joining player to "User (2)" if "User" is already connected, otherwise prohibit join.
lateobservers = everyone ; Allow observers to join the game after it started. Possible values: everyone, buddies, disabled.
observerlimit = 8 ; Prevent further observer joins in running games if this limit is reached
gamestarttimeout = 60000 ; Don't disconnect clients timing out in the loading screen and rejoin process before exceeding this timeout.
[overlay]
fps = "false" ; Show frames per second in top right corner
realtime = "false" ; Show current system time in top right corner
netwarnings = "true" ; Show warnings if the network connection is bad
[profiler2]
autoenable = false ; Enable HTTP server output at startup (default off for security/performance)
gpu.arb.enable = true ; Allow GL_ARB_timer_query timing mode when available
gpu.ext.enable = true ; Allow GL_EXT_timer_query timing mode when available
gpu.intel.enable = true ; Allow GL_INTEL_performance_queries timing mode when available
[sound]
mastergain = 0.9
musicgain = 0.2
ambientgain = 0.6
actiongain = 0.7
uigain = 0.7
[sound.notify]
nick = true ; Play a sound when someone mentions your name in the lobby or game
gamesetup.join = false ; Play a sound when a new client joins the game setup
[tinygettext]
debug = false ; Print error messages each time a translation for an English string is not found.
[userreport] ; Opt-in online user reporting system
url_upload = "https://feedback.wildfiregames.com/report/upload/v1/" ; URL where UserReports are uploaded to
url_publication = "https://feedback.wildfiregames.com/" ; URL where UserReports were analyzed and published
url_terms = "https://trac.wildfiregames.com/browser/ps/trunk/binaries/data/mods/public/gui/userreport/Terms_and_Conditions.txt"; Allows the user to save the text and print the terms
terms = "0" ; Version (hash) of the UserReporter Terms that the user has accepted
[view] ; Camera control settings
scroll.speed = 120.0
scroll.speed.modifier = 1.05 ; Multiplier for changing scroll speed
rotate.x.speed = 1.2
rotate.x.min = 28.0
rotate.x.max = 60.0
rotate.x.default = 35.0
rotate.y.speed = 2.0
rotate.y.speed.wheel = 0.45
rotate.y.default = 0.0
rotate.speed.modifier = 1.05 ; Multiplier for changing rotation speed
drag.speed = 0.5
zoom.speed = 256.0
zoom.speed.wheel = 32.0
zoom.min = 50.0
zoom.max = 200.0
zoom.default = 120.0
zoom.speed.modifier = 1.05 ; Multiplier for changing zoom speed
pos.smoothness = 0.1
zoom.smoothness = 0.4
rotate.x.smoothness = 0.5
rotate.y.smoothness = 0.3
near = 2.0 ; Near plane distance
far = 4096.0 ; Far plane distance
fov = 45.0 ; Field of view (degrees), lower is narrow, higher is wide
height.smoothness = 0.5
height.min = 16
Index: ps/trunk/binaries/data/mods/mod/gui/common/modern/sprites.xml
===================================================================
--- ps/trunk/binaries/data/mods/mod/gui/common/modern/sprites.xml (revision 23373)
+++ ps/trunk/binaries/data/mods/mod/gui/common/modern/sprites.xml (revision 23374)
@@ -1,678 +1,672 @@
-
+
-
-
-
Index: ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js (revision 23374)
@@ -1,80 +1,82 @@
/**
* Is this user in control of game settings (i.e. is a network server, or offline player).
*/
const g_IsController = !Engine.HasNetClient() || Engine.HasNetServer();
var g_PlayerSlot;
var g_AIDescriptions = [{
"id": "",
"data": {
"name": translateWithContext("ai", "None"),
"description": translate("AI will be disabled for this player.")
}
}].concat(g_Settings.AIDescriptions);
var g_AIControls = {
"aiSelection": {
"labels": g_AIDescriptions.map(ai => ai.data.name),
"selected": settings => g_AIDescriptions.findIndex(ai => ai.id == settings.id)
},
"aiDifficulty": {
"labels": prepareForDropdown(g_Settings.AIDifficulties).Title,
"selected": settings => settings.difficulty
},
"aiBehavior": {
"labels": prepareForDropdown(g_Settings.AIBehaviors).Title,
"selected": settings => g_Settings.AIBehaviors.findIndex(b => b.Name == settings.behavior)
}
};
function init(settings)
{
// Remember the player ID that we change the AI settings for
g_PlayerSlot = settings.playerSlot;
+ let enabled = g_IsController && !settings.fixed;
+
for (let name in g_AIControls)
{
let control = Engine.GetGUIObjectByName(name);
control.list = g_AIControls[name].labels;
control.selected = g_AIControls[name].selected(settings);
- control.hidden = !g_IsController;
+ control.hidden = !enabled;
let label = Engine.GetGUIObjectByName(name + "Text");
label.caption = control.list[control.selected];
- label.hidden = g_IsController;
+ label.hidden = enabled;
}
checkBehavior();
}
function selectAI(idx)
{
Engine.GetGUIObjectByName("aiDescription").caption = g_AIDescriptions[idx].data.description;
}
/** Behavior choice does not apply for Sandbox level */
function checkBehavior()
{
if (g_Settings.AIDifficulties[Engine.GetGUIObjectByName("aiDifficulty").selected].Name != "sandbox")
{
Engine.GetGUIObjectByName("aiBehavior").enabled = true;
return;
}
let aiBehavior = Engine.GetGUIObjectByName("aiBehavior");
aiBehavior.enabled = false;
aiBehavior.selected = g_Settings.AIBehaviors.findIndex(b => b.Name == "balanced");
}
function returnAI(save = true)
{
let idx = Engine.GetGUIObjectByName("aiSelection").selected;
Engine.PopGuiPage({
"save": save,
"id": g_AIDescriptions[idx].id,
"name": g_AIDescriptions[idx].data.name,
"difficulty": Engine.GetGUIObjectByName("aiDifficulty").selected,
"behavior": g_Settings.AIBehaviors[Engine.GetGUIObjectByName("aiBehavior").selected].Name,
"playerSlot": g_PlayerSlot
});
}
Index: ps/trunk/binaries/data/mods/public/gui/common/MapCache.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/MapCache.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/common/MapCache.js (revision 23374)
@@ -0,0 +1,100 @@
+/**
+ * This class obtains, caches and provides the gamesettings from map XML and JSON files.
+ */
+class MapCache
+{
+ constructor()
+ {
+ this.cache = {};
+ }
+
+ 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)
+ {
+ let mapData = this.getMapData(mapType, mapPath);
+
+ let biomePreviewFile =
+ basename(mapPath) + "_" +
+ basename(gameAttributes && gameAttributes.settings.Biome || "") + ".png";
+
+ let biomePreview = Engine.TextureExists(
+ this.TexturesPath + this.PreviewsPath + biomePreviewFile) && biomePreviewFile;
+
+ let filename =
+ biomePreview ?
+ biomePreview :
+ mapData && mapData.settings && mapData.settings.Preview ?
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/common/MapCache.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js (revision 23374)
@@ -1,237 +1,232 @@
/**
* Used for acoustic GUI notifications.
* Define the soundfile paths and specific time thresholds (avoid spam).
* And store the timestamp of last interaction for each notification.
*/
var g_SoundNotifications = {
"nick": { "soundfile": "audio/interface/ui/chat_alert.ogg", "threshold": 3000 },
"gamesetup.join": { "soundfile": "audio/interface/ui/gamesetup_join.ogg", "threshold": 0 }
};
/**
* These events are fired when the user has closed the options page.
* The handlers are provided a Set storing which config values have changed.
* TODO: This should become a GUI event sent by the engine.
*/
var g_ConfigChangeHandlers = new Set();
function registerConfigChangeHandler(handler)
{
g_ConfigChangeHandlers.add(handler);
}
/**
* @param changes - a Set of config names
*/
function fireConfigChangeHandlers(changes)
{
for (let handler of g_ConfigChangeHandlers)
handler(changes);
}
/**
* Returns translated history and gameplay data of all civs, optionally including a mock gaia civ.
*/
function loadCivData(selectableOnly, gaia)
{
let civData = loadCivFiles(selectableOnly);
translateObjectKeys(civData, ["Name", "Description", "History", "Special"]);
if (gaia)
civData.gaia = { "Code": "gaia", "Name": translate("Gaia") };
return deepfreeze(civData);
}
// A sorting function for arrays of objects with 'name' properties, ignoring case
function sortNameIgnoreCase(x, y)
{
let lowerX = x.name.toLowerCase();
let lowerY = y.name.toLowerCase();
if (lowerX < lowerY)
return -1;
if (lowerX > lowerY)
return 1;
return 0;
}
/**
* Escape tag start and escape characters, so users cannot use special formatting.
*/
function escapeText(text)
{
return text.replace(/\\/g, "\\\\").replace(/\[/g, "\\[");
}
function unescapeText(text)
{
return text.replace(/\\\\/g, "\\").replace(/\\\[/g, "\[");
}
/**
* Merge players by team to remove duplicate Team entries, thus reducing the packet size of the lobby report.
*/
function playerDataToStringifiedTeamList(playerData)
{
let teamList = {};
for (let pData of playerData)
{
let team = pData.Team === undefined ? -1 : pData.Team;
if (!teamList[team])
teamList[team] = [];
teamList[team].push(pData);
delete teamList[team].Team;
}
return escapeText(JSON.stringify(teamList));
}
function stringifiedTeamListToPlayerData(stringifiedTeamList)
{
let teamList = {};
try
{
teamList = JSON.parse(unescapeText(stringifiedTeamList));
}
catch (e)
{
// Ignore invalid input from remote users
return [];
}
let playerData = [];
for (let team in teamList)
for (let pData of teamList[team])
{
pData.Team = team;
playerData.push(pData);
}
return playerData;
}
-function translateMapTitle(mapTitle)
-{
- return mapTitle == "random" ? translateWithContext("map selection", "Random") : translate(mapTitle);
-}
-
function removeDupes(array)
{
// loop backwards to make splice operations cheaper
let i = array.length;
while (i--)
if (array.indexOf(array[i]) != i)
array.splice(i, 1);
}
function singleplayerName()
{
return Engine.ConfigDB_GetValue("user", "playername.singleplayer") || Engine.GetSystemUsername();
}
function multiplayerName()
{
return Engine.ConfigDB_GetValue("user", "playername.multiplayer") || Engine.GetSystemUsername();
}
function tryAutoComplete(text, autoCompleteList)
{
if (!text.length)
return text;
var wordSplit = text.split(/\s/g);
if (!wordSplit.length)
return text;
var lastWord = wordSplit.pop();
if (!lastWord.length)
return text;
for (var word of autoCompleteList)
{
if (word.toLowerCase().indexOf(lastWord.toLowerCase()) != 0)
continue;
text = wordSplit.join(" ");
if (text.length > 0)
text += " ";
text += word;
break;
}
return text;
}
function autoCompleteText(guiObject, words)
{
let text = guiObject.caption;
if (!text.length)
return;
let bufferPosition = guiObject.buffer_position;
let textTillBufferPosition = text.substring(0, bufferPosition);
let newText = tryAutoComplete(textTillBufferPosition, words);
guiObject.caption = newText + text.substring(bufferPosition);
guiObject.buffer_position = bufferPosition + (newText.length - textTillBufferPosition.length);
}
/**
* Manage acoustic GUI notifications.
*
* @param {string} type - Notification type.
*/
function soundNotification(type)
{
if (Engine.ConfigDB_GetValue("user", "sound.notify." + type) != "true")
return;
let notificationType = g_SoundNotifications[type];
let timeNow = Date.now();
if (!notificationType.lastInteractionTime || timeNow > notificationType.lastInteractionTime + notificationType.threshold)
Engine.PlayUISound(notificationType.soundfile, false);
notificationType.lastInteractionTime = timeNow;
}
/**
* Horizontally spaces objects within a parent
*
* @param margin The gap, in px, between the objects
*/
function horizontallySpaceObjects(parentName, margin = 0)
{
let objects = Engine.GetGUIObjectByName(parentName).children;
for (let i = 0; i < objects.length; ++i)
{
let size = objects[i].size;
let width = size.right - size.left;
size.left = i * (width + margin) + margin;
size.right = (i + 1) * (width + margin);
objects[i].size = size;
}
}
/**
* Hide all children after a certain index
*/
function hideRemaining(parentName, start = 0)
{
let objects = Engine.GetGUIObjectByName(parentName).children;
for (let i = start; i < objects.length; ++i)
objects[i].hidden = true;
}
function getBuildString()
{
return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), {
"buildDate": Engine.GetBuildDate(),
"revision": Engine.GetBuildRevision()
});
}
Index: ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/common/gamedescription.js (revision 23374)
@@ -1,477 +1,432 @@
/**
* 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 = '•';
-var g_MapPreviewPath = "session/icons/mappreview/";
-
-/**
- * Returns the biome specific mappreview image if it exists, or empty string otherwise.
- */
-function getBiomePreview(mapName, biomeName)
-{
- let biomePreview = basename(mapName) + "_" + basename(biomeName) + ".png";
-
- if (Engine.TextureExists("art/textures/ui/" + g_MapPreviewPath + biomePreview))
- return biomePreview;
-
- return "";
-}
-
-/**
- * Returns map description and preview image or placeholder.
- */
-function getMapDescriptionAndPreview(mapType, mapName, gameAttributes = undefined)
-{
- let mapData;
- if (mapType == "random" && mapName == "random")
- mapData = { "settings": { "Description": translate("A randomly selected map.") } };
- else if (mapType == "random" && Engine.FileExists(mapName + ".json"))
- mapData = Engine.ReadJSONFile(mapName + ".json");
- else if (Engine.FileExists(mapName + ".xml"))
- mapData = Engine.LoadMapSettings(mapName + ".xml");
-
- let biomePreview = getBiomePreview(mapName, gameAttributes && gameAttributes.settings.Biome || "");
-
- return deepfreeze({
- "description": mapData && mapData.settings && mapData.settings.Description ? translate(mapData.settings.Description) : translate("Sorry, no description available."),
- "preview": biomePreview ? biomePreview :
- mapData && mapData.settings && mapData.settings.Preview ? mapData.settings.Preview : "nopreview.png"
- });
-}
-
-/**
- * Sets the mappreview image correctly.
- * It needs to be cropped as the engine only allows loading square textures.
- *
- * @param {string} filename
- */
-function getMapPreviewImage(filename)
-{
- return "cropped:" + 400 / 512 + "," + 300 / 512 + ":" +
- g_MapPreviewPath + filename;
-}
-
/**
* Returns a formatted string describing the player assignments.
* Needs g_CivData to translate!
*
* @param {object} playerDataArray - As known from gamesetup and simstate.
* @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 gamesettings more closely.
*
* Requires g_GameAttributes and g_VictoryConditions.
*/
-function getGameDescription()
+function getGameDescription(mapCache)
{
let titles = [];
if (!g_GameAttributes.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)
continue;
let title = translateVictoryCondition(victoryCondition.Name);
if (victoryCondition.Name == "wonder")
title = sprintf(
translatePluralWithContext(
"victory condition",
"Wonder (%(min)s minute)",
"Wonder (%(min)s minutes)",
g_GameAttributes.settings.WonderDuration
),
{ "min": g_GameAttributes.settings.WonderDuration }
);
let isCaptureTheRelic = victoryCondition.Name == "capture_the_relic";
if (isCaptureTheRelic)
title = sprintf(
translatePluralWithContext(
"victory condition",
"Capture the Relic (%(min)s minute)",
"Capture the Relic (%(min)s minutes)",
g_GameAttributes.settings.RelicDuration
),
{ "min": g_GameAttributes.settings.RelicDuration }
);
titles.push({
"label": title,
"value": victoryCondition.Description
});
if (isCaptureTheRelic)
titles.push({
"label": translate("Relic Count"),
"value": g_GameAttributes.settings.RelicCount
});
if (victoryCondition.Name == "regicide")
if (g_GameAttributes.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)
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)
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)
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.")
});
titles.push({
"label": translate("Ceasefire"),
"value":
g_GameAttributes.settings.Ceasefire == 0 ?
translate("disabled") :
sprintf(translatePlural(
"For the first minute, other players will stay neutral.",
"For the first %(min)s minutes, other players will stay neutral.",
g_GameAttributes.settings.Ceasefire),
{ "min": g_GameAttributes.settings.Ceasefire })
});
if (g_GameAttributes.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": translate(g_GameAttributes.settings.Name)
+ "value": mapCache.translateMapName(
+ mapCache.getTranslatableMapName(g_GameAttributes.mapType, g_GameAttributes.map, g_GameAttributes))
});
+
titles.push({
"label": translate("Map Description"),
- "value": g_GameAttributes.settings.Description ?
- translate(g_GameAttributes.settings.Description) :
- translate("Sorry, no description available.")
+ "value": mapCache.getTranslatedMapDescription(g_GameAttributes.mapType, g_GameAttributes.map)
});
}
titles.push({
"label": translate("Map Type"),
"value": g_MapTypes.Title[g_MapTypes.Name.indexOf(g_GameAttributes.mapType)]
});
- if (typeof g_MapFilterList !== "undefined")
- titles.push({
- "label": translate("Map Filter"),
- "value": g_MapFilterList.name[g_MapFilterList.id.findIndex(id => id == g_GameAttributes.mapFilter)]
- });
-
if (g_GameAttributes.mapType == "random")
{
let mapSize = g_MapSizes.Name[g_MapSizes.Tiles.indexOf(g_GameAttributes.settings.Size)];
if (mapSize)
titles.push({
"label": translate("Map Size"),
"value": mapSize
});
}
if (g_GameAttributes.settings.Biome)
{
let biome = g_Settings.Biomes.find(b => b.Id == g_GameAttributes.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)
{
let triggerDifficulty = g_Settings.TriggerDifficulties.find(difficulty => difficulty.Difficulty == g_GameAttributes.settings.TriggerDifficulty);
titles.push({
"label": triggerDifficulty.Title,
"value": triggerDifficulty.Tooltip
});
}
- titles.push({
- "label": g_GameAttributes.settings.Nomad ? translate("Nomad Mode") : translate("Civic Centers"),
- "value":
- g_GameAttributes.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.")
- });
-
- titles.push({
- "label": translate("Starting Resources"),
- "value": sprintf(translate("%(startingResourcesTitle)s (%(amount)s)"), {
- "startingResourcesTitle":
- g_StartingResources.Title[
- g_StartingResources.Resources.indexOf(
- g_GameAttributes.settings.StartingResources)],
- "amount": g_GameAttributes.settings.StartingResources
- })
- });
-
- titles.push({
- "label": translate("Population Limit"),
- "value":
- g_PopulationCapacities.Title[
- g_PopulationCapacities.Population.indexOf(
- g_GameAttributes.settings.PopulationCap)]
- });
+ if (g_GameAttributes.settings.Nomad !== undefined)
+ titles.push({
+ "label": g_GameAttributes.settings.Nomad ? translate("Nomad Mode") : translate("Civic Centers"),
+ "value":
+ g_GameAttributes.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)
+ titles.push({
+ "label": translate("Starting Resources"),
+ "value":
+ g_GameAttributes.settings.PlayerData &&
+ g_GameAttributes.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
+ })
+ });
+
+ if (g_GameAttributes.settings.PopulationCap !== undefined)
+ titles.push({
+ "label": translate("Population Limit"),
+ "value":
+ g_GameAttributes.settings.PlayerData &&
+ g_GameAttributes.settings.PlayerData.some(pData => pData && pData.PopulationLimit !== undefined) ?
+ translateWithContext("population limit", "Per Player") :
+ g_PopulationCapacities.Title[
+ g_PopulationCapacities.Population.indexOf(
+ g_GameAttributes.settings.PopulationCap)]
+ });
titles.push({
"label": translate("Treasures"),
"value": g_GameAttributes.settings.DisableTreasures ?
translateWithContext("treasures", "Disabled") :
translateWithContext("treasures", "As defined by the map.")
});
titles.push({
"label": translate("Revealed Map"),
"value": g_GameAttributes.settings.RevealMap
});
titles.push({
"label": translate("Explored Map"),
"value": g_GameAttributes.settings.ExploreMap
});
titles.push({
"label": translate("Cheats"),
"value": g_GameAttributes.settings.CheatsEnabled
});
return titles.map(title => sprintf(translate("%(label)s %(details)s"), {
"label": coloredText(title.label, g_DescriptionHighlight),
"details":
title.value === true ? translateWithContext("gamesetup option", "enabled") :
title.value || translateWithContext("gamesetup 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/common/settings.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/settings.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/common/settings.js (revision 23374)
@@ -1,425 +1,432 @@
/**
* The maximum number of players that the engine supports.
* TODO: Maybe we can support more than 8 players sometime.
*/
const g_MaxPlayers = 8;
/**
* The maximum number of teams allowed.
*/
const g_MaxTeams = 4;
/**
* Directory containing all editable settings.
*/
const g_SettingsDirectory = "simulation/data/settings/";
/**
* Directory containing all biomes supported for random map scripts.
*/
const g_BiomesDirectory = "maps/random/rmbiome/";
/**
* An object containing all values given by setting name.
* Used by lobby, gamesetup, session, summary screen and replay menu.
*/
const g_Settings = loadSettingsValues();
/**
* Loads and translates all values of all settings which
* can be configured by dropdowns in the gamesetup.
*
* @returns {Object|undefined}
*/
function loadSettingsValues()
{
var settings = {
"AIDescriptions": loadAIDescriptions(),
"AIDifficulties": loadAIDifficulties(),
"AIBehaviors": loadAIBehaviors(),
"Ceasefire": loadCeasefire(),
"VictoryDurations": loadVictoryDuration(),
"GameSpeeds": loadSettingValuesFile("game_speeds.json"),
"MapTypes": loadMapTypes(),
"MapSizes": loadSettingValuesFile("map_sizes.json"),
"Biomes": loadBiomes(),
"PlayerDefaults": loadPlayerDefaults(),
"PopulationCapacities": loadPopulationCapacities(),
"StartingResources": loadSettingValuesFile("starting_resources.json"),
"VictoryConditions": loadVictoryConditions(),
"TriggerDifficulties": loadSettingValuesFile("trigger_difficulties.json")
};
if (Object.keys(settings).some(key => settings[key] === undefined))
return undefined;
return deepfreeze(settings);
}
/**
* Returns an array of objects reflecting all possible values for a given setting.
*
* @param {string} filename
* @see simulation/data/settings/
* @returns {Array|undefined}
*/
function loadSettingValuesFile(filename)
{
var json = Engine.ReadJSONFile(g_SettingsDirectory + filename);
if (!json || !json.Data)
{
error("Could not load " + filename + "!");
return undefined;
}
if (json.TranslatedKeys)
{
let keyContext = json.TranslatedKeys;
if (json.TranslationContext)
{
keyContext = {};
for (let key of json.TranslatedKeys)
keyContext[key] = json.TranslationContext;
}
translateObjectKeys(json.Data, keyContext);
}
return json.Data;
}
/**
* Loads the descriptions as defined in simulation/ai/.../data.json and loaded by ICmpAIManager.cpp.
*
* @returns {Array}
*/
function loadAIDescriptions()
{
var ais = Engine.GetAIs();
translateObjectKeys(ais, ["name", "description"]);
return ais.sort((a, b) => a.data.name.localeCompare(b.data.name));
}
/**
* Hardcoded, as modding is not supported without major changes.
* Notice the AI code parses the difficulty level by the index, not by name.
*
* @returns {Array}
*/
function loadAIDifficulties()
{
return [
{
"Name": "sandbox",
"Title": translateWithContext("aiDiff", "Sandbox")
},
{
"Name": "very easy",
"Title": translateWithContext("aiDiff", "Very Easy")
},
{
"Name": "easy",
"Title": translateWithContext("aiDiff", "Easy")
},
{
"Name": "medium",
"Title": translateWithContext("aiDiff", "Medium"),
"Default": true
},
{
"Name": "hard",
"Title": translateWithContext("aiDiff", "Hard")
},
{
"Name": "very hard",
"Title": translateWithContext("aiDiff", "Very Hard")
}
];
}
function loadAIBehaviors()
{
return [
{
"Name": "random",
"Title": translateWithContext("aiBehavior", "Random"),
"Default": true
},
{
"Name": "balanced",
"Title": translateWithContext("aiBehavior", "Balanced"),
},
{
"Name": "defensive",
"Title": translateWithContext("aiBehavior", "Defensive")
},
{
"Name": "aggressive",
"Title": translateWithContext("aiBehavior", "Aggressive")
}
];
}
/**
* Loads available victory times for victory conditions like Wonder and Capture the Relic.
*/
function loadVictoryDuration()
{
var jsonFile = "victory_times.json";
var json = Engine.ReadJSONFile(g_SettingsDirectory + jsonFile);
if (!json || json.Default === undefined || !json.Times || !Array.isArray(json.Times))
{
error("Could not load " + jsonFile);
return undefined;
}
return json.Times.map(duration => ({
"Duration": duration,
"Default": duration == json.Default,
"Title": sprintf(translatePluralWithContext("victory duration", "%(min)s minute", "%(min)s minutes", duration), { "min": duration })
}));
}
/**
* Loads available ceasefire settings.
*
* @returns {Array|undefined}
*/
function loadCeasefire()
{
var json = Engine.ReadJSONFile(g_SettingsDirectory + "ceasefire.json");
if (!json || json.Default === undefined || !json.Times || !Array.isArray(json.Times))
{
error("Could not load ceasefire.json");
return undefined;
}
return json.Times.map(timeout => ({
"Duration": timeout,
"Default": timeout == json.Default,
"Title": timeout == 0 ? translateWithContext("ceasefire", "No ceasefire") :
sprintf(translatePluralWithContext("ceasefire", "%(minutes)s minute", "%(minutes)s minutes", timeout), { "minutes": timeout })
}));
}
/**
* Hardcoded, as modding is not supported without major changes.
- *
- * @returns {Array}
*/
function loadMapTypes()
{
return [
{
"Name": "skirmish",
"Title": translateWithContext("map", "Skirmish"),
"Description": translate("A map with a predefined landscape and number of players. Freely select the other gamesettings."),
- "Default": true
+ "Default": true,
+ "Path": "maps/skirmishes/",
+ "Suffix": ".xml",
+ "GetData": Engine.LoadMapSettings
},
{
"Name": "random",
"Title": translateWithContext("map", "Random"),
- "Description": translate("Create a unique map with a different resource distribution each time. Freely select the number of players and teams.")
+ "Description": translate("Create a unique map with a different resource distribution each time. Freely select the number of players and teams."),
+ "Path": "maps/random/",
+ "Suffix": ".json",
+ "GetData": mapPath => Engine.ReadJSONFile(mapPath + ".json")
},
{
"Name": "scenario",
"Title": translateWithContext("map", "Scenario"),
- "Description": translate("A map with a predefined landscape and matchsettings.")
+ "Description": translate("A map with a predefined landscape and matchsettings."),
+ "Path": "maps/scenarios/",
+ "Suffix": ".xml",
+ "GetData": Engine.LoadMapSettings
}
];
}
function loadBiomes()
{
return listFiles(g_BiomesDirectory, ".json", true).filter(biomeID => biomeID != "defaultbiome").map(biomeID => {
let description = Engine.ReadJSONFile(g_BiomesDirectory + biomeID + ".json").Description;
return {
"Id": biomeID,
"Title": translateWithContext("biome definition", description.Title),
"Description": description.Description ? translateWithContext("biome definition", description.Description) : "",
"Preview": description.Preview || undefined
};
});
}
/**
* Loads available victoryCondtions from json files.
*
* @returns {Array|undefined}
*/
function loadVictoryConditions()
{
let subdir = "victory_conditions/";
let victoryConditions = listFiles(g_SettingsDirectory + subdir, ".json", false).map(victoryScriptName => {
let victoryCondition = loadSettingValuesFile(subdir + victoryScriptName + ".json");
if (victoryCondition)
victoryCondition.Name = victoryScriptName;
return victoryCondition;
});
if (victoryConditions.some(victoryCondition => victoryCondition == undefined))
return undefined;
return victoryConditions.sort((a, b) => a.GUIOrder - b.GUIOrder || (a.Title > b.Title ? 1 : a.Title > b.Title ? -1 : 0));
}
/**
* Loads the default player settings (like civs and colors).
*
* @returns {Array|undefined}
*/
function loadPlayerDefaults()
{
var json = Engine.ReadJSONFile(g_SettingsDirectory + "player_defaults.json");
if (!json || !json.PlayerData)
{
error("Could not load player_defaults.json");
return undefined;
}
return json.PlayerData;
}
/**
* Loads available population capacities.
*
* @returns {Array|undefined}
*/
function loadPopulationCapacities()
{
var json = Engine.ReadJSONFile(g_SettingsDirectory + "population_capacities.json");
if (!json || json.Default === undefined || !json.PopulationCapacities || !Array.isArray(json.PopulationCapacities))
{
error("Could not load population_capacities.json");
return undefined;
}
return json.PopulationCapacities.map(population => ({
"Population": population,
"Default": population == json.Default,
"Title": population < 10000 ? population : translate("Unlimited")
}));
}
/**
* Creates an object with all values of that property of the given setting and
* finds the index of the default value.
*
* This allows easy copying of setting values to dropdown lists.
*
* @param {Array} settingValues
* @returns {Object|undefined}
*/
function prepareForDropdown(settingValues)
{
if (!settingValues)
return undefined;
let settings = { "Default": 0 };
for (let index in settingValues)
{
for (let property in settingValues[index])
{
if (property == "Default")
continue;
if (!settings[property])
settings[property] = [];
// Switch property and index
settings[property][index] = settingValues[index][property];
}
// Copy default value
if (settingValues[index].Default)
settings.Default = +index;
}
return deepfreeze(settings);
}
/**
* Returns title or placeholder.
*
* @param {string} aiName - for example "petra"
*/
function translateAIName(aiName)
{
let description = g_Settings.AIDescriptions.find(ai => ai.id == aiName);
return description ? translate(description.data.name) : translateWithContext("AI name", "Unknown");
}
/**
* Returns title or placeholder.
*
* @param {Number} index - index of AIDifficulties
*/
function translateAIDifficulty(index)
{
let difficulty = g_Settings.AIDifficulties[index];
return difficulty ? difficulty.Title : translateWithContext("AI difficulty", "Unknown");
}
/**
* Returns title or placeholder.
*
* @param {string} aiBehavior - for example "defensive"
*/
function translateAIBehavior(aiBehavior)
{
let behavior = g_Settings.AIBehaviors.find(b => b.Name == aiBehavior);
return behavior ? behavior.Title : translateWithContext("AI behavior", "Default");
}
/**
* Returns title or placeholder.
*
* @param {string} mapType - for example "skirmish"
* @returns {string}
*/
function translateMapType(mapType)
{
let type = g_Settings.MapTypes.find(t => t.Name == mapType);
return type ? type.Title : translateWithContext("map type", "Unknown");
}
/**
* Returns title or placeholder "Default".
*
* @param {Number} mapSize - tilecount
* @returns {string}
*/
function translateMapSize(tiles)
{
let mapSize = g_Settings.MapSizes.find(size => size.Tiles == +tiles);
return mapSize ? mapSize.Name : translateWithContext("map size", "Default");
}
/**
* Returns title or placeholder.
*
* @param {Number} population - for example 300
* @returns {string}
*/
function translatePopulationCapacity(population)
{
let popCap = g_Settings.PopulationCapacities.find(p => p.Population == population);
return popCap ? popCap.Title : translateWithContext("population capacity", "Unknown");
}
/**
* Returns title or placeholder.
*
* @param {string} victoryConditionName - For example "conquest".
* @returns {string}
*/
function translateVictoryCondition(victoryConditionName)
{
let victoryCondition = g_Settings.VictoryConditions.find(condition => condition.Name == victoryConditionName);
return victoryCondition ? victoryCondition.Title : translate("Unknown Victory Condition");
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.js (revision 23374)
@@ -0,0 +1,269 @@
+/**
+ * This class provides a property independent interface to g_GameAttributes events.
+ * Classes may use this interface in order to react to changing g_GameAttributes.
+ */
+class GameSettingsControl
+{
+ constructor(gamesetupPage, netMessages, startGameControl, mapCache)
+ {
+ this.startGameControl = startGameControl;
+ this.mapCache = mapCache;
+ this.gameSettingsFile = new GameSettingsFile(gamesetupPage);
+
+ this.previousMap = undefined;
+ this.depth = 0;
+
+ // 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();
+
+ gamesetupPage.registerLoadHandler(this.onLoad.bind(this));
+ gamesetupPage.registerGetHotloadDataHandler(this.onGetHotloadData.bind(this));
+
+ startGameControl.registerLaunchGameHandler(this.onLaunchGame.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)
+ {
+ this.gameAttributesChangeHandlers.delete(handler);
+ }
+
+ /**
+ * This message is triggered after g_GameAttributes changed and recursed gameAttributesChangeHandlers finished.
+ * The use case for this is to update GUI objects which do not change g_GameAttributes but only display the attributes.
+ */
+ registerGameAttributesBatchChangeHandler(handler)
+ {
+ this.gameAttributesBatchChangeHandlers.add(handler);
+ }
+
+ unregisterGameAttributesBatchChangeHandler(handler)
+ {
+ this.gameAttributesBatchChangeHandlers.delete(handler);
+ }
+
+ registerGameAttributesFinalizeHandler(handler)
+ {
+ this.gameAttributesFinalizeHandlers.add(handler);
+ }
+
+ unregisterGameAttributesFinalizeHandler(handler)
+ {
+ this.gameAttributesFinalizeHandlers.delete(handler);
+ }
+
+ registerAssignPlayerHandler(handler)
+ {
+ this.assignPlayerHandlers.add(handler);
+ }
+
+ unregisterAssignPlayerHandler(handler)
+ {
+ this.assignPlayerHandlers.delete(handler);
+ }
+
+ registerPickRandomItemsHandler(handler)
+ {
+ this.pickRandomItemsHandlers.add(handler);
+ }
+
+ unregisterPickRandomItemsHandler(handler)
+ {
+ this.pickRandomItemsHandlers.delete(handler);
+ }
+
+ onLoad(initData, hotloadData)
+ {
+ if (initData && initData.map && initData.mapType)
+ {
+ 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 = {
+ "mapType": initData.mapType,
+ "map": initData.map
+ };
+
+ this.updateGameAttributes();
+ // Don't launchGame before all Load handlers finished
+ }
+ else
+ {
+ if (hotloadData)
+ g_GameAttributes = hotloadData.gameAttributes;
+ else if (g_IsController && this.gameSettingsFile.enabled)
+ g_GameAttributes = this.gameSettingsFile.loadFile();
+
+ this.updateGameAttributes();
+ this.setNetworkGameAttributes();
+ }
+ }
+
+ onGetHotloadData(object)
+ {
+ object.gameAttributes = g_GameAttributes;
+ }
+
+ onGamesetupMessage(message)
+ {
+ if (!message.data)
+ return;
+
+ g_GameAttributes = message.data;
+ this.updateGameAttributes();
+ }
+
+ /**
+ * This is to be called whenever g_GameAttributes has been changed except on gameAttributes finalization.
+ */
+ updateGameAttributes()
+ {
+ 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;
+ let mapData = this.mapCache.getMapData(g_GameAttributes.mapType, g_GameAttributes.map);
+ for (let handler of this.mapChangeHandlers)
+ handler(mapData);
+ }
+
+ for (let handler of this.gameAttributesChangeHandlers)
+ handler();
+
+ --this.depth;
+
+ if (this.depth == 0)
+ {
+ for (let handler of this.gameAttributesBatchChangeHandlers)
+ handler();
+ Engine.ProfileStop();
+ }
+ }
+
+ /**
+ * 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 gamesetup message was
+ * received and the data had only been modified deterministically.
+ */
+ setNetworkGameAttributes()
+ {
+ 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();
+ }
+
+ onLaunchGame()
+ {
+ if (!this.autostart)
+ this.gameSettingsFile.saveFile();
+
+ this.pickRandomItems();
+
+ for (let handler of this.gameAttributesFinalizeHandlers)
+ handler();
+
+ this.setNetworkGameAttributes();
+ }
+}
+
+GameSettingsControl.prototype.MaxDepth = 512;
+
+/**
+ * This number is used when selecting the random map type, which doesn't provide PlayerData.
+ */
+GameSettingsControl.prototype.DefaultPlayerCount = 4;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsControl.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 (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsFile.js (revision 23374)
@@ -0,0 +1,64 @@
+/**
+ * This class provides a way to save g_GameAttributes to a file and load them.
+ */
+class GameSettingsFile
+{
+ constructor(gamesetupPage)
+ {
+ this.filename = g_IsNetworked ?
+ this.GameAttributesFileMultiplayer :
+ this.GameAttributesFileSingleplayer;
+
+ this.engineInfo = Engine.GetEngineInfo();
+ this.enabled = Engine.ConfigDB_GetValue("user", this.ConfigName) == "true";
+
+ gamesetupPage.registerClosePageHandler(this.saveFile.bind(this));
+ }
+
+ 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 : {},
+ "engine_info": this.engineInfo
+ });
+ Engine.ProfileStop();
+ }
+}
+
+GameSettingsFile.prototype.ConfigName =
+ "persistmatchsettings";
+
+GameSettingsFile.prototype.GameAttributesFileSingleplayer =
+ "config/matchsettings.json";
+
+GameSettingsFile.prototype.GameAttributesFileMultiplayer =
+ "config/matchsettings.mp.json";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/GameSettingsFile.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/MapFilters.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/MapFilters.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/MapFilters.js (revision 23374)
@@ -0,0 +1,119 @@
+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)
+ {
+ 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;
+ }
+}
+
+/**
+ * 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": "!"
+ }
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/MapFilters.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/PlayerAssignmentsControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/PlayerAssignmentsControl.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/PlayerAssignmentsControl.js (revision 23374)
@@ -0,0 +1,164 @@
+/**
+ * This class provides a property independent interface to g_PlayerAssignment events and actions.
+ */
+class PlayerAssignmentsControl
+{
+ constructor(gamesetupPage, netMessages)
+ {
+ this.clientJoinHandlers = new Set();
+ this.clientLeaveHandlers = new Set();
+ this.playerAssignmentsChangeHandlers = new Set();
+
+ 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
+ }
+ };
+ }
+
+ gamesetupPage.registerLoadHandler(this.onLoad.bind(this));
+ gamesetupPage.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/leaves or any gamesetting 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();
+ }
+
+ assignClient(guid, playerIndex)
+ {
+ if (g_IsNetworked)
+ Engine.AssignNetworkPlayer(playerIndex, guid);
+ else
+ {
+ g_PlayerAssignments[guid].player = playerIndex;
+ 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)
+ // Remove obsolete playerIDs from the servers playerassignments copy
+ Engine.AssignNetworkPlayer(playerID, "");
+
+ else if (g_PlayerAssignments.local.player > g_GameAttributes.settings.PlayerData.length)
+ {
+ g_PlayerAssignments.local.player = -1;
+ this.updatePlayerAssignments();
+ }
+ }
+}
+
+PlayerAssignmentsControl.prototype.ConfigNameSingleplayer =
+ "playername.singleplayer";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/PlayerAssignmentsControl.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/ReadyControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/ReadyControl.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/ReadyControl.js (revision 23374)
@@ -0,0 +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));
+ 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()
+ {
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/ReadyControl.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 (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/StartGameControl.js (revision 23374)
@@ -0,0 +1,53 @@
+/**
+ * Cheat prevention:
+ *
+ * 1. Ensure that the host cannot start the game unless all clients agreed on the gamesettings 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 gamesettings to the engine when starting the game instance
+ * - rejecting new gamesettings 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;
+
+ 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();
+ }
+ }
+
+ switchToLoadingPage()
+ {
+ Engine.SwitchGuiPage("page_loading.xml", {
+ "attribs": g_GameAttributes,
+ "playerAssignments": g_PlayerAssignments
+ });
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Controls/StartGameControl.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControl.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControl.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControl.js (revision 23374)
@@ -0,0 +1,178 @@
+/**
+ * The GameSettingControl is an abstract class that is inherited by gamesetting 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 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.
+ *
+ * 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.
+ */
+class GameSettingControl
+{
+ // The constructor and inherited constructors shall not modify game attributes,
+ // since all GameSettingControl shall be able to subscribe to any gamesetting change.
+ constructor(gameSettingControlManager, category, playerIndex, gamesetupPage, gameSettingsControl, mapCache, mapFilters, netMessages, playerAssignmentsControl)
+ {
+ // Store arguments
+ {
+ this.category = category;
+ if (playerIndex !== undefined)
+ this.playerIndex = playerIndex;
+ this.gamesetupPage = gamesetupPage;
+ this.gameSettingsControl = gameSettingsControl;
+ this.mapCache = mapCache;
+ this.mapFilters = mapFilters;
+ this.netMessages = netMessages;
+ this.playerAssignmentsControl = 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)
+ gameSettingsControl.registerMapChangeHandler(this.onMapChange.bind(this));
+
+ if (this.onLoad)
+ gamesetupPage.registerLoadHandler(this.onLoad.bind(this));
+
+ if (this.onGameAttributesChange)
+ gameSettingsControl.registerGameAttributesChangeHandler(this.onGameAttributesChange.bind(this));
+
+ if (this.onGameAttributesBatchChange)
+ gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+
+ if (this.onAssignPlayer && this.playerIndex === 0)
+ this.gameSettingsControl.registerAssignPlayerHandler(this.onAssignPlayer.bind(this));
+
+ if (this.onPickRandomItems)
+ gameSettingsControl.registerPickRandomItemsHandler(this.onPickRandomItems.bind(this));
+
+ if (this.onGameAttributesFinalize)
+ gameSettingsControl.registerGameAttributesFinalizeHandler(this.onGameAttributesFinalize.bind(this));
+
+ if (this.onPlayerAssignmentsChange)
+ 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();
+ }
+
+ 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;
+
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControl.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.js (revision 23374)
@@ -0,0 +1,60 @@
+/**
+ * This class is implemented by gamesettings that are controlled by a checkbox.
+ */
+class GameSettingControlCheckbox extends GameSettingControl
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.isInGuiUpdate = false;
+ this.previousSelectedValue = undefined;
+ }
+
+ setControl(gameSettingControlManager)
+ {
+ let row = gameSettingControlManager.getNextRow("checkboxSettingFrame");
+ this.frame = Engine.GetGUIObjectByName("checkboxSettingFrame[" + row + "]");
+ this.checkbox = Engine.GetGUIObjectByName("checkboxSettingControl[" + row + "]");
+ this.checkbox.onPress = this.onPressSuper.bind(this);
+
+ let labels = this.frame.children[0].children;
+ this.title = labels[0];
+ this.label = labels[1];
+ }
+
+ setControlTooltip(tooltip)
+ {
+ this.checkbox.tooltip = tooltip;
+ }
+
+ setControlHidden(hidden)
+ {
+ this.checkbox.hidden = hidden;
+ }
+
+ setChecked(checked)
+ {
+ if (this.previousSelectedValue == checked)
+ return;
+
+ this.isInGuiUpdate = true;
+ this.checkbox.checked = checked;
+ this.isInGuiUpdate = false;
+
+ if (this.label)
+ this.label.caption = checked ? this.Checked : this.Unchecked;
+ }
+
+ onPressSuper()
+ {
+ if (!this.isInGuiUpdate)
+ this.onPress(this.checkbox.checked);
+ }
+}
+
+GameSettingControlCheckbox.prototype.Checked =
+ translate("Yes");
+
+GameSettingControlCheckbox.prototype.Unchecked =
+ translate("No");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.xml (revision 23374)
@@ -0,0 +1,14 @@
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlCheckbox.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.js (revision 23374)
@@ -0,0 +1,64 @@
+/**
+ * This class is implemented by gamesettings that are controlled by a dropdown.
+ */
+class GameSettingControlDropdown extends GameSettingControl
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.isInGuiUpdate = false;
+ this.dropdown.onSelectionChange = this.onSelectionChangeSuper.bind(this);
+ if (this.onHoverChange)
+ this.dropdown.onHoverChange = this.onHoverChange.bind(this);
+ }
+
+ setControl(gameSettingControlManager)
+ {
+ let row = gameSettingControlManager.getNextRow("dropdownSettingFrame");
+ this.frame = Engine.GetGUIObjectByName("dropdownSettingFrame[" + row + "]");
+ this.dropdown = Engine.GetGUIObjectByName("dropdownSettingControl[" + row + "]");
+
+ let labels = this.frame.children[0].children;
+ this.title = labels[0];
+ this.label = labels[1];
+ }
+
+ setControlTooltip(tooltip)
+ {
+ this.dropdown.tooltip = tooltip;
+ }
+
+ setControlHidden(hidden)
+ {
+ this.dropdown.hidden = hidden;
+ }
+
+ setSelectedValue(value)
+ {
+ let index = this.dropdown.list_data.indexOf(String(value));
+
+ this.isInGuiUpdate = true;
+ this.dropdown.selected = index;
+ this.isInGuiUpdate = false;
+
+ if (this.label)
+ this.label.caption = index == -1 ? this.UnknownValue : this.dropdown.list[index];
+ }
+
+ onSelectionChangeSuper()
+ {
+ if (!this.isInGuiUpdate)
+ this.onSelectionChange(this.dropdown.selected);
+ }
+}
+
+/**
+ * Highlight the "random" dropdownlist item.
+ */
+GameSettingControlDropdown.prototype.RandomItemTags = {
+ "color": "orange"
+};
+
+GameSettingControlDropdown.prototype.UnknownValue =
+ translateWithContext("settings value", "Unknown");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.xml (revision 23374)
@@ -0,0 +1,14 @@
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlDropdown.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlLabels.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlLabels.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlLabels.xml (revision 23374)
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlLabels.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlManager.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlManager.js (revision 23374)
@@ -0,0 +1,66 @@
+/**
+ * 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.
+ */
+class GameSettingControlManager
+{
+ constructor(gamesetupPage, gameSettingsControl, mapCache, mapFilters, netMessages, playerAssignmentsControl)
+ {
+ this.gameSettingsControl = gameSettingsControl;
+
+ this.rows = {};
+ this.gameSettingControls = {};
+
+ let args = Array.from(arguments);
+
+ 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, ...args);
+
+ for (let victoryCondition of g_VictoryConditions)
+ this.gameSettingControls[victoryCondition.Name] =
+ new VictoryConditionCheckbox(
+ victoryCondition, this, getCategory(victoryCondition.Name), undefined, ...args);
+
+ this.playerSettingControlManagers = Array.from(
+ new Array(g_MaxPlayers),
+ (value, playerIndex) =>
+ new PlayerSettingControlManager(playerIndex, ...args));
+ }
+
+ 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);
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingControlManager.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js (revision 23374)
@@ -0,0 +1,46 @@
+/**
+ * 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",
+ "MapSize",
+ "Biome",
+ "TriggerDifficulty",
+ "Nomad",
+ "Treasures",
+ "ExploredMap",
+ "RevealedMap"
+ ]
+ },
+ {
+ "label": translateWithContext("Match settings tab name", "Player"),
+ "settings": [
+ "PlayerCount",
+ "PopulationCap",
+ "StartingResources",
+ "Spies",
+ "Cheats"
+ ]
+ },
+ {
+ "label": translateWithContext("Match settings tab name", "Game Type"),
+ "settings": [
+ ...g_VictoryConditions.map(victoryCondition => victoryCondition.Name),
+ "RelicCount",
+ "RelicDuration",
+ "WonderDuration",
+ "RegicideGarrison",
+ "GameSpeed",
+ "Ceasefire",
+ "LockedTeams",
+ "LastManStanding",
+ "Rating"
+ ]
+ }
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/GameSettingsLayout.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/AIConfigButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/AIConfigButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/AIConfigButton.js (revision 23374)
@@ -0,0 +1,188 @@
+PlayerSettingControls.AIConfigButton = class extends GameSettingControl
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.playerConfig = Engine.GetGUIObjectByName("playerConfig[" + this.playerIndex + "]");
+
+ this.isPageOpen = false;
+ this.guid = undefined;
+ this.fixedAI = undefined;
+
+ this.defaultAIDiff = +Engine.ConfigDB_GetValue("user", this.ConfigDifficulty);
+ this.defaultBehavior = Engine.ConfigDB_GetValue("user", this.ConfigBehavior);
+
+ // Save little performance by not reallocating every call
+ this.sprintfArgs = {};
+
+ this.playerConfig.onPress = this.openConfigPage.bind(this, this.playerIndex);
+ }
+
+ onMapChange(mapData)
+ {
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!pData)
+ return;
+
+ let isScenario = g_GameAttributes.mapType == "scenario";
+
+ let mapPData = this.gameSettingsControl.getPlayerData(mapData, this.playerIndex);
+ if (mapPData && mapPData.AI)
+ {
+ let defaultPData = g_Settings.PlayerDefaults[this.playerIndex + 1];
+
+ this.fixedAI = {
+ "AI": mapPData.AI,
+ "AIDiff":
+ mapPData.AIDiff !== undefined ?
+ mapPData.AIDiff :
+ defaultPData.AIDiff,
+ "AIBehavior":
+ mapPData.AIBehavior !== undefined ?
+ mapPData.AIBehavior :
+ defaultPData.AIBehavior
+ };
+ }
+ else
+ this.fixedAI = undefined;
+ }
+
+ onAssignPlayer(source, target)
+ {
+ if (source && target.AI)
+ {
+ source.AI = target.AI;
+ source.AIDiff = target.AIDiff;
+ source.AIBehavior = target.AIBehavior;
+ }
+
+ target.AI = false;
+ delete target.AIDiff;
+ delete target.AIBehavior;
+ }
+
+ onPlayerAssignmentsChange()
+ {
+ this.guid = undefined;
+
+ for (let guid in g_PlayerAssignments)
+ if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
+ this.guid = guid;
+ }
+
+ onGameAttributesChange()
+ {
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!pData)
+ return;
+
+ // Enforce map specified AI
+ if (this.fixedAI &&
+ (pData.AI !== this.fixedAI.AI ||
+ pData.AIDiff !== this.fixedAI.AIDiff ||
+ pData.AIBehavior !== this.fixedAI.AIBehavior))
+ {
+ pData.AI = this.fixedAI.AI;
+ pData.AIDiff = this.fixedAI.AIDiff;
+ pData.AIBehavior = this.fixedAI.AIBehavior;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+
+ // Sanitize, make AI state self-consistent
+ if (pData.AI)
+ {
+ if (!pData.AIDiff || !pData.AIBehavior)
+ {
+ if (!pData.AIDiff)
+ pData.AIDiff = this.defaultAIDiff;
+
+ if (!pData.AIBehavior)
+ pData.AIBehavior = this.defaultBehavior;
+
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+ else if (pData.AI === undefined)
+ {
+ if (this.guid)
+ pData.AI = false;
+ else
+ {
+ pData.AI = g_Settings.PlayerDefaults[this.playerIndex + 1].AI;
+ pData.AIDiff = this.defaultAIDiff;
+ pData.AIBehavior = this.defaultBehavior;
+ }
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ else if (pData.AIBehavior || pData.AIDiff)
+ {
+ pData.AI = false;
+ delete pData.AIBehavior;
+ delete pData.AIDiff;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ let isPageOpen = this.isPageOpen;
+ if (isPageOpen)
+ Engine.PopGuiPage();
+
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!pData)
+ return;
+
+ this.sprintfArgs.description = translateAISettings(pData);
+ this.playerConfig.tooltip = sprintf(this.Tooltip, this.sprintfArgs);
+ this.playerConfig.hidden = !pData.AI;
+
+ if (isPageOpen)
+ this.openConfigPage();
+ }
+
+ openConfigPage()
+ {
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!pData || !pData.AI)
+ return;
+
+ this.isPageOpen = true;
+ Engine.PushGuiPage(
+ "page_aiconfig.xml",
+ {
+ "playerSlot": this.playerIndex,
+ "id": pData.AI,
+ "difficulty": pData.AIDiff,
+ "behavior": pData.AIBehavior,
+ "fixed": !!this.fixedAI
+ },
+ this.onConfigPageClosed.bind(this));
+ }
+
+ onConfigPageClosed(data)
+ {
+ this.isPageOpen = false;
+
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!data || !data.save || !g_IsController || !pData)
+ return;
+
+ pData.AI = data.id;
+ pData.AIDiff = data.difficulty;
+ pData.AIBehavior = data.behavior;
+
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+PlayerSettingControls.AIConfigButton.prototype.Tooltip =
+ translate("Configure AI: %(description)s.");
+
+PlayerSettingControls.AIConfigButton.prototype.ConfigDifficulty =
+ "gui.gamesetup.aidifficulty";
+
+PlayerSettingControls.AIConfigButton.prototype.ConfigBehavior =
+ "gui.gamesetup.aibehavior";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/AIConfigButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js (revision 23374)
@@ -0,0 +1,269 @@
+PlayerSettingControls.PlayerAssignment = class 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;
+
+ 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);
+ }
+
+ 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)
+ 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;
+ for (let guid in g_PlayerAssignments)
+ if (g_PlayerAssignments[guid].player == this.playerIndex + 1)
+ {
+ this.assignedGUID = guid;
+ break;
+ }
+
+ 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;
+ }
+
+ onGameAttributesChange()
+ {
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!pData)
+ return;
+
+ if (this.fixedAI && pData.AI != this.fixedAI)
+ {
+ pData.AI = this.fixedAI;
+ this.gameSettingsControl.updateGameAttributes();
+ this.playerAssignmentsControl.unassignClient(this.playerIndex + 1);
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ let pData = this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ if (!pData)
+ 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)));
+ }
+
+ 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);
+ 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;
+ playerAssignmentsControl.assignPlayer(guidToAssign, playerIndex);
+ gameSettingsControl.assignPlayer(sourcePlayer, playerIndex);
+ }
+
+ 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;
+
+ 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();
+ gameSettingsControl.setNetworkGameAttributes();
+ }
+
+ isSelected(pData, guid, value)
+ {
+ return !guid && !pData.AI;
+ }
+ };
+
+ PlayerAssignmentItem.Unassigned.prototype.Label =
+ translate("Unassigned");
+
+ PlayerAssignmentItem.Unassigned.prototype.Tags =
+ { "color": "140 140 140" };
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerAssignment.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js (revision 23374)
@@ -0,0 +1,142 @@
+PlayerSettingControls.PlayerCiv = class 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;
+ }
+
+ 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)
+ {
+ 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);
+ }
+
+ 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();
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerCiv.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerColor.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerColor.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerColor.js (revision 23374)
@@ -0,0 +1,175 @@
+PlayerSettingControls.PlayerColor = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.defaultColors = g_Settings.PlayerDefaults.slice(1).map(pData => pData.Color);
+ }
+
+ setControl()
+ {
+ this.dropdown = Engine.GetGUIObjectByName("playerColor[" + this.playerIndex + "]");
+ this.playerBackgroundColor = Engine.GetGUIObjectByName("playerBackgroundColor[" + this.playerIndex + "]");
+ this.playerColorHeading = Engine.GetGUIObjectByName("playerColorHeading");
+ }
+
+ onMapChange(mapData)
+ {
+ Engine.ProfileStart("updatePlayerColorList");
+ let hidden = !g_IsController || g_GameAttributes.mapType == "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);
+ }
+
+ 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();
+ 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 =
+ "â– ";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/Dropdowns/PlayerColor.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerFrame.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerFrame.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerFrame.js (revision 23374)
@@ -0,0 +1,23 @@
+PlayerSettingControls.PlayerFrame = class 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;
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.playerFrame.hidden = !this.gameSettingsControl.getPlayerData(g_GameAttributes, this.playerIndex);
+ }
+}
+
+PlayerSettingControls.PlayerFrame.prototype.Height = 32;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerFrame.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerSettings.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerSettings.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PerPlayer/PlayerSettings.js (revision 23374)
@@ -0,0 +1,45 @@
+// 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/GameSettings/PerPlayer/PlayerSettings.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PlayerSettingControlManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PlayerSettingControlManager.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PlayerSettingControlManager.js (revision 23374)
@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+class PlayerSettingControlManager
+{
+ constructor(playerIndex, gamesetupPage, gameSettingsControl, mapCache, mapFilters, netMessages, playerAssignmentsControl)
+ {
+ this.playerSettingControls = {};
+
+ for (let name in PlayerSettingControls)
+ this.playerSettingControls[name] =
+ new PlayerSettingControls[name](
+ undefined, undefined, playerIndex, gamesetupPage, gameSettingsControl, mapCache, mapFilters, netMessages, playerAssignmentsControl);
+ }
+
+ addAutocompleteEntries(autocomplete)
+ {
+ for (let name in this.playerSettingControls)
+ this.playerSettingControls[name].addAutocompleteEntries(name, autocomplete);
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/PlayerSettingControlManager.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/ExploredMap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/ExploredMap.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/ExploredMap.js (revision 23374)
@@ -0,0 +1,58 @@
+GameSettingControls.ExploredMap = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.ExploreMap || undefined;
+
+ 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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (!g_GameAttributes.mapType)
+ return;
+
+ this.setChecked(g_GameAttributes.settings.ExploreMap);
+ this.setEnabled(g_GameAttributes.mapType != "scenario" && !g_GameAttributes.settings.RevealMap);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.ExploreMap = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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).");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/ExploredMap.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Nomad.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Nomad.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Nomad.js (revision 23374)
@@ -0,0 +1,63 @@
+GameSettingControls.Nomad = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let available = g_GameAttributes.mapType == "random";
+ this.setHidden(!available);
+ if (!available)
+ return;
+
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.Nomad;
+
+ if (mapValue !== undefined && mapValue != g_GameAttributes.settings.Nomad)
+ {
+ g_GameAttributes.settings.Nomad = mapValue;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesChange()
+ {
+ 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);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.Nomad = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/Nomad.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RevealedMap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RevealedMap.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RevealedMap.js (revision 23374)
@@ -0,0 +1,53 @@
+GameSettingControls.RevealedMap = class extends GameSettingControlCheckbox
+{
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.RevealMap || undefined;
+
+ if (mapValue !== undefined && mapValue != g_GameAttributes.settings.RevealMap)
+ {
+ g_GameAttributes.settings.RevealMap = mapValue;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+
+ this.setEnabled(g_GameAttributes.mapType != "scenario");
+ }
+
+ onGameAttributesChange()
+ {
+ 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);
+ }
+
+ onPress(checked)
+ {
+ g_GameAttributes.settings.RevealMap = checked;
+ this.gameSettingsControl.updateGameAttributes();
+ 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).");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Checkboxes/RevealedMap.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js (revision 23374)
@@ -0,0 +1,133 @@
+GameSettingControls.Biome = class 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
+ };
+ }
+
+ 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();
+ }
+ else
+ this.values = undefined;
+ }
+
+ onGameAttributesChange()
+ {
+ if (!g_GameAttributes.mapType)
+ return;
+
+ if (this.values)
+ {
+ if (this.values.Id.indexOf(g_GameAttributes.settings.Biome || undefined) == -1)
+ {
+ g_GameAttributes.settings.Biome = this.RandomBiomeId;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+ else if (g_GameAttributes.settings.Biome)
+ {
+ delete g_GameAttributes.settings.Biome;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (this.values)
+ this.setSelectedValue(g_GameAttributes.settings.Biome);
+ }
+
+ filterBiome(available)
+ {
+ if (typeof available == "string")
+ return biome => biome.Id.startsWith(available);
+
+ return biome => available.indexOf(biome.Id) != -1;
+ }
+
+ createBiomeItem(biome)
+ {
+ return {
+ "Id": biome.Id,
+ "Title": biome.Title,
+ "Autocomplete": biome.Title,
+ "Description": biome.Description
+ };
+ }
+
+ getAutocompleteEntries()
+ {
+ return this.values && this.values.Autocomplete;
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.Biome = this.values.Id[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/Biome.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapFilter.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapFilter.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapFilter.js (revision 23374)
@@ -0,0 +1,94 @@
+GameSettingControls.MapFilter = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = undefined;
+ this.previousMapType = undefined;
+ }
+
+ 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)
+ return;
+
+ Engine.ProfileStart("updateMapFilterList");
+ this.previousMapType = g_GameAttributes.mapType;
+
+ let values = prepareForDropdown(
+ this.mapFilters.getAvailableMapFilters(
+ g_GameAttributes.mapType));
+
+ 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();
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (this.values && g_GameAttributes.mapFilter)
+ this.setSelectedValue(g_GameAttributes.mapFilter);
+ }
+
+ 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()
+ {
+ // The setting is only relevant to the gamesetup stage!
+ delete g_GameAttributes.mapFilter;
+ }
+};
+
+GameSettingControls.MapFilter.prototype.TitleCaption =
+ translate("Map Filter");
+
+GameSettingControls.MapFilter.prototype.Tooltip =
+ translate("Select a map filter.");
+
+GameSettingControls.MapFilter.prototype.AutocompleteOrder = 0;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapFilter.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSize.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSize.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSize.js (revision 23374)
@@ -0,0 +1,79 @@
+GameSettingControls.MapSize = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.previousMapType = undefined;
+
+ this.dropdown.list = g_MapSizes.Name;
+ this.dropdown.list_data = g_MapSizes.Tiles;
+ }
+
+ onHoverChange()
+ {
+ this.dropdown.tooltip = g_MapSizes.Tooltip[this.dropdown.hovered] || this.Tooltip;
+ }
+
+ onMapChange(mapData)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.Size || undefined;
+
+ if (g_GameAttributes.mapType == "random" && mapValue !== undefined && mapValue != g_GameAttributes.settings.Size)
+ {
+ g_GameAttributes.settings.Size = mapValue;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesChange()
+ {
+ if (!g_GameAttributes.mapType ||
+ !g_GameAttributes.settings ||
+ this.previousMapType == g_GameAttributes.mapType)
+ return;
+
+ this.previousMapType = g_GameAttributes.mapType;
+
+ 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();
+ }
+ }
+
+ getAutocompleteEntries()
+ {
+ return g_MapSizes.Name;
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.Size = g_MapSizes.Tiles[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSize.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicCount.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicCount.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicCount.js (revision 23374)
@@ -0,0 +1,85 @@
+GameSettingControls.RelicCount = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = Array.from(new Array(g_CivData.length), (v, i) => i + 1);
+ this.dropdown.list = this.values;
+ this.dropdown.list_data = this.values;
+
+ this.available = false;
+ }
+
+ onMapChange(mapData)
+ {
+ this.setEnabled(g_GameAttributes.mapType != "scenario");
+
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.VictoryConditions &&
+ mapData.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1 &&
+ mapData.settings.RelicCount || undefined;
+
+ 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();
+ }
+
+ onGameAttributesChange()
+ {
+ if (!g_GameAttributes.settings.VictoryConditions)
+ return;
+
+ this.available = g_GameAttributes.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1;
+
+ if (this.available)
+ {
+ if (g_GameAttributes.settings.RelicCount === undefined)
+ {
+ g_GameAttributes.settings.RelicCount = this.DefaultRelicCount;
+ 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)
+ this.setSelectedValue(g_GameAttributes.settings.RelicCount);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.RelicCount = this.values[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.RelicCount.prototype.TitleCaption =
+ translate("Relic Count");
+
+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 =
+ this.NameCaptureTheRelic;
+
+GameSettingControls.RelicCount.prototype.DefaultRelicCount = 2;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicCount.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSelection.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSelection.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSelection.js (revision 23374)
@@ -0,0 +1,171 @@
+GameSettingControls.MapSelection = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = undefined;
+
+ this.previousMapType = undefined;
+ this.previousMapFilter = undefined;
+
+ 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()
+ {
+ 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)
+ {
+ g_GameAttributes.map = this.values.file[0];
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (g_GameAttributes.map)
+ this.setSelectedValue(g_GameAttributes.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;
+
+ {
+ let values =
+ this.mapFilters.getFilteredMaps(
+ g_GameAttributes.mapType,
+ g_GameAttributes.mapFilter,
+ false);
+
+ values.sort(sortNameIgnoreCase);
+
+ if (g_GameAttributes.mapType == "random")
+ values.unshift(this.randomItem);
+
+ this.values = prepareForDropdown(values);
+ }
+
+ this.dropdown.list = this.values.name;
+ this.dropdown.list_data = this.values.file;
+ 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)
+ 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();
+ }
+
+ 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);
+
+ g_GameAttributes.settings.CircularMap =
+ mapData.settings.CircularMap || true;
+
+ 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 = [
+ "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"
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapSelection.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PopulationCap.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PopulationCap.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PopulationCap.js (revision 23374)
@@ -0,0 +1,101 @@
+GameSettingControls.PopulationCap = class 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 =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.PopulationCap || undefined;
+
+ 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.settings.PlayerData &&
+ mapData.settings.PlayerData.some(pData => pData && pData.PopulationLimit !== undefined);
+
+ this.setEnabled(!isScenario && !this.perPlayer);
+
+ if (this.perPlayer)
+ this.label.caption = this.PerPlayerCaption;
+ }
+
+ onGameAttributesChange()
+ {
+ 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);
+ }
+
+ 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;
+ 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();
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PopulationCap.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PlayerCount.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PlayerCount.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PlayerCount.js (revision 23374)
@@ -0,0 +1,52 @@
+GameSettingControls.PlayerCount = class 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");
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setSelectedValue(g_GameAttributes.settings.PlayerData.length);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ let length = this.values[itemIdx];
+ if (g_GameAttributes.settings.PlayerData.length == length)
+ return;
+
+ g_GameAttributes.settings.PlayerData.length = length;
+
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+
+ this.playerAssignmentsControl.unassignInvalidPlayers();
+ }
+};
+
+GameSettingControls.PlayerCount.prototype.TitleCaption =
+ translate("Number of Players");
+
+GameSettingControls.PlayerCount.prototype.Tooltip =
+ translate("Select number of players.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/PlayerCount.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapType.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapType.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapType.js (revision 23374)
@@ -0,0 +1,66 @@
+/**
+ * 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
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.dropdown.list = g_MapTypes.Title;
+ this.dropdown.list_data = g_MapTypes.Name;
+ }
+
+ onHoverChange()
+ {
+ this.dropdown.tooltip = g_MapTypes.Description[this.dropdown.hovered] || this.Tooltip;
+ }
+
+ onGameAttributesChange()
+ {
+ if (g_MapTypes.Name.indexOf(g_GameAttributes.mapType || undefined) == -1)
+ {
+ g_GameAttributes.mapType = g_MapTypes.Name[g_MapTypes.Default];
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setSelectedValue(g_GameAttributes.mapType);
+ }
+
+ 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();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.MapType.prototype.TitleCaption =
+ translate("Map Type");
+
+GameSettingControls.MapType.prototype.Tooltip =
+ translate("Select a map type.");
+
+GameSettingControls.MapType.prototype.AutocompleteOrder = 0;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/MapType.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/WonderDuration.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/WonderDuration.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/WonderDuration.js (revision 23374)
@@ -0,0 +1,85 @@
+GameSettingControls.WonderDuration = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = prepareForDropdown(g_Settings.VictoryDurations);
+
+ this.dropdown.list = this.values.Title;
+ this.dropdown.list_data = this.values.Duration;
+
+ this.available = false;
+ }
+
+ onMapChange(mapData)
+ {
+ this.setEnabled(g_GameAttributes.mapType != "scenario");
+
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.VictoryConditions &&
+ mapData.settings.VictoryConditions.indexOf(this.NameWonderVictory) != -1 &&
+ mapData.settings.WonderDuration || undefined;
+
+ 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();
+ }
+
+ onGameAttributesChange()
+ {
+ if (!g_GameAttributes.settings.VictoryConditions)
+ return;
+
+ this.available = g_GameAttributes.settings.VictoryConditions.indexOf(this.NameWonderVictory) != -1;
+
+ if (this.available)
+ {
+ if (g_GameAttributes.settings.WonderDuration === undefined)
+ {
+ g_GameAttributes.settings.WonderDuration = this.values.Duration[this.values.Default];
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+ else if (g_GameAttributes.settings.WonderDuration !== undefined)
+ {
+ delete g_GameAttributes.settings.WonderDuration;
+ this.gameSettingsControl.updateGameAttributes();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.setHidden(!this.available);
+
+ if (this.available)
+ this.setSelectedValue(g_GameAttributes.settings.WonderDuration);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.WonderDuration = this.values.Duration[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+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";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/WonderDuration.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.xml (revision 23374)
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.xml (revision 23374)
@@ -0,0 +1,8 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.js (revision 23374)
@@ -0,0 +1,88 @@
+class ReadyButton
+{
+ constructor(readyControl, netMessages, playerAssignmentsControl)
+ {
+ this.readyControl = readyControl;
+
+ this.hidden = undefined;
+
+ this.buttonHiddenChangeHandlers = new Set();
+ this.readyButtonPressHandlers = new Set();
+
+ this.readyButton = Engine.GetGUIObjectByName("readyButton");
+ this.readyButton.onPress = this.onPress.bind(this);
+ this.readyButton.onPressRight = this.onPressRight.bind(this);
+
+ playerAssignmentsControl.registerPlayerAssignmentsChangeHandler(this.onPlayerAssignmentsChange.bind(this));
+ netMessages.registerNetMessageHandler("netstatus", this.onNetStatusMessage.bind(this));
+
+ if (g_IsController && g_IsNetworked)
+ this.readyControl.setReady(this.readyControl.StayReady, true);
+ }
+
+ registerButtonHiddenChangeHandler(handler)
+ {
+ this.buttonHiddenChangeHandlers.add(handler);
+ }
+
+ onNetStatusMessage(message)
+ {
+ if (message.status == "disconnected")
+ this.readyButton.enabled = false;
+ }
+
+ onPlayerAssignmentsChange()
+ {
+ let playerAssignment = g_PlayerAssignments[Engine.GetPlayerGUID()];
+ let hidden = g_IsController || !playerAssignment || playerAssignment.player == -1;
+
+ if (!hidden)
+ {
+ this.readyButton.caption = this.Caption[playerAssignment.status];
+ this.readyButton.tooltip = this.Tooltip[playerAssignment.status];
+ }
+
+ if (hidden == this.hidden)
+ return;
+
+ this.hidden = hidden;
+ this.readyButton.hidden = hidden;
+
+ for (let handler of this.buttonHiddenChangeHandlers)
+ handler(this.readyButton);
+ }
+
+ registerReadyButtonPressHandler(handler)
+ {
+ this.readyButtonPressHandlers.add(handler);
+ }
+
+ onPress()
+ {
+ let newState =
+ (g_PlayerAssignments[Engine.GetPlayerGUID()].status + 1) % (this.readyControl.StayReady + 1);
+
+ for (let handler of this.readyButtonPressHandlers)
+ handler(newState);
+
+ this.readyControl.setReady(newState, true);
+ }
+
+ onPressRight()
+ {
+ if (g_PlayerAssignments[Engine.GetPlayerGUID()].status != this.readyControl.NotReady)
+ this.readyControl.setReady(this.readyControl.NotReady, true);
+ }
+}
+
+ReadyButton.prototype.Caption = [
+ translate("I'm ready"),
+ translate("Stay ready"),
+ translate("I'm not ready!")
+];
+
+ReadyButton.prototype.Tooltip = [
+ translate("State that you are ready to play."),
+ translate("Stay ready even when the game settings change."),
+ translate("State that you are not ready to play.")
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.js (revision 23374)
@@ -0,0 +1,72 @@
+class StartGameButton
+{
+ constructor(gamesetupPage, startGameControl, netMessages, readyControl, playerAssignmentsControl)
+ {
+ this.startGameControl = startGameControl;
+ this.readyControl = readyControl;
+ this.gameStarted = false;
+
+ this.buttonHiddenChangeHandlers = new Set();
+
+ this.startGameButton = Engine.GetGUIObjectByName("startGameButton");
+ this.startGameButton.caption = this.Caption;
+ this.startGameButton.onPress = this.onPress.bind(this);
+
+ gamesetupPage.registerLoadHandler(this.onLoad.bind(this));
+ playerAssignmentsControl.registerPlayerAssignmentsChangeHandler(this.update.bind(this));
+ }
+
+ registerButtonHiddenChangeHandler(handler)
+ {
+ this.buttonHiddenChangeHandlers.add(handler);
+ }
+
+ onLoad()
+ {
+ this.startGameButton.hidden = !g_IsController;
+ for (let handler of this.buttonHiddenChangeHandlers)
+ handler();
+ }
+
+ update()
+ {
+ let isEveryoneReady = this.isEveryoneReady();
+ this.startGameButton.enabled = !this.gameStarted && isEveryoneReady;
+ this.startGameButton.tooltip =
+ !g_IsNetworked || isEveryoneReady ?
+ this.ReadyTooltip :
+ this.ReadyTooltipWaiting;
+ }
+
+ isEveryoneReady()
+ {
+ if (!g_IsNetworked)
+ return true;
+
+ for (let guid in g_PlayerAssignments)
+ if (g_PlayerAssignments[guid].player != -1 &&
+ g_PlayerAssignments[guid].status == this.readyControl.NotReady)
+ return false;
+
+ return true;
+ }
+
+ onPress()
+ {
+ if (this.gameStarted)
+ return;
+
+ this.gameStarted = true;
+ this.update();
+ this.startGameControl.launchGame();
+ }
+}
+
+StartGameButton.prototype.Caption =
+ translate("Start Game!");
+
+StartGameButton.prototype.ReadyTooltip =
+ translate("Start a new game with the current settings.");
+
+StartGameButton.prototype.ReadyTooltipWaiting =
+ translate("Start a new game with the current settings (disabled until all players are ready).");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientChat.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientChat.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientChat.js (revision 23374)
@@ -0,0 +1,53 @@
+ChatMessageEvents.ClientChat = class
+{
+ constructor(chatMessagesPanel, netMessages)
+ {
+ this.chatMessagesPanel = chatMessagesPanel;
+
+ netMessages.registerNetMessageHandler("chat", this.onClientChat.bind(this));
+ this.usernameArgs = {};
+ this.messageArgs = {};
+
+ // TODO: Remove this global required by gui/common/
+ global.colorizePlayernameByGUID = this.colorizePlayernameByGUID.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;
+
+ // 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"
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientChat.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/GameSettingsChanged.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/GameSettingsChanged.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/GameSettingsChanged.js (revision 23374)
@@ -0,0 +1,22 @@
+/**
+ * The purpose of this message is to indicate to the local player when settings they had agreed on changed.
+ */
+ChatMessageEvents.GameSettingsChanged = class
+{
+ constructor(chatMessagesPanel, netMessages, gameSettingsControl, playerAssignmentsControl, readyControl)
+ {
+ this.readyControl = readyControl;
+ this.chatMessagesPanel = chatMessagesPanel;
+
+ readyControl.registerResetReadyHandler(this.onResetReady.bind(this));
+ }
+
+ onResetReady()
+ {
+ if (this.readyControl.getLocalReadyState() == this.readyControl.Ready)
+ this.chatMessagesPanel.addStatusMessage(this.MessageText);
+ }
+};
+
+ChatMessageEvents.GameSettingsChanged.prototype.MessageText =
+ translate("Game settings have been changed");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/GameSettingsChanged.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/StatusMessageFormat.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/StatusMessageFormat.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/StatusMessageFormat.js (revision 23374)
@@ -0,0 +1,28 @@
+/**
+ * Status messages are textual event notifications triggered by multi-user chat room actions.
+ */
+class StatusMessageFormat
+{
+ constructor()
+ {
+ this.args = {};
+ }
+
+ /**
+ * escapeText is the responsibility of the caller.
+ */
+ format(text)
+ {
+ this.args.message = text;
+ return setStringTags(
+ sprintf(this.MessageFormat, this.args),
+ this.MessageTags);
+ }
+}
+
+StatusMessageFormat.prototype.MessageFormat =
+ translate("== %(message)s");
+
+StatusMessageFormat.prototype.MessageTags = {
+ "font": "sans-bold-13"
+};
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/StatusMessageFormat.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/TriggerDifficulty.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/TriggerDifficulty.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/TriggerDifficulty.js (revision 23374)
@@ -0,0 +1,84 @@
+GameSettingControls.TriggerDifficulty = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = undefined;
+ }
+
+ onHoverChange()
+ {
+ this.dropdown.tooltip =
+ this.values && this.values.Tooltip[this.dropdown.hovered] ||
+ this.Tooltip;
+ }
+
+ onMapChange(mapData)
+ {
+ 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)
+ 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();
+ }
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (this.values)
+ this.setSelectedValue(g_GameAttributes.settings.TriggerDifficulty);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.TriggerDifficulty = this.values.Difficulty[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+GameSettingControls.TriggerDifficulty.prototype.TitleCaption =
+ translate("Difficulty");
+
+GameSettingControls.TriggerDifficulty.prototype.Tooltip =
+ translate("Select the difficulty of this scenario.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/TriggerDifficulty.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.js (revision 23374)
@@ -0,0 +1,147 @@
+/**
+ * The GamesetupPage 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 GamesetupPage
+{
+ constructor(initData, hotloadData)
+ {
+ if (!g_Settings)
+ return;
+
+ Engine.ProfileStart("GamesetupPage");
+
+ this.loadHandlers = new Set();
+ this.closePageHandlers = new Set();
+ this.getHotloadDataHandlers = new Set();
+
+ 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 playerAssignmentsControl = new PlayerAssignmentsControl(this, netMessages);
+ 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
+ };
+
+ // These class instances are interfaces to networked messages and do not manage any GUI Object.
+ this.netMessages = {
+ "netMessages": netMessages,
+ "gameRegisterStanza":
+ Engine.HasXmppClient() &&
+ new GameRegisterStanza(
+ initData, this, netMessages, gameSettingsControl, playerAssignmentsControl, mapCache)
+ };
+
+ // This class instance owns all gamesetting GUI controls such as dropdowns and checkboxes.
+ // The controls also deterministically sanitize g_GameAttributes and g_PlayerAssignments
+ // without broadcasting the change.
+ this.gameSettingControlManager =
+ new GameSettingControlManager(this, gameSettingsControl, mapCache, mapFilters, netMessages, playerAssignmentsControl);
+
+ // These classes manage GUI buttons.
+ {
+ let startGameButton = new StartGameButton(this, startGameControl, netMessages, readyControl, playerAssignmentsControl);
+ let readyButton = new ReadyButton(readyControl, netMessages, playerAssignmentsControl);
+ this.panelButtons = {
+ "cancelButton": new CancelButton(this, startGameButton, readyButton, this.netMessages.gameRegisterStanza),
+ "civInfoButton": new CivInfoButton(),
+ "lobbyButton": new LobbyButton(),
+ "readyButton": readyButton,
+ "startGameButton": startGameButton
+ };
+ }
+
+ // These classes manage GUI Objects.
+ {
+ let gameSettingTabs = new GameSettingTabs(this, this.panelButtons.lobbyButton);
+ let gameSettingsPanel = new GameSettingsPanel(
+ this, gameSettingTabs, gameSettingsControl, this.gameSettingControlManager);
+
+ this.panels = {
+ "chatPanel": new ChatPanel(this.gameSettingControlManager, gameSettingsControl, netMessages, playerAssignmentsControl, readyControl, gameSettingsPanel),
+ "gameSettingWarning": new GameSettingWarning(gameSettingsControl, this.panelButtons.cancelButton),
+ "gameDescription": new GameDescription(mapCache, gameSettingTabs, gameSettingsControl),
+ "gameSettingsPanel": gameSettingsPanel,
+ "gameSettingsTabs": gameSettingTabs,
+ "loadingWindow": new LoadingWindow(netMessages),
+ "mapPreview": new MapPreview(gameSettingsControl, mapCache),
+ "resetCivsButton": new ResetCivsButton(gameSettingsControl),
+ "resetTeamsButton": new ResetTeamsButton(gameSettingsControl),
+ "soundNotification": new SoundNotification(netMessages, playerAssignmentsControl),
+ "tipsPanel": new TipsPanel(gameSettingsPanel),
+ "tooltip": new Tooltip(this.panelButtons.cancelButton)
+ };
+ }
+
+ 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");
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSetupPage.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.js (revision 23374)
@@ -0,0 +1,47 @@
+class CancelButton
+{
+ constructor(gamesetupPage, startGameButton, readyButton, gameSettingsControl)
+ {
+ this.gamesetupPage = gamesetupPage;
+ this.startGameButton = startGameButton;
+ this.readyButton = readyButton;
+ this.gameSettingsControl = gameSettingsControl;
+
+ this.cancelButtonResizeHandlers = new Set();
+
+ this.buttonPositions = Engine.GetGUIObjectByName("bottomRightPanel").children;
+
+ this.cancelButton = Engine.GetGUIObjectByName("cancelButton");
+ this.cancelButton.caption = this.Caption;
+ this.cancelButton.tooltip = Engine.HasXmppClient() ? this.TooltipLobby : this.TooltipMenu;
+ this.cancelButton.onPress = gamesetupPage.closePage.bind(gamesetupPage);
+
+ readyButton.registerButtonHiddenChangeHandler(this.onNeighborButtonHiddenChange.bind(this));
+ startGameButton.registerButtonHiddenChangeHandler(this.onNeighborButtonHiddenChange.bind(this));
+ }
+
+ registerCancelButtonResizeHandler(handler)
+ {
+ this.cancelButtonResizeHandlers.add(handler);
+ }
+
+ onNeighborButtonHiddenChange()
+ {
+ this.cancelButton.size = this.buttonPositions[
+ this.buttonPositions[1].children.every(button => button.hidden) ? 1 : 0].size;
+
+ for (let handler of this.cancelButtonResizeHandlers)
+ handler(this.cancelButton);
+ }
+}
+
+CancelButton.prototype.Caption =
+ translate("Back");
+
+CancelButton.prototype.TooltipLobby =
+ translate("Return to the lobby.");
+
+CancelButton.prototype.TooltipMenu =
+ translate("Return to the main menu.");
+
+CancelButton.prototype.Margin = 0;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CancelButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.xml (revision 23374)
@@ -0,0 +1,11 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetTeamsButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetTeamsButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetTeamsButton.js (revision 23374)
@@ -0,0 +1,35 @@
+class ResetTeamsButton
+{
+ constructor(gameSettingsControl)
+ {
+ this.gameSettingsControl = 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);
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (!g_GameAttributes.mapType)
+ return;
+
+ this.teamResetButton.hidden = g_GameAttributes.mapType == "scenario" || !g_IsController;
+ }
+
+ onPress()
+ {
+ if (!g_GameAttributes.settings || !g_GameAttributes.settings.PlayerData)
+ return;
+
+ for (let pData of g_GameAttributes.settings.PlayerData)
+ pData.Team = -1;
+
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+}
+
+ResetTeamsButton.prototype.Tooltip =
+ translate("Reset all teams to the default.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetTeamsButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputPanel.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputPanel.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputPanel.js (revision 23374)
@@ -0,0 +1,55 @@
+class ChatInputPanel
+{
+ constructor(netMessages, chatInputAutocomplete)
+ {
+ this.chatInputAutocomplete = chatInputAutocomplete;
+
+ this.chatInput = Engine.GetGUIObjectByName("chatInput");
+ this.chatInput.tooltip = colorizeAutocompleteHotkey(this.Tooltip);
+ this.chatInput.onPress = this.onPress.bind(this);
+ this.chatInput.onTab = this.onTab.bind(this);
+ this.chatInput.focus();
+
+ this.chatSubmitButton = Engine.GetGUIObjectByName("chatSubmitButton");
+ this.chatSubmitButton.onPress = this.onPress.bind(this);
+
+ netMessages.registerNetMessageHandler("netstatus", this.onNetStatusMessage.bind(this));
+ }
+
+ onNetStatusMessage(message)
+ {
+ if (message.status == "disconnected")
+ {
+ reportDisconnect(message.reason, true);
+ this.chatInput.hidden = true;
+ this.chatSubmitButton.hidden = true;
+ }
+ }
+
+ onTab()
+ {
+ autoCompleteText(
+ this.chatInput,
+ this.chatInputAutocomplete.getAutocompleteEntries());
+ }
+
+ onPress()
+ {
+ if (!g_IsNetworked)
+ return;
+
+ let text = this.chatInput.caption;
+ if (!text.length)
+ return;
+
+ this.chatInput.caption = "";
+
+ if (!executeNetworkCommand(text))
+ Engine.SendNetworkChat(text);
+
+ this.chatInput.focus();
+ }
+}
+
+ChatInputPanel.prototype.Tooltip =
+ translate("Press %(hotkey)s to autocomplete player names or settings.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputPanel.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientReady.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientReady.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientReady.js (revision 23374)
@@ -0,0 +1,30 @@
+ChatMessageEvents.ClientReady = class
+{
+ constructor(chatMessagesPanel, netMessages, gameSettingsControl, playerAssignmentsControl, readyControl)
+ {
+ this.chatMessagesPanel = chatMessagesPanel;
+
+ this.args = {};
+
+ netMessages.registerNetMessageHandler("ready", this.onReadyMessage.bind(this));
+ }
+
+ onReadyMessage(message)
+ {
+ let playerAssignment = g_PlayerAssignments[message.guid];
+ if (!playerAssignment || playerAssignment.player == -1)
+ return;
+
+ let text = this.ReadyMessage[message.status] || undefined;
+ if (!text)
+ return;
+
+ this.args.username = playerAssignment.name;
+ this.chatMessagesPanel.addText(sprintf(text, this.args));
+ }
+};
+
+ChatMessageEvents.ClientReady.prototype.ReadyMessage = [
+ translate("* %(username)s is not ready."),
+ translate("* %(username)s is ready!")
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientReady.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.xml (revision 23374)
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+ Send
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.xml (revision 23374)
@@ -0,0 +1,4 @@
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/StartingResources.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/StartingResources.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/StartingResources.js (revision 23374)
@@ -0,0 +1,92 @@
+GameSettingControls.StartingResources = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.dropdown.list = g_StartingResources.Title;
+ this.dropdown.list_data = g_StartingResources.Resources;
+
+ this.perPlayer = false;
+ this.sprintfArgs = {};
+ }
+
+ 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)
+ {
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.StartingResources || undefined;
+
+ 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.settings.PlayerData &&
+ mapData.settings.PlayerData.some(pData => pData && pData.Resources !== undefined);
+
+ this.setEnabled(!isScenario && !this.perPlayer);
+
+ if (this.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);
+ }
+
+ getAutocompleteEntries()
+ {
+ return g_StartingResources.Title;
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.StartingResources = g_StartingResources.Resources[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ 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;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/StartingResources.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/VictoryConditionCheckbox.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/VictoryConditionCheckbox.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/VictoryConditionCheckbox.js (revision 23374)
@@ -0,0 +1,105 @@
+/**
+ * This is an abstract class instantiated per defined VictoryCondition.
+ */
+class VictoryConditionCheckbox extends GameSettingControlCheckbox
+{
+ constructor(victoryCondition, ...args)
+ {
+ super(...args);
+
+ this.victoryCondition = victoryCondition;
+
+ this.setTitle(this.victoryCondition.Title);
+ this.setTooltip(this.victoryCondition.Description);
+ }
+
+ onMapChange(mapData)
+ {
+ 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);
+ }
+
+ 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();
+ 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);
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/VictoryConditionCheckbox.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/NetMessages.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js (revision 23374)
@@ -0,0 +1,71 @@
+/**
+ * This class enables other classes to subscribe to specific CNetMessage types (see NetMessage.h, NetMessages.h) sent by the CNetServer.
+ */
+class NetMessages
+{
+ constructor(gamesetupPage)
+ {
+ this.netMessageHandlers = {};
+
+ for (let messageType of this.MessageTypes)
+ this.netMessageHandlers[messageType] = new Set();
+
+ this.registerNetMessageHandler("netwarn", addNetworkWarning);
+
+ Engine.GetGUIObjectByName("netMessages").onTick = this.onTick.bind(this);
+ gamesetupPage.registerClosePageHandler(this.onClosePage.bind(this));
+ }
+
+ registerNetMessageHandler(messageType, handler)
+ {
+ if (this.netMessageHandlers[messageType])
+ this.netMessageHandlers[messageType].add(handler);
+ else
+ error("Unknown net message type: " + uneval(messageType));
+ }
+
+ unregisterNetMessageHandler(messageType, handler)
+ {
+ if (this.netMessageHandlers[messageType])
+ this.netMessageHandlers[messageType].delete(handler);
+ else
+ error("Unknown net message type: " + uneval(messageType));
+ }
+
+ onTick()
+ {
+ while (true)
+ {
+ let message = Engine.PollNetworkClient();
+ if (!message)
+ break;
+
+ log("Net message: " + uneval(message));
+
+ if (this.netMessageHandlers[message.type])
+ for (let handler of this.netMessageHandlers[message.type])
+ handler(message);
+ else
+ error("Unrecognized net message type " + message.type);
+ }
+ }
+
+ onClosePage()
+ {
+ Engine.DisconnectNetworkGame();
+ }
+}
+
+/**
+ * Messages types are present here if and only if they are sent by NetClient.cpp.
+ */
+NetMessages.prototype.MessageTypes = [
+ "chat",
+ "ready",
+ "gamesetup",
+ "kicked",
+ "netstatus",
+ "netwarn",
+ "players",
+ "start"
+];
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.js (revision 23374)
@@ -0,0 +1,19 @@
+class LobbyButton
+{
+ constructor()
+ {
+ this.lobbyButton = Engine.GetGUIObjectByName("lobbyButton");
+ this.lobbyButton.tooltip = this.Tooltip;
+ this.lobbyButton.onPress = this.onPress.bind(this);
+ this.lobbyButton.hidden = !Engine.HasXmppClient();
+ }
+
+ onPress()
+ {
+ if (Engine.HasXmppClient())
+ Engine.PushGuiPage("page_lobby.xml", { "dialog": true });
+ }
+}
+
+LobbyButton.prototype.Tooltip =
+ translate("Show the multiplayer lobby in a dialog window.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/LobbyButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetCivsButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetCivsButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetCivsButton.js (revision 23374)
@@ -0,0 +1,36 @@
+class ResetCivsButton
+{
+ constructor(gameSettingsControl)
+ {
+ this.gameSettingsControl = 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);
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (g_GameAttributes.mapType)
+ this.civResetButton.hidden = g_GameAttributes.mapType == "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;
+
+ 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";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ResetCivsButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputAutocomplete.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputAutocomplete.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputAutocomplete.js (revision 23374)
@@ -0,0 +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));
+ }
+
+ 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;
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatInputAutocomplete.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientKicked.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientKicked.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientKicked.js (revision 23374)
@@ -0,0 +1,25 @@
+ChatMessageEvents.ClientKicked = class
+{
+ constructor(chatMessagesPanel, netMessages)
+ {
+ this.chatMessagesPanel = chatMessagesPanel;
+
+ this.messageArgs = {};
+
+ netMessages.registerNetMessageHandler("kicked", this.onClientKicked.bind(this));
+ }
+
+ onClientKicked(message)
+ {
+ this.messageArgs.username = message.username;
+ this.chatMessagesPanel.addStatusMessage(sprintf(
+ message.banned ? this.BannedMessage : this.KickedMessage,
+ this.messageArgs));
+ }
+};
+
+ChatMessageEvents.ClientKicked.prototype.KickedMessage =
+ translate("%(username)s has been kicked");
+
+ChatMessageEvents.ClientKicked.prototype.BannedMessage =
+ translate("%(username)s has been banned");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientKicked.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.js (revision 23374)
@@ -0,0 +1,26 @@
+/**
+ * Properties of this prototype are classes that subscribe to one or more events and
+ * construct a formatted chat message to be displayed on that event.
+ *
+ * Important: Apply escapeText on player provided input to avoid players breaking the game for everybody.
+ */
+class ChatMessageEvents
+{
+}
+
+class ChatPanel
+{
+ constructor(gameSettingControlManager, gameSettingsControl, netMessages, playerAssignmentsControl, readyControl, gameSettingsPanel)
+ {
+ this.statusMessageFormat = new StatusMessageFormat();
+
+ this.chatMessagesPanel = new ChatMessagesPanel(gameSettingsPanel);
+ this.chatInputAutocomplete = new ChatInputAutocomplete(gameSettingControlManager, gameSettingsControl, playerAssignmentsControl);
+ this.chatInputPanel = new ChatInputPanel(netMessages, this.chatInputAutocomplete);
+
+ this.chatMessageEvents = [];
+ for (let name in ChatMessageEvents)
+ this.chatMessageEvents.push(new ChatMessageEvents[name](
+ this.chatMessagesPanel, netMessages, gameSettingsControl, playerAssignmentsControl, readyControl));
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatPanel.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.js (revision 23374)
@@ -0,0 +1,27 @@
+class GameDescription
+{
+ constructor(mapCache, gameSettingTabs, gameSettingsControl)
+ {
+ this.mapCache = mapCache;
+
+ this.gameDescriptionFrame = Engine.GetGUIObjectByName("gameDescriptionFrame");
+ this.gameDescription = Engine.GetGUIObjectByName("gameDescription");
+
+ gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+ gameSettingTabs.registerTabsResizeHandler(this.onTabsResize.bind(this));
+ }
+
+ onTabsResize(settingsTabButtonsFrame)
+ {
+ let size = this.gameDescriptionFrame.size;
+ size.top = settingsTabButtonsFrame.size.bottom + this.Margin;
+ this.gameDescriptionFrame.size = size;
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.gameDescription.caption = getGameDescription(this.mapCache);
+ }
+}
+
+GameDescription.prototype.Margin = 3;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameDescription.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.js (revision 23374)
@@ -0,0 +1,41 @@
+class GameSettingWarning
+{
+ constructor(gameSettingsControl, cancelButton)
+ {
+ if (!g_IsNetworked)
+ return;
+
+ this.gameSettingWarning = Engine.GetGUIObjectByName("gameSettingWarning");
+
+ gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+ cancelButton.registerCancelButtonResizeHandler(this.onCancelButtonResize.bind(this));
+ }
+
+ onGameAttributesBatchChange()
+ {
+ let caption =
+ g_GameAttributes.settings.CheatsEnabled ?
+ this.CheatsEnabled :
+ g_GameAttributes.settings.RatingEnabled ?
+ 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.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.js (revision 23374)
@@ -0,0 +1,97 @@
+class GameSettingTabs
+{
+ constructor(gamesetupPage, lobbyButton)
+ {
+ this.lobbyButton = lobbyButton;
+
+ this.tabSelectHandlers = new Set();
+ this.tabsResizeHandlers = new Set();
+
+ this.settingsTabButtonsFrame = Engine.GetGUIObjectByName("settingTabButtonsFrame");
+
+ for (let tab in g_GameSettingsLayout)
+ g_GameSettingsLayout[tab].tooltip =
+ sprintf(this.ToggleTooltip, { "name": g_GameSettingsLayout[tab].label }) +
+ colorizeHotkey("\n" + this.HotkeyDownTooltip, this.ConfigNameHotkeyDown) +
+ colorizeHotkey("\n" + this.HotkeyUpTooltip, this.ConfigNameHotkeyUp);
+
+ gamesetupPage.registerLoadHandler(this.onLoad.bind(this));
+ Engine.SetGlobalHotkey("cancel", selectPanel);
+ }
+
+ registerTabsResizeHandler(handler)
+ {
+ this.tabsResizeHandlers.add(handler);
+ }
+
+ registerTabSelectHandler(handler)
+ {
+ this.tabSelectHandlers.add(handler);
+ }
+
+ onLoad()
+ {
+ placeTabButtons(
+ g_GameSettingsLayout,
+ this.TabButtonHeight,
+ this.TabButtonMargin,
+ this.onTabPress.bind(this),
+ this.onTabSelect.bind(this));
+
+ this.resize();
+
+ if (!g_IsController)
+ selectPanel();
+ }
+
+ resize()
+ {
+ let size = this.settingsTabButtonsFrame.size;
+ size.bottom = size.top + g_GameSettingsLayout.length * (this.TabButtonHeight + this.TabButtonMargin);
+
+ if (!this.lobbyButton.lobbyButton.hidden)
+ {
+ let lobbyButtonSize = this.lobbyButton.lobbyButton.parent.size;
+ size.right -= lobbyButtonSize.right - lobbyButtonSize.left + this.LobbyButtonMargin;
+ }
+ this.settingsTabButtonsFrame.size = size;
+
+ for (let handler of this.tabsResizeHandlers)
+ handler(this.settingsTabButtonsFrame);
+ }
+
+ onTabPress(category)
+ {
+ selectPanel(category == g_TabCategorySelected ? undefined : category);
+ }
+
+ onTabSelect()
+ {
+ for (let handler of this.tabSelectHandlers)
+ handler();
+ }
+}
+
+GameSettingTabs.prototype.ToggleTooltip =
+ translate("Toggle the %(name)s settings tab.");
+
+GameSettingTabs.prototype.HotkeyUpTooltip =
+ translate("Use %(hotkey)s to move a settings tab up.");
+
+GameSettingTabs.prototype.HotkeyDownTooltip =
+ translate("Use %(hotkey)s to move a settings tab down.");
+
+GameSettingTabs.prototype.ConfigNameHotkeyUp =
+ "tab.next";
+
+GameSettingTabs.prototype.ConfigNameHotkeyDown =
+ "tab.prev";
+
+GameSettingTabs.prototype.TabButtonHeight = 30;
+
+GameSettingTabs.prototype.TabButtonMargin = 4;
+
+/**
+ * Horizontal space between tab buttons and lobby button.
+ */
+GameSettingTabs.prototype.LobbyButtonMargin = 8;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.xml (revision 23374)
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.js (revision 23374)
@@ -0,0 +1,187 @@
+class GameSettingsPanel
+{
+ constructor(gamesetupPage, gameSettingTabs, gameSettingsControl, gameSettingControlManager)
+ {
+ 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.onTabSelect.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.onTabSelect.bind(this));
+ gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+ gamesetupPage.registerLoadHandler(this.onLoad.bind(this));
+ }
+
+ registerGameSettingsPanelResizeHandler(handler)
+ {
+ this.gameSettingsPanelResizeHandlers.add(handler);
+ }
+
+ onLoad()
+ {
+ for (let handler of this.gameSettingsPanelResizeHandlers)
+ handler(this.settingsPanelFrame);
+ }
+
+ onGameAttributesBatchChange()
+ {
+ this.gameSettingControlManager.updateSettingVisibility();
+ this.positionSettings();
+ }
+
+ onTabSelect()
+ {
+ 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;
+
+ for (let handler of this.gameSettingsPanelResizeHandlers)
+ handler(this.settingsPanelFrame);
+ }
+
+ /**
+ * 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.settingTabButtonsFrame.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 = 32;
+
+/**
+ * Horizontal space between two setting frames.
+ */
+GameSettingsPanel.prototype.SettingMarginRight = 10;
+
+/**
+ * Vertical space between two setting frames.
+ */
+GameSettingsPanel.prototype.SettingMarginBottom = 2;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsPanel.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicDuration.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicDuration.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicDuration.js (revision 23374)
@@ -0,0 +1,85 @@
+GameSettingControls.RelicDuration = class extends GameSettingControlDropdown
+{
+ constructor(...args)
+ {
+ super(...args);
+
+ this.values = prepareForDropdown(g_Settings.VictoryDurations);
+
+ this.dropdown.list = this.values.Title;
+ this.dropdown.list_data = this.values.Duration;
+
+ this.available = false;
+ }
+
+ onMapChange(mapData)
+ {
+ this.setEnabled(g_GameAttributes.mapType != "scenario");
+
+ let mapValue =
+ mapData &&
+ mapData.settings &&
+ mapData.settings.VictoryConditions &&
+ mapData.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1 &&
+ mapData.settings.RelicDuration || undefined;
+
+ 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();
+ }
+
+ onGameAttributesChange()
+ {
+ if (!g_GameAttributes.settings.VictoryConditions)
+ return;
+
+ this.available = g_GameAttributes.settings.VictoryConditions.indexOf(this.NameCaptureTheRelic) != -1;
+
+ if (this.available)
+ {
+ if (g_GameAttributes.settings.RelicDuration === undefined)
+ {
+ g_GameAttributes.settings.RelicDuration = this.values.Duration[this.values.Default];
+ 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)
+ this.setSelectedValue(g_GameAttributes.settings.RelicDuration);
+ }
+
+ onSelectionChange(itemIdx)
+ {
+ g_GameAttributes.settings.RelicDuration = this.values.Duration[itemIdx];
+ this.gameSettingsControl.updateGameAttributes();
+ this.gameSettingsControl.setNetworkGameAttributes();
+ }
+};
+
+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";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Dropdowns/RelicDuration.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Seed.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Seed.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/GameSettings/Single/Seed.js (revision 23374)
@@ -0,0 +1,12 @@
+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/GameSettings/Single/Seed.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 (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js (revision 23374)
@@ -0,0 +1,146 @@
+/**
+ * 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, gamesetupPage, netMessages, gameSettingsControl, playerAssignmentsControl, mapCache)
+ {
+ this.mapCache = mapCache;
+
+ this.serverName = initData.serverName;
+ this.serverPort = initData.serverPort;
+ this.stunEndpoint = initData.stunEndpoint;
+
+ this.mods = JSON.stringify(Engine.GetEngineInfo().mods);
+ this.timer = undefined;
+
+ // Only send a lobby update when its data changed
+ this.lastStanza = undefined;
+
+ // Events
+ let sendImmediately = this.sendImmediately.bind(this);
+ playerAssignmentsControl.registerClientJoinHandler(sendImmediately);
+ playerAssignmentsControl.registerClientLeaveHandler(sendImmediately);
+
+ gamesetupPage.registerClosePageHandler(this.onClosePage.bind(this));
+ gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+ netMessages.registerNetMessageHandler("start", this.onGameStart.bind(this));
+ }
+
+ onGameAttributesBatchChange()
+ {
+ 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 gamesettings to the lobbybot 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 gamesettings to the lobbybot 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,
+ "port": this.serverPort,
+ "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(","),
+ "nbp": clients.connectedPlayers,
+ "maxnbp": g_GameAttributes.settings.PlayerData.length,
+ "players": clients.list,
+ "stunIP": this.stunEndpoint ? this.stunEndpoint.ip : "",
+ "stunPort": this.stunEndpoint ? this.stunEndpoint.port : "",
+ "mods": this.mods
+ };
+
+ // 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])
+ ++connectedPlayers;
+ else
+ pData.Team = "observer";
+
+ playerData.push(pData);
+ }
+
+ return {
+ "list": playerDataToStringifiedTeamList(playerData),
+ "connectedPlayers": connectedPlayers
+ };
+ }
+}
+
+/**
+ * Send the current gamesettings to the lobby bot if the settings didn't change for this number of milliseconds.
+ */
+GameRegisterStanza.prototype.Timeout = 2000;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/NetMessages/GameRegisterStanza.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CivInfoButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CivInfoButton.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CivInfoButton.js (revision 23374)
@@ -0,0 +1,48 @@
+class CivInfoButton
+{
+ constructor()
+ {
+ this.civInfo = {
+ "civ": "",
+ "page": "page_civinfo.xml"
+ };
+
+ let civInfoButton = Engine.GetGUIObjectByName("civInfoButton");
+ civInfoButton.onPress = this.onPress.bind(this);
+ civInfoButton.tooltip =
+ sprintf(translate(this.Tooltip), {
+ "hotkey_civinfo": colorizeHotkey("%(hotkey)s", "civinfo"),
+ "hotkey_structree": colorizeHotkey("%(hotkey)s", "structree")
+ });
+
+ Engine.SetGlobalHotkey("structree", this.openPage.bind(this, "page_structree.xml"));
+ Engine.SetGlobalHotkey("civinfo", this.openPage.bind(this, "page_civinfo.xml"));
+ }
+
+ onPress()
+ {
+ this.openPage(this.civInfo.page);
+ }
+
+ openPage(page)
+ {
+ Engine.PushGuiPage(
+ page,
+ { "civ": this.civInfo.civ },
+ this.storeCivInfoPage.bind(this));
+ }
+
+ storeCivInfoPage(data)
+ {
+ if (data.nextPage)
+ Engine.PushGuiPage(
+ data.nextPage,
+ { "civ": data.civ },
+ this.storeCivInfoPage.bind(this));
+ else
+ this.civInfo = data;
+ }
+}
+
+CivInfoButton.prototype.Tooltip =
+ translate("%(hotkey_civinfo)s / %(hotkey_structree)s: View History / Structure Tree\nLast opened will be reopened on click.");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/CivInfoButton.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.xml (revision 23374)
@@ -0,0 +1,8 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/ReadyButton.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.xml (revision 23374)
@@ -0,0 +1,8 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Buttons/StartGameButton.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientConnection.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientConnection.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientConnection.js (revision 23374)
@@ -0,0 +1,30 @@
+ChatMessageEvents.ClientConnection = class
+{
+ constructor(chatMessagesPanel, netMessages, gameSettingsControl, playerAssignmentsControl)
+ {
+ this.chatMessagesPanel = chatMessagesPanel;
+
+ playerAssignmentsControl.registerClientJoinHandler(this.onClientJoin.bind(this));
+ playerAssignmentsControl.registerClientLeaveHandler(this.onClientLeave.bind(this));
+
+ this.args = {};
+ }
+
+ onClientJoin(newGUID, newAssignments)
+ {
+ this.args.username = newAssignments[newGUID].name;
+ this.chatMessagesPanel.addStatusMessage(sprintf(this.JoinText, this.args));
+ }
+
+ onClientLeave(guid)
+ {
+ this.args.username = g_PlayerAssignments[guid].name;
+ this.chatMessagesPanel.addStatusMessage(sprintf(this.LeaveText, this.args));
+ }
+};
+
+ChatMessageEvents.ClientConnection.prototype.JoinText =
+ translate("%(username)s has joined");
+
+ChatMessageEvents.ClientConnection.prototype.LeaveText =
+ translate("%(username)s has left");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessages/ClientConnection.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessagesPanel.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessagesPanel.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessagesPanel.js (revision 23374)
@@ -0,0 +1,81 @@
+/**
+ * This class stores and displays the chat history since the login and
+ * displays timestamps if enabled.
+ */
+class ChatMessagesPanel
+{
+ constructor(gameSettingsPanel)
+ {
+ this.gameSettingsPanel = gameSettingsPanel;
+
+ this.chatHistory = "";
+
+ this.statusMessageFormat = new StatusMessageFormat();
+
+ if (Engine.ConfigDB_GetValue("user", this.ConfigTimestamp) == "true")
+ this.timestampWrapper = new TimestampWrapper();
+
+ this.chatText = Engine.GetGUIObjectByName("chatText");
+ this.chatPanel = Engine.GetGUIObjectByName("chatPanel");
+ this.chatPanel.onWindowResized = this.onWindowResized.bind(this);
+ gameSettingsPanel.registerGameSettingsPanelResizeHandler(this.onGameSettingsPanelResize.bind(this));
+
+ // TODO: Remove global requirements by gui/common/network.js
+ g_NetworkCommands["/list"] = () => { this.addText(getUsernameList()); };
+ g_NetworkCommands["/clear"] = this.clearChatMessages.bind(this);
+ global.kickError = () => {};
+ }
+
+ addText(text)
+ {
+ if (this.timestampWrapper)
+ text = this.timestampWrapper.format(text);
+
+ this.chatHistory += this.chatHistory ? "\n" + text : text;
+ this.chatText.caption = this.chatHistory;
+ }
+
+ addStatusMessage(text)
+ {
+ this.addText(this.statusMessageFormat.format(text));
+ }
+
+ clearChatMessages()
+ {
+ this.chatHistory = "";
+ this.chatText.caption = "";
+ }
+
+ updateHidden()
+ {
+ let size = this.chatPanel.getComputedSize();
+ this.chatPanel.hidden = !g_IsNetworked || size.right - size.left < this.MinimumWidth;
+ }
+
+ onWindowResized()
+ {
+ this.updateHidden();
+ }
+
+ onGameSettingsPanelResize(settingsPanel)
+ {
+ let size = this.chatPanel.size;
+ size.right = settingsPanel.size.left + this.gameSettingsPanel.MaxColumnWidth + this.Margin;
+ this.chatPanel.size = size;
+
+ this.updateHidden();
+ }
+}
+
+/**
+ * Minimum amount of pixels required for the chat panel to be visible.
+ */
+ChatMessagesPanel.prototype.MinimumWidth = 96;
+
+/**
+ * Horizontal space between the chat window and the settings panel.
+ */
+ChatMessagesPanel.prototype.Margin = 10;
+
+ChatMessagesPanel.prototype.ConfigTimestamp =
+ "chat.timestamp";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/ChatMessagesPanel.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/TimestampWrapper.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/TimestampWrapper.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/TimestampWrapper.js (revision 23374)
@@ -0,0 +1,31 @@
+/**
+ * This class wraps a string with a timestamp dating to when the message was sent.
+ */
+class TimestampWrapper
+{
+ constructor()
+ {
+ this.timeArgs = {};
+ this.timestampArgs = {};
+ }
+
+ format(text)
+ {
+ this.timeArgs.time =
+ Engine.FormatMillisecondsIntoDateStringLocal(Date.now(), this.TimeFormat);
+
+ this.timestampArgs.time = sprintf(this.TimestampFormat, this.timeArgs);
+ this.timestampArgs.message = text;
+
+ return sprintf(this.TimestampedMessageFormat, this.timestampArgs);
+ }
+}
+
+TimestampWrapper.prototype.TimestampedMessageFormat =
+ translate("%(time)s %(message)s");
+
+TimestampWrapper.prototype.TimestampFormat =
+ translate("\\[%(time)s]");
+
+TimestampWrapper.prototype.TimeFormat =
+ translate("HH:mm");
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Chat/TimestampWrapper.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.xml (revision 23374)
@@ -0,0 +1,2 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingWarning.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.xml (revision 23374)
@@ -0,0 +1,5 @@
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/GameSettingsTabs.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js (revision 23374)
@@ -0,0 +1,24 @@
+class MapPreview
+{
+ constructor(gameSettingsControl, mapCache)
+ {
+ this.mapCache = mapCache;
+
+ this.mapInfoName = Engine.GetGUIObjectByName("mapInfoName");
+ this.mapPreview = Engine.GetGUIObjectByName("mapPreview");
+
+ gameSettingsControl.registerGameAttributesBatchChangeHandler(this.onGameAttributesBatchChange.bind(this));
+ }
+
+ onGameAttributesBatchChange()
+ {
+ if (!g_GameAttributes.map || !g_GameAttributes.mapType)
+ 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);
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.js (revision 23374)
@@ -0,0 +1,45 @@
+/**
+ * The TipsPanel shows some hints to newcomers.
+ * It is only shown in Singleplayer mode since the chat is shown instead in multiplayer mode.
+ */
+class TipsPanel
+{
+ constructor(gameSettingsPanel)
+ {
+ let available = !g_IsNetworked && Engine.ConfigDB_GetValue("user", this.Config) == "true";
+
+ this.spTips = Engine.GetGUIObjectByName("spTips");
+ this.spTips.hidden = !available;
+ if (!available)
+ return;
+
+ this.displaySPTips = Engine.GetGUIObjectByName("displaySPTips");
+ this.displaySPTips.onPress = this.onPress.bind(this);
+
+ Engine.GetGUIObjectByName("aiTips").caption =
+ Engine.TranslateLines(Engine.ReadFile(this.File));
+
+ gameSettingsPanel.registerGameSettingsPanelResizeHandler(this.onGameSettingsPanelResize.bind(this));
+ }
+
+ onPress()
+ {
+ Engine.ConfigDB_CreateAndWriteValueToFile(
+ "user",
+ this.Config,
+ String(this.displaySPTips.checked),
+ "config/user.cfg");
+ }
+
+ onGameSettingsPanelResize(settingsPanel)
+ {
+ this.spTips.hidden =
+ this.spTips.getComputedSize().right > settingsPanel.getComputedSize().left;
+ }
+}
+
+TipsPanel.prototype.File =
+ "gui/gamesetup/Panels/Tips.txt";
+
+TipsPanel.prototype.Config =
+ "gui.gamesetup.enabletips";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 23374)
@@ -1,2776 +1,50 @@
-const g_MatchSettings_SP = "config/matchsettings.json";
-const g_MatchSettings_MP = "config/matchsettings.mp.json";
-
-const g_Ceasefire = prepareForDropdown(g_Settings && g_Settings.Ceasefire);
+// 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_TriggerDifficulties = prepareForDropdown(g_Settings && g_Settings.TriggerDifficulties);
const g_PopulationCapacities = prepareForDropdown(g_Settings && g_Settings.PopulationCapacities);
const g_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources);
-const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations);
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
-var g_GameSpeeds = getGameSpeedChoices(false);
-
/**
* Offer users to select playable civs only.
* Load unselectable civs as they could appear in scenario maps.
*/
const g_CivData = loadCivData(false, false);
/**
- * Store civilization code and page (structree or history) opened in civilization info.
- */
-var g_CivInfo = {
- "civ": "",
- "page": "page_civinfo.xml"
-};
-
-/**
- * Highlight the "random" dropdownlist item.
- */
-var g_ColorRandom = "orange";
-
-/**
- * Color for regular dropdownlist items.
- */
-var g_ColorRegular = "white";
-
-/**
- * Color for "Unassigned"-placeholder item in the dropdownlist.
- */
-var g_PlayerAssignmentColors = {
- "player": g_ColorRegular,
- "observer": "170 170 250",
- "unassigned": "140 140 140",
- "AI": "70 150 70"
-};
-
-/**
- * Used for highlighting the sender of chat messages.
- */
-var g_SenderFontTag = { "font": "sans-bold-13" };
-
-/**
- * This yields [1, 2, ..., MaxPlayers].
- */
-var g_NumPlayersList = Array(g_MaxPlayers).fill(0).map((v, i) => i + 1);
-
-/**
- * Used for generating the botnames.
- */
-var g_RomanNumbers = [undefined, "I", "II", "III", "IV", "V", "VI", "VII", "VIII"];
-
-var g_PlayerTeamList = prepareForDropdown([{
- "label": translateWithContext("team", "None"),
- "id": -1
- }].concat(
- Array(g_MaxTeams).fill(0).map((v, i) => ({
- "label": i + 1,
- "id": i
- }))
- )
-);
-
-/**
- * Number of relics: [1, ..., NumCivs]
- */
-var g_RelicCountList = Object.keys(g_CivData).map((civ, i) => i + 1);
-
-var g_PlayerCivList = g_CivData && prepareForDropdown([{
- "name": translateWithContext("civilization", "Random"),
- "tooltip": translate("Picks one civilization at random when the game starts."),
- "color": g_ColorRandom,
- "code": "random"
- }].concat(
- Object.keys(g_CivData).filter(
- civ => g_CivData[civ].SelectableInGameSetup
- ).map(civ => ({
- "name": g_CivData[civ].Name,
- "tooltip": g_CivData[civ].History,
- "color": g_ColorRegular,
- "code": civ
- })).sort(sortNameIgnoreCase)
- )
-);
-
-/**
- * All selectable playercolors except gaia.
- */
-var g_PlayerColorPickerList = g_Settings && g_Settings.PlayerDefaults.slice(1).map(pData => pData.Color);
-
-/**
- * Directory containing all maps of the given type.
- */
-var g_MapPath = {
- "random": "maps/random/",
- "scenario": "maps/scenarios/",
- "skirmish": "maps/skirmishes/"
-};
-
-/**
- * Containing the colors to highlight the ready status of players,
- * the chat ready messages and
- * the tooltips and captions for the ready button
- */
-
-var g_ReadyData = [
- {
- "color": g_ColorRegular,
- "chat": translate("* %(username)s is not ready."),
- "caption": translate("I'm ready"),
- "tooltip": translate("State that you are ready to play.")
- },
- {
- "color": "green",
- "chat": translate("* %(username)s is ready!"),
- "caption": translate("Stay ready"),
- "tooltip": translate("Stay ready even when the game settings change.")
- },
- {
- "color": "150 150 250",
- "chat": "",
- "caption": translate("I'm not ready!"),
- "tooltip": translate("State that you are not ready to play.")
- }
-];
-
-/**
- * Processes a CNetMessage (see NetMessage.h, NetMessages.h) sent by the CNetServer.
- */
-var g_NetMessageTypes = {
- "netstatus": msg => handleNetStatusMessage(msg),
- "netwarn": msg => addNetworkWarning(msg),
- "gamesetup": msg => handleGamesetupMessage(msg),
- "players": msg => handlePlayerAssignmentMessage(msg),
- "ready": msg => handleReadyMessage(msg),
- "start": msg => handleGamestartMessage(msg),
- "kicked": msg => addChatMessage({
- "type": msg.banned ? "banned" : "kicked",
- "username": msg.username
- }),
- "chat": msg => addChatMessage({ "type": "chat", "guid": msg.guid, "text": msg.text }),
-};
-
-var g_FormatChatMessage = {
- "system": (msg, user) => systemMessage(msg.text),
- "settings": (msg, user) => systemMessage(translate('Game settings have been changed')),
- "connect": (msg, user) => systemMessage(sprintf(translate("%(username)s has joined"), { "username": user })),
- "disconnect": (msg, user) => systemMessage(sprintf(translate("%(username)s has left"), { "username": user })),
- "kicked": (msg, user) => systemMessage(sprintf(translate("%(username)s has been kicked"), { "username": user })),
- "banned": (msg, user) => systemMessage(sprintf(translate("%(username)s has been banned"), { "username": user })),
- "chat": (msg, user) => sprintf(translate("%(username)s %(message)s"), {
- "username": setStringTags(sprintf(translate("<%(username)s>"), { "username": user }), g_SenderFontTag),
- "message": escapeText(msg.text || "")
- }),
- "ready": (msg, user) => sprintf(g_ReadyData[msg.status].chat, { "username": user }),
- "clientlist": (msg, user) => getUsernameList(),
-};
-
-var g_MapFilters = [
- {
- "id": "default",
- "name": translateWithContext("map filter", "Default"),
- "tooltip": translateWithContext("map filter", "All maps except naval and demo maps."),
- "filter": mapKeywords => mapKeywords.every(keyword => ["naval", "demo", "hidden"].indexOf(keyword) == -1),
- "Default": true
- },
- {
- "id": "naval",
- "name": translate("Naval Maps"),
- "tooltip": translateWithContext("map filter", "Maps where ships are needed to reach the enemy."),
- "filter": mapKeywords => mapKeywords.indexOf("naval") != -1
- },
- {
- "id": "demo",
- "name": translate("Demo Maps"),
- "tooltip": translateWithContext("map filter", "These maps are not playable but for demonstration purposes only."),
- "filter": mapKeywords => mapKeywords.indexOf("demo") != -1
- },
- {
- "id": "new",
- "name": translate("New Maps"),
- "tooltip": translateWithContext("map filter", "Maps that are brand new in this release of the game."),
- "filter": mapKeywords => mapKeywords.indexOf("new") != -1
- },
- {
- "id": "trigger",
- "name": translate("Trigger Maps"),
- "tooltip": translateWithContext("map filter", "Maps that come with scripted events and potentially spawn enemy units."),
- "filter": mapKeywords => mapKeywords.indexOf("trigger") != -1
- },
- {
- "id": "all",
- "name": translate("All Maps"),
- "tooltip": translateWithContext("map filter", "Every map of the chosen maptype."),
- "filter": mapKeywords => true
- },
-];
-
-/**
- * This contains only filters that have at least one map matching them.
- */
-var g_MapFilterList;
-
-/**
- * Array of biome identifiers supported by the currently selected map.
- */
-var g_BiomeList;
-
-/**
- * Array of trigger difficulties identifiers supported by the currently selected map.
- */
-var g_TriggerDifficultyList;
-
-/**
* Whether this is a single- or multiplayer match.
*/
const g_IsNetworked = Engine.HasNetClient();
/**
* Is this user in control of game settings (i.e. is a network server, or offline player).
*/
const g_IsController = !g_IsNetworked || Engine.HasNetServer();
/**
- * Whether this is a tutorial.
- */
-var g_IsTutorial;
-
-/**
- * To report the game to the lobby bot.
- */
-var g_ServerName;
-var g_ServerPort;
-
-/**
- * IP address and port of the STUN endpoint.
- */
-var g_StunEndpoint;
-
-/**
- * States whether the GUI is currently updated in response to network messages instead of user input
- * and therefore shouldn't send further messages to the network.
+ * Central data storing all settings relevant to the map generation and simulation.
*/
-var g_IsInGuiUpdate = false;
-
-/**
- * Whether the current player is ready to start the game.
- * 0 - not ready
- * 1 - ready
- * 2 - stay ready
- */
-var g_IsReady = 0;
-
-/**
- * Ignore duplicate ready commands on init.
- */
-var g_ReadyInit = true;
-
-/**
- * If noone has changed the ready status, we have no need to spam the settings changed message.
- *
- * <=0 - Suppressed settings message
- * 1 - Will show settings message
- * 2 - Host's initial ready, suppressed settings message
- */
-var g_ReadyChanged = 2;
-
-/**
- * Used to prevent calling resetReadyData when starting a game or doubleclicking on the "Start Game" button.
- */
-var g_GameStarted = false;
-
-/**
- * Selectable options (player, AI, unassigned) in the player assignment dropdowns and
- * their colorized, textual representation.
- */
-var g_PlayerAssignmentList = {};
+var g_GameAttributes = {};
/**
* Remembers which clients are assigned to which player slots and whether they are ready.
* The keys are GUIDs or "local" in single-player.
*/
var g_PlayerAssignments = {};
-var g_DefaultPlayerData = [];
-
-var g_GameAttributes = { "settings": {} };
-
-/**
- * Array of strings formatted as displayed, including playername.
- */
-var g_ChatMessages = [];
-
-/**
- * Minimum amount of pixels required for the chat panel to be visible.
- */
-var g_MinChatWidth = 96;
-
-/**
- * Horizontal space between chat window and settings.
- */
-var g_ChatSettingsMargin = 10;
-
-/**
- * Filename and translated title of all maps, given the currently selected
- * maptype and filter. Sorted by title, shown in the dropdown.
- */
-var g_MapSelectionList = [];
-
-/**
- * Cache containing the mapsettings. Just-in-time loading.
- */
-var g_MapData = {};
-
-/**
- * Wait one tick before initializing the GUI objects and
- * don't process netmessages prior to that.
- */
-var g_LoadingState = 0;
-
-/**
- * Send the current gamesettings to the lobby bot if the settings didn't change for this number of seconds.
- */
-var g_GameStanzaTimeout = 2;
-
-/**
- * Index of the GUI timer.
- */
-var g_GameStanzaTimer;
-
-/**
- * Only send a lobby update if something actually changed.
- */
-var g_LastGameStanza;
-
-/**
- * Remembers if the current player viewed the AI settings of some playerslot.
- */
-var g_LastViewedAIPlayer = -1;
-
-/**
- * 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".
- */
-var g_PopulationCapacityRecommendation = 1200;
-
-/**
- * Horizontal space between tab buttons and lobby button.
- */
-var g_LobbyButtonSpacing = 8;
-
-/**
- * Vertical size of a tab button.
- */
-var g_TabButtonHeight = 30;
-
-/**
- * Vertical space between two tab buttons.
- */
-var g_TabButtonDist = 4;
-
-/**
- * Vertical size of a setting object.
- */
-var g_SettingHeight = 32;
-
-/**
- * Vertical space between two setting objects.
- */
-var g_SettingDist = 2;
-
-/**
- * Maximum width of a column in the settings panel.
- */
-var g_MaxColumnWidth = 470;
-
-/**
- * Pixels per millisecond the settings panel slides when opening/closing.
- */
-var g_SlideSpeed = 1.2;
-
-/**
- * Store last tick time.
- */
-var g_LastTickTime = Date.now();
-
-/**
- * Order in which the GUI elements will be shown.
- * All valid settings are required to appear here.
- */
-var g_SettingsTabsGUI = [
- {
- "label": translateWithContext("Match settings tab name", "Map"),
- "settings": [
- "mapType",
- "mapFilter",
- "mapSelection",
- "mapSize",
- "biome",
- "triggerDifficulty",
- "nomad",
- "disableTreasures",
- "exploreMap",
- "revealMap"
- ]
- },
- {
- "label": translateWithContext("Match settings tab name", "Player"),
- "settings": [
- "numPlayers",
- "populationCap",
- "startingResources",
- "disableSpies",
- "enableCheats"
- ]
- },
- {
- "label": translateWithContext("Match settings tab name", "Game Type"),
- "settings": [
- ...g_VictoryConditions.map(victoryCondition => victoryCondition.Name),
- "relicCount",
- "relicDuration",
- "wonderDuration",
- "regicideGarrison",
- "gameSpeed",
- "ceasefire",
- "lockTeams",
- "lastManStanding",
- "enableRating"
- ]
- }
-];
-
-/**
- * Contains the logic of all multiple-choice gamesettings.
- *
- * Logic
- * ids - Array of identifier strings that indicate the selected value.
- * default - Returns the index of the default value (not the value itself).
- * defined - Whether a value for the setting is actually specified.
- * get - The identifier of the currently selected value.
- * select - Saves and processes the value of the selected index of the ids array.
- *
- * GUI
- * title - The caption shown in the label.
- * tooltip - A description shown when hovering the dropdown or a specific item.
- * labels - Array of translated strings selectable for this dropdown.
- * colors - Optional array of colors to tint the according dropdown items with.
- * hidden - If hidden, both the label and dropdown won't be visible. (default: false)
- * enabled - Only the label will be shown if the setting is disabled. (default: true)
- * autocomplete - Marks whether to autocomplete translated values of the string. (default: undefined)
- * If not undefined, must be a number that denotes the priority (higher numbers come first).
- * If undefined, still autocompletes the translated title of the setting.
- * initOrder - Settings with lower values will be initialized first.
- */
-var g_Dropdowns = {
- "mapType": {
- "title": () => translate("Map Type"),
- "tooltip": (hoverIdx) => g_MapTypes.Description[hoverIdx] || translate("Select a map type."),
- "labels": () => g_MapTypes.Title,
- "ids": () => g_MapTypes.Name,
- "default": () => g_MapTypes.Default,
- "defined": () => g_GameAttributes.mapType !== undefined,
- "get": () => g_GameAttributes.mapType,
- "select": (itemIdx) => {
- g_MapData = {};
- g_GameAttributes.mapType = g_MapTypes.Name[itemIdx];
- g_GameAttributes.mapPath = g_MapPath[g_GameAttributes.mapType];
- delete g_GameAttributes.map;
-
- if (g_GameAttributes.mapType != "scenario")
- g_GameAttributes.settings = {
- "PlayerData": clone(g_DefaultPlayerData.slice(0, 4))
- };
-
- reloadMapFilterList();
- },
- "autocomplete": 0,
- "initOrder": 1
- },
- "mapFilter": {
- "title": () => translate("Map Filter"),
- "tooltip": (hoverIdx) => g_MapFilterList.tooltip[hoverIdx] || translate("Select a map filter."),
- "labels": () => g_MapFilterList.name,
- "ids": () => g_MapFilterList.id,
- "default": () => g_MapFilterList.Default,
- "defined": () => g_MapFilterList.id.indexOf(g_GameAttributes.mapFilter || "") != -1,
- "get": () => g_GameAttributes.mapFilter,
- "select": (itemIdx) => {
- g_GameAttributes.mapFilter = g_MapFilterList.id[itemIdx];
- delete g_GameAttributes.map;
- reloadMapList();
- },
- "autocomplete": 0,
- "initOrder": 2
- },
- "mapSelection": {
- "title": () => translate("Select Map"),
- "tooltip": (hoverIdx) => g_MapSelectionList.description[hoverIdx] || translate("Select a map to play on."),
- "labels": () => g_MapSelectionList.name,
- "colors": () => g_MapSelectionList.color,
- "ids": () => g_MapSelectionList.file,
- "default": () => 0,
- "defined": () => g_GameAttributes.map !== undefined,
- "get": () => g_GameAttributes.map,
- "select": (itemIdx) => {
- selectMap(g_MapSelectionList.file[itemIdx]);
- },
- "autocomplete": 0,
- "initOrder": 3
- },
- "mapSize": {
- "title": () => translate("Map Size"),
- "tooltip": (hoverIdx) => g_MapSizes.Tooltip[hoverIdx] || translate("Select map size. (Larger sizes may reduce performance.)"),
- "labels": () => g_MapSizes.Name,
- "ids": () => g_MapSizes.Tiles,
- "default": () => g_MapSizes.Default,
- "defined": () => g_GameAttributes.settings.Size !== undefined,
- "get": () => g_GameAttributes.settings.Size,
- "select": (itemIdx) => {
- g_GameAttributes.settings.Size = g_MapSizes.Tiles[itemIdx];
- },
- "hidden": () => g_GameAttributes.mapType != "random",
- "autocomplete": 0,
- "initOrder": 1000
- },
- "biome": {
- "title": () => translate("Biome"),
- "tooltip": (hoverIdx) => g_BiomeList && g_BiomeList.Description && g_BiomeList.Description[hoverIdx] || translate("Select the flora and fauna."),
- "labels": () => g_BiomeList ? g_BiomeList.Title : [],
- "colors": (itemIdx) => g_BiomeList ? g_BiomeList.Color : [],
- "ids": () => g_BiomeList ? g_BiomeList.Id : [],
- "default": () => 0,
- "defined": () => g_GameAttributes.settings.Biome !== undefined,
- "get": () => g_GameAttributes.settings.Biome,
- "select": (itemIdx) => {
- g_GameAttributes.settings.Biome = g_BiomeList && g_BiomeList.Id[itemIdx];
- },
- "hidden": () => !g_BiomeList,
- "autocomplete": 0,
- "initOrder": 1000
- },
- "numPlayers": {
- "title": () => translate("Number of Players"),
- "tooltip": (hoverIdx) => translate("Select number of players."),
- "labels": () => g_NumPlayersList,
- "ids": () => g_NumPlayersList,
- "default": () => g_MaxPlayers - 1,
- "defined": () => g_GameAttributes.settings.PlayerData !== undefined,
- "get": () => g_GameAttributes.settings.PlayerData.length,
- "enabled": () => g_GameAttributes.mapType == "random",
- "select": (itemIdx) => {
- let num = itemIdx + 1;
- let pData = g_GameAttributes.settings.PlayerData;
- g_GameAttributes.settings.PlayerData =
- num > pData.length ?
- pData.concat(clone(g_DefaultPlayerData.slice(pData.length, num))) :
- pData.slice(0, num);
- unassignInvalidPlayers(num);
- sanitizePlayerData(g_GameAttributes.settings.PlayerData);
- },
- "initOrder": 1000
- },
- "populationCap": {
- "title": () => translate("Population Cap"),
- "tooltip": (hoverIdx) => {
-
- let popCap = g_PopulationCapacities.Population[hoverIdx];
- let players = g_GameAttributes.settings.PlayerData.length;
-
- if (hoverIdx == -1 || popCap * players <= g_PopulationCapacityRecommendation)
- return translate("Select population limit.");
-
- return coloredText(
- sprintf(translate("Warning: There might be performance issues if all %(players)s players reach %(popCap)s population."), {
- "players": players,
- "popCap": popCap
- }),
- "orange");
- },
- "labels": () => g_PopulationCapacities.Title,
- "ids": () => g_PopulationCapacities.Population,
- "default": () => g_PopulationCapacities.Default,
- "defined": () => g_GameAttributes.settings.PopulationCap !== undefined,
- "get": () => g_GameAttributes.settings.PopulationCap,
- "select": (itemIdx) => {
- g_GameAttributes.settings.PopulationCap = g_PopulationCapacities.Population[itemIdx];
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "startingResources": {
- "title": () => translate("Starting Resources"),
- "tooltip": (hoverIdx) => {
- return hoverIdx >= 0 ?
- sprintf(translate("Initial amount of each resource: %(resources)s."), {
- "resources": g_StartingResources.Resources[hoverIdx]
- }) :
- translate("Select the game's starting resources.");
- },
- "labels": () => g_StartingResources.Title,
- "ids": () => g_StartingResources.Resources,
- "default": () => g_StartingResources.Default,
- "defined": () => g_GameAttributes.settings.StartingResources !== undefined,
- "get": () => g_GameAttributes.settings.StartingResources,
- "select": (itemIdx) => {
- g_GameAttributes.settings.StartingResources = g_StartingResources.Resources[itemIdx];
- },
- "hidden": () => g_GameAttributes.mapType == "scenario",
- "autocomplete": 0,
- "initOrder": 1000
- },
- "ceasefire": {
- "title": () => translate("Ceasefire"),
- "tooltip": (hoverIdx) => translate("Set time where no attacks are possible."),
- "labels": () => g_Ceasefire.Title,
- "ids": () => g_Ceasefire.Duration,
- "default": () => g_Ceasefire.Default,
- "defined": () => g_GameAttributes.settings.Ceasefire !== undefined,
- "get": () => g_GameAttributes.settings.Ceasefire,
- "select": (itemIdx) => {
- g_GameAttributes.settings.Ceasefire = g_Ceasefire.Duration[itemIdx];
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "relicCount": {
- "title": () => translate("Relic Count"),
- "tooltip": (hoverIdx) => 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."),
- "labels": () => g_RelicCountList,
- "ids": () => g_RelicCountList,
- "default": () => g_RelicCountList.indexOf(2),
- "defined": () => g_GameAttributes.settings.RelicCount !== undefined,
- "get": () => g_GameAttributes.settings.RelicCount,
- "select": (itemIdx) => {
- g_GameAttributes.settings.RelicCount = g_RelicCountList[itemIdx];
- },
- "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("capture_the_relic") == -1,
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "relicDuration": {
- "title": () => translate("Relic Duration"),
- "tooltip": (hoverIdx) => translate("Minutes until the player has achieved Relic Victory."),
- "labels": () => g_VictoryDurations.Title,
- "ids": () => g_VictoryDurations.Duration,
- "default": () => g_VictoryDurations.Default,
- "defined": () => g_GameAttributes.settings.RelicDuration !== undefined,
- "get": () => g_GameAttributes.settings.RelicDuration,
- "select": (itemIdx) => {
- g_GameAttributes.settings.RelicDuration = g_VictoryDurations.Duration[itemIdx];
- },
- "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("capture_the_relic") == -1,
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "wonderDuration": {
- "title": () => translate("Wonder Duration"),
- "tooltip": (hoverIdx) => translate("Minutes until the player has achieved Wonder Victory."),
- "labels": () => g_VictoryDurations.Title,
- "ids": () => g_VictoryDurations.Duration,
- "default": () => g_VictoryDurations.Default,
- "defined": () => g_GameAttributes.settings.WonderDuration !== undefined,
- "get": () => g_GameAttributes.settings.WonderDuration,
- "select": (itemIdx) => {
- g_GameAttributes.settings.WonderDuration = g_VictoryDurations.Duration[itemIdx];
- },
- "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("wonder") == -1,
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "gameSpeed": {
- "title": () => translate("Game Speed"),
- "tooltip": (hoverIdx) => translate("Select game speed."),
- "labels": () => g_GameSpeeds.Title,
- "ids": () => g_GameSpeeds.Speed,
- "default": () => g_GameSpeeds.Default,
- "defined": () =>
- g_GameAttributes.gameSpeed !== undefined &&
- g_GameSpeeds.Speed.indexOf(g_GameAttributes.gameSpeed) != -1,
- "get": () => g_GameAttributes.gameSpeed,
- "select": (itemIdx) => {
- g_GameAttributes.gameSpeed = g_GameSpeeds.Speed[itemIdx];
- },
- "initOrder": 1000
- },
- "triggerDifficulty": {
- "title": () => translate("Difficulty"),
- "tooltip": (hoverIdx) => g_TriggerDifficultyList && g_TriggerDifficultyList.Description[hoverIdx] ||
- translate("Select the difficulty of this scenario."),
- "labels": () => g_TriggerDifficultyList ? g_TriggerDifficultyList.Title : [],
- "ids": () => g_TriggerDifficultyList ? g_TriggerDifficultyList.Id : [],
- "default": () => g_TriggerDifficultyList ? g_TriggerDifficultyList.Default : 0,
- "defined": () => g_GameAttributes.settings.TriggerDifficulty !== undefined,
- "get": () => g_GameAttributes.settings.TriggerDifficulty,
- "select": (itemIdx) => {
- g_GameAttributes.settings.TriggerDifficulty = g_TriggerDifficultyList && g_TriggerDifficultyList.Id[itemIdx];
- },
- "hidden": () => !g_TriggerDifficultyList,
- "initOrder": 1000
- },
-};
-
-/**
- * These dropdowns provide a setting that is repeated once for each player
- * (where playerIdx is starting from 0 for player 1).
- */
-var g_PlayerDropdowns = {
- "playerAssignment": {
- "tooltip": (playerIdx) => translate("Select player."),
- "labels": (playerIdx) => g_PlayerAssignmentList.Name || [],
- "colors": (playerIdx) => g_PlayerAssignmentList.Color || [],
- "ids": (playerIdx) => g_PlayerAssignmentList.Choice || [],
- "default": (playerIdx) => "ai:petra",
- "defined": (playerIdx) => playerIdx < g_GameAttributes.settings.PlayerData.length,
- "get": (playerIdx) => {
- for (let guid in g_PlayerAssignments)
- if (g_PlayerAssignments[guid].player == playerIdx + 1)
- return "guid:" + guid;
-
- for (let ai of g_Settings.AIDescriptions)
- if (g_GameAttributes.settings.PlayerData[playerIdx].AI == ai.id)
- return "ai:" + ai.id;
-
- return "unassigned";
- },
- "select": (selectedIdx, playerIdx) => {
-
- let choice = g_PlayerAssignmentList.Choice[selectedIdx];
- if (choice == "unassigned" || choice.startsWith("ai:"))
- {
- if (g_IsNetworked)
- Engine.AssignNetworkPlayer(playerIdx+1, "");
- else if (g_PlayerAssignments.local.player == playerIdx+1)
- g_PlayerAssignments.local.player = -1;
-
- g_GameAttributes.settings.PlayerData[playerIdx].AI = choice.startsWith("ai:") ? choice.substr(3) : "";
- }
- else
- swapPlayers(choice.substr("guid:".length), playerIdx);
- },
- "autocomplete": 100,
- },
- "playerTeam": {
- "tooltip": (playerIdx) => translate("Select player's team."),
- "labels": (playerIdx) => g_PlayerTeamList.label,
- "ids": (playerIdx) => g_PlayerTeamList.id,
- "default": (playerIdx) => 0,
- "defined": (playerIdx) => g_GameAttributes.settings.PlayerData[playerIdx].Team !== undefined,
- "get": (playerIdx) => g_GameAttributes.settings.PlayerData[playerIdx].Team,
- "select": (selectedIdx, playerIdx) => {
- g_GameAttributes.settings.PlayerData[playerIdx].Team = selectedIdx - 1;
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- },
- "playerCiv": {
- "tooltip": (hoverIdx, playerIdx) => g_PlayerCivList.tooltip[hoverIdx] || translate("Choose the civilization for this player."),
- "labels": (playerIdx) => g_PlayerCivList.name,
- "colors": (playerIdx) => g_PlayerCivList.color,
- "ids": (playerIdx) => g_PlayerCivList.code,
- "default": (playerIdx) => 0,
- "defined": (playerIdx) => g_GameAttributes.settings.PlayerData[playerIdx].Civ !== undefined,
- "get": (playerIdx) => g_GameAttributes.settings.PlayerData[playerIdx].Civ,
- "select": (selectedIdx, playerIdx) => {
- g_GameAttributes.settings.PlayerData[playerIdx].Civ = g_PlayerCivList.code[selectedIdx];
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "autocomplete": 90,
- },
- "playerColorPicker": {
- "tooltip": (playerIdx) => translate("Pick a color."),
- "labels": (playerIdx) => g_PlayerColorPickerList.map(color => "â– "),
- "colors": (playerIdx) => g_PlayerColorPickerList.map(color => rgbToGuiColor(color)),
- "ids": (playerIdx) => g_PlayerColorPickerList.map((color, index) => index),
- "default": (playerIdx) => playerIdx,
- "defined": (playerIdx) => g_GameAttributes.settings.PlayerData[playerIdx].Color !== undefined,
- "get": (playerIdx) => g_PlayerColorPickerList.indexOf(g_GameAttributes.settings.PlayerData[playerIdx].Color),
- "select": (selectedIdx, playerIdx) => {
- let playerData = g_GameAttributes.settings.PlayerData;
-
- // If someone else has that color, give that player the old color
- let sameColorPData = playerData.find(pData => sameColor(g_PlayerColorPickerList[selectedIdx], pData.Color));
- if (sameColorPData)
- sameColorPData.Color = playerData[playerIdx].Color;
-
- playerData[playerIdx].Color = g_PlayerColorPickerList[selectedIdx];
- ensureUniquePlayerColors(playerData);
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- },
-};
-
-/**
- * Contains the logic of all boolean gamesettings.
- */
-var g_Checkboxes = Object.assign(
- {},
- g_VictoryConditions.reduce((obj, victoryCondition) => {
- obj[victoryCondition.Name] = {
- "title": () => victoryCondition.Title,
- "tooltip": () => victoryCondition.Description,
- // Defaults are set in supplementDefault directly from g_VictoryConditions since we use an array
- "defined": () => true,
- "get": () => g_GameAttributes.settings.VictoryConditions.indexOf(victoryCondition.Name) != -1,
- "set": checked => {
- if (checked)
- {
- g_GameAttributes.settings.VictoryConditions.push(victoryCondition.Name);
- if (victoryCondition.ChangeOnChecked)
- for (let setting in victoryCondition.ChangeOnChecked)
- g_Checkboxes[setting].set(victoryCondition.ChangeOnChecked[setting]);
- }
- else
- g_GameAttributes.settings.VictoryConditions = g_GameAttributes.settings.VictoryConditions.filter(victoryConditionName => victoryConditionName != victoryCondition.Name);
- },
- "enabled": () =>
- g_GameAttributes.mapType != "scenario" &&
- (!victoryCondition.DisabledWhenChecked ||
- victoryCondition.DisabledWhenChecked.every(victoryConditionName => g_GameAttributes.settings.VictoryConditions.indexOf(victoryConditionName) == -1))
- };
- return obj;
- }, {}),
- {
- "regicideGarrison": {
- "title": () => translate("Hero Garrison"),
- "tooltip": () => translate("Toggle whether heroes can be garrisoned."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.RegicideGarrison !== undefined,
- "get": () => g_GameAttributes.settings.RegicideGarrison,
- "set": checked => {
- g_GameAttributes.settings.RegicideGarrison = checked;
- },
- "hidden": () => g_GameAttributes.settings.VictoryConditions.indexOf("regicide") == -1,
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "nomad": {
- "title": () => translate("Nomad"),
- "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."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.Nomad !== undefined,
- "get": () => g_GameAttributes.settings.Nomad,
- "set": checked => {
- g_GameAttributes.settings.Nomad = checked;
- },
- "hidden": () => g_GameAttributes.mapType != "random",
- "initOrder": 1000
- },
- "revealMap": {
- "title": () =>
- // Translation: Make sure to differentiate between the revealed map and explored map settings!
- translate("Revealed Map"),
- "tooltip":
- // Translation: Make sure to differentiate between the revealed map and explored map settings!
- () => translate("Toggle revealed map (see everything)."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.RevealMap !== undefined,
- "get": () => g_GameAttributes.settings.RevealMap,
- "set": checked => {
- g_GameAttributes.settings.RevealMap = checked;
-
- if (checked)
- g_Checkboxes.exploreMap.set(true);
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "exploreMap": {
- "title":
- // Translation: Make sure to differentiate between the revealed map and explored map settings!
- () => translate("Explored Map"),
- "tooltip":
- // Translation: Make sure to differentiate between the revealed map and explored map settings!
- () => translate("Toggle explored map (see initial map)."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.ExploreMap !== undefined,
- "get": () => g_GameAttributes.settings.ExploreMap,
- "set": checked => {
- g_GameAttributes.settings.ExploreMap = checked;
- },
- "enabled": () => g_GameAttributes.mapType != "scenario" && !g_GameAttributes.settings.RevealMap,
- "initOrder": 1000
- },
- "disableTreasures": {
- "title": () => translate("Disable Treasures"),
- "tooltip": () => translate("Do not add treasures to the map."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.DisableTreasures !== undefined,
- "get": () => g_GameAttributes.settings.DisableTreasures,
- "set": checked => {
- g_GameAttributes.settings.DisableTreasures = checked;
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "disableSpies": {
- "title": () => translate("Disable Spies"),
- "tooltip": () => translate("Disable spies during the game."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.DisableSpies !== undefined,
- "get": () => g_GameAttributes.settings.DisableSpies,
- "set": checked => {
- g_GameAttributes.settings.DisableSpies = checked;
- },
- "enabled": () => g_GameAttributes.mapType != "scenario",
- "initOrder": 1000
- },
- "lockTeams": {
- "title": () => translate("Teams Locked"),
- "tooltip": () => translate("Toggle locked teams."),
- "default": () => Engine.HasXmppClient(),
- "defined": () => g_GameAttributes.settings.LockTeams !== undefined,
- "get": () => g_GameAttributes.settings.LockTeams,
- "set": checked => {
- g_GameAttributes.settings.LockTeams = checked;
- g_GameAttributes.settings.LastManStanding = false;
- },
- "enabled": () =>
- g_GameAttributes.mapType != "scenario" &&
- !g_GameAttributes.settings.RatingEnabled,
- "initOrder": 1000
- },
- "lastManStanding": {
- "title": () => translate("Last Man Standing"),
- "tooltip": () => translate("Toggle whether the last remaining player or the last remaining set of allies wins."),
- "default": () => false,
- "defined": () => g_GameAttributes.settings.LastManStanding !== undefined,
- "get": () => g_GameAttributes.settings.LastManStanding,
- "set": checked => {
- g_GameAttributes.settings.LastManStanding = checked;
- },
- "enabled": () =>
- g_GameAttributes.mapType != "scenario" &&
- !g_GameAttributes.settings.LockTeams,
- "initOrder": 1000
- },
- "enableCheats": {
- "title": () => translate("Cheats"),
- "tooltip": () => translate("Toggle the usability of cheats."),
- "default": () => !g_IsNetworked,
- "hidden": () => !g_IsNetworked,
- "defined": () => g_GameAttributes.settings.CheatsEnabled !== undefined,
- "get": () => g_GameAttributes.settings.CheatsEnabled,
- "set": checked => {
- g_GameAttributes.settings.CheatsEnabled = !g_IsNetworked ||
- checked && !g_GameAttributes.settings.RatingEnabled;
- },
- "enabled": () => !g_GameAttributes.settings.RatingEnabled,
- "initOrder": 1000
- },
- "enableRating": {
- "title": () => translate("Rated Game"),
- "tooltip": () => translate("Toggle if this game will be rated for the leaderboard."),
- "default": () => Engine.HasXmppClient(),
- "hidden": () => !Engine.HasXmppClient(),
- "defined": () => g_GameAttributes.settings.RatingEnabled !== undefined,
- "get": () => !!g_GameAttributes.settings.RatingEnabled,
- "set": checked => {
- g_GameAttributes.settings.RatingEnabled = Engine.HasXmppClient() ? checked : undefined;
- Engine.SetRankedGame(!!g_GameAttributes.settings.RatingEnabled);
- if (checked)
- {
- g_Checkboxes.lockTeams.set(true);
- g_Checkboxes.enableCheats.set(false);
- }
- },
- "initOrder": 1000
- },
- }
-);
-
-/**
- * For setting up arbitrary GUI objects.
- */
-var g_MiscControls = {
- "chatPanel": {
- "hidden": () => {
- if (!g_IsNetworked)
- return true;
-
- let size = Engine.GetGUIObjectByName("chatPanel").getComputedSize();
- return size.right - size.left < g_MinChatWidth;
- },
- },
- "chatInput": {
- "tooltip": () => colorizeAutocompleteHotkey(translate("Press %(hotkey)s to autocomplete player names or settings.")),
- "onPress": () => submitChatInput,
- "onTab": () => {
- // Figures out all strings that can be autocompleted and
- // sorts them by priority (so that playernames are always autocompleted first).
- let autocomplete = { "0": [] };
-
- for (let control of [g_Dropdowns, g_Checkboxes])
- for (let name in control)
- autocomplete[0] = autocomplete[0].concat(control[name].title());
-
- for (let dropdown of [g_Dropdowns, g_PlayerDropdowns])
- for (let name in dropdown)
- {
- let priority = dropdown[name].autocomplete;
- if (priority === undefined)
- continue;
-
- autocomplete[priority] = (autocomplete[priority] || []).concat(dropdown[name].labels());
- }
-
- autocomplete = Object.keys(autocomplete).sort().reverse().reduce((all, priority) => all.concat(autocomplete[priority]), []);
-
- return () => {
- autoCompleteText(Engine.GetGUIObjectByName("chatInput"), autocomplete);
- };
- },
- },
- "mapInfoName": {
- "caption": () => translateMapTitle(getMapDisplayName(g_GameAttributes.map))
- },
- "mapInfoDescription": {
- "caption": getGameDescription
- },
- "mapPreview": {
- "sprite": () => {
- let biomePreview = g_GameAttributes.settings.Biome && getBiomePreview(g_GameAttributes.map, g_GameAttributes.settings.Biome);
- if (biomePreview)
- return getMapPreviewImage(biomePreview);
-
- let mapData = loadMapData(g_GameAttributes.map);
- return getMapPreviewImage(mapData && mapData.settings && mapData.settings.Preview || "nopreview.png");
- }
- },
- "cheatWarningText": {
- "hidden": () => !g_IsNetworked || !g_GameAttributes.settings.CheatsEnabled,
- },
- "cancelGame": {
- "caption": () => translate("Back"),
- "tooltip": () =>
- Engine.HasXmppClient() ?
- translate("Return to the lobby.") :
- translate("Return to the main menu."),
- "onPress": () => cancelSetup,
- "size": () => {
- // Right align the button
- let offset = 10;
-
- let startGame = Engine.GetGUIObjectByName("startGame");
- let right = startGame.hidden ? startGame.size.right : startGame.size.left - offset;
-
- let cancelGame = Engine.GetGUIObjectByName("cancelGame");
- let cancelGameSize = cancelGame.size;
- let buttonWidth = cancelGameSize.right - cancelGameSize.left;
- cancelGameSize.right = right;
- right -= buttonWidth;
-
- for (let element of ["cheatWarningText", "onscreenToolTip"])
- {
- let elementSize = Engine.GetGUIObjectByName(element).size;
- elementSize.right = right - (cancelGameSize.left - elementSize.right);
- Engine.GetGUIObjectByName(element).size = elementSize;
- }
-
- cancelGameSize.left = right;
- return cancelGameSize;
- }
- },
- "startGame": {
- "caption": () =>
- g_IsController ? translate("Start Game!") : g_ReadyData[g_IsReady].caption,
- "onPress": () => function() {
- if (g_IsController)
- launchGame();
- else
- setReady((g_IsReady + 1) % 3, true);
- },
- "onPressRight": () => function() {
- if (!g_IsController && g_IsReady)
- setReady(0, true);
- },
- "tooltip": (hoverIdx) =>
- !g_IsController ?
- g_ReadyData[g_IsReady].tooltip :
- !g_IsNetworked || Object.keys(g_PlayerAssignments).every(guid =>
- g_PlayerAssignments[guid].status || g_PlayerAssignments[guid].player == -1) ?
- translate("Start a new game with the current settings.") :
- translate("Start a new game with the current settings (disabled until all players are ready)."),
- "enabled": () => !g_GameStarted && (
- !g_IsController ||
- Object.keys(g_PlayerAssignments).every(guid => g_PlayerAssignments[guid].status ||
- g_PlayerAssignments[guid].player == -1 ||
- guid == Engine.GetPlayerGUID() && g_IsController)),
- "hidden": () =>
- !g_PlayerAssignments[Engine.GetPlayerGUID()] ||
- g_PlayerAssignments[Engine.GetPlayerGUID()].player == -1 && !g_IsController,
- },
- "civInfoButton": {
- "tooltip": () => sprintf(
- translate("%(hotkey_civinfo)s / %(hotkey_structree)s: View History / Structure Tree\nLast opened will be reopened on click."), {
- "hotkey_civinfo": colorizeHotkey("%(hotkey)s", "civinfo"),
- "hotkey_structree": colorizeHotkey("%(hotkey)s", "structree")
- }),
- "onPress": () => function() {
- Engine.PushGuiPage(
- g_CivInfo.page,
- { "civ": g_CivInfo.civ },
- storeCivInfoPage);
- }
- },
- "civResetButton": {
- "tooltip": () => translate("Reset any civilizations that have been selected to the default (random)."),
- "onPress": () => function() {
- for (let i in g_GameAttributes.settings.PlayerData)
- g_GameAttributes.settings.PlayerData[i].Civ = "random";
-
- updateGameAttributes();
- },
- "hidden": () => g_GameAttributes.mapType == "scenario" || !g_IsController,
- },
- "teamResetButton": {
- "tooltip": () => translate("Reset all teams to the default."),
- "onPress": () => function() {
- for (let i in g_GameAttributes.settings.PlayerData)
- g_GameAttributes.settings.PlayerData[i].Team = -1;
-
- updateGameAttributes();
- },
- "hidden": () => g_GameAttributes.mapType == "scenario" || !g_IsController,
- },
- "lobbyButton": {
- "onPress": () => function() {
- if (Engine.HasXmppClient())
- Engine.PushGuiPage("page_lobby.xml", { "dialog": true });
- },
- "hidden": () => !Engine.HasXmppClient()
- },
- "spTips": {
- "hidden": () => {
- let settingsPanel = Engine.GetGUIObjectByName("settingsPanel");
- let spTips = Engine.GetGUIObjectByName("spTips");
- return g_IsNetworked ||
- Engine.ConfigDB_GetValue("user", "gui.gamesetup.enabletips") !== "true" ||
- spTips.size.right > settingsPanel.getComputedSize().left;
- }
- }
-};
-
-/**
- * Contains gui elements that are repeated for every player.
- */
-var g_PlayerMiscElements = {
- "playerBox": {
- "size": (playerIdx) => ["0", 32 * playerIdx, "100%", 32 * (playerIdx + 1)].join(" "),
- },
- "playerName": {
- "caption": (playerIdx) => {
- let pData = g_GameAttributes.settings.PlayerData[playerIdx];
-
- let assignedGUID = Object.keys(g_PlayerAssignments).find(
- guid => g_PlayerAssignments[guid].player == playerIdx + 1);
-
- let name = translate(pData.Name || g_DefaultPlayerData[playerIdx].Name);
-
- if (g_IsNetworked)
- name = coloredText(name, g_ReadyData[assignedGUID ? g_PlayerAssignments[assignedGUID].status : 2].color);
-
- return name;
- },
- },
- "playerColor": {
- "sprite": (playerIdx) => "color:" + rgbToGuiColor(g_GameAttributes.settings.PlayerData[playerIdx].Color, 100),
- },
- "playerConfig": {
- "hidden": (playerIdx) => !g_GameAttributes.settings.PlayerData[playerIdx].AI,
- "onPress": (playerIdx) => function() {
- openAIConfig(playerIdx);
- },
- "tooltip": (playerIdx) => sprintf(translate("Configure AI: %(description)s."), {
- "description": translateAISettings(g_GameAttributes.settings.PlayerData[playerIdx])
- }),
- },
-};
-
-var g_Hotkeys = {
- "civinfo": () => {
- Engine.PushGuiPage("page_civinfo.xml", { "civ": g_CivInfo.code }, storeCivInfoPage);
- },
- "structree": () => {
- Engine.PushGuiPage("page_structree.xml", { "civ": g_CivInfo.civ }, storeCivInfoPage);
- },
- "cancel": () => {
- selectPanel(undefined);
- }
-};
-
-/**
- * Initializes some globals without touching the GUI.
- *
- * @param {Object} attribs - context data sent by the lobby / mainmenu
- */
-function init(attribs)
-{
- if (!g_Settings)
- {
- cancelSetup();
- return;
- }
-
- g_IsTutorial = !!attribs.tutorial;
- g_ServerName = attribs.serverName;
- g_ServerPort = attribs.serverPort;
- g_StunEndpoint = attribs.stunEndpoint;
-
- if (!g_IsNetworked)
- g_PlayerAssignments = {
- "local": {
- "name": singleplayerName(),
- "player": 1
- }
- };
-
- // Replace empty player name when entering a single-player match for the first time.
- if (!g_IsNetworked)
- Engine.ConfigDB_CreateAndWriteValueToFile("user", "playername.singleplayer", singleplayerName(), "config/user.cfg");
-
- initDefaults();
- supplementDefaults();
-
- setTimeout(displayGamestateNotifications, 1000);
-}
-
-function initDefaults()
-{
- // Remove gaia from both arrays
- g_DefaultPlayerData = clone(g_Settings.PlayerDefaults.slice(1));
-
- let aiDifficulty = +Engine.ConfigDB_GetValue("user", "gui.gamesetup.aidifficulty");
- let aiBehavior = Engine.ConfigDB_GetValue("user", "gui.gamesetup.aibehavior");
-
- // Don't change the underlying defaults file, as Atlas uses that file too
- for (let i in g_DefaultPlayerData)
- {
- g_DefaultPlayerData[i].Civ = "random";
- g_DefaultPlayerData[i].Team = -1;
- g_DefaultPlayerData[i].AIDiff = aiDifficulty;
- g_DefaultPlayerData[i].AIBehavior = aiBehavior;
- }
-
- deepfreeze(g_DefaultPlayerData);
-}
-
-/**
- * Sets default values for all g_GameAttribute settings which don't have a value set.
- */
-function supplementDefaults()
-{
- g_GameAttributes.settings.VictoryConditions = g_GameAttributes.settings.VictoryConditions ||
- g_VictoryConditions.filter(victoryCondition => !!victoryCondition.Default).map(victoryCondition => victoryCondition.Name);
-
- for (let dropdown in g_Dropdowns)
- if (!g_Dropdowns[dropdown].defined())
- g_Dropdowns[dropdown].select(g_Dropdowns[dropdown].default());
-
- for (let checkbox in g_Checkboxes)
- if (!g_Checkboxes[checkbox].defined())
- g_Checkboxes[checkbox].set(g_Checkboxes[checkbox].default());
-
- for (let dropdown in g_PlayerDropdowns)
- for (let i = 0; i < g_GameAttributes.settings.PlayerData.length; ++i)
- if (!isControlArrayElementHidden(i) && !g_PlayerDropdowns[dropdown].defined(i))
- g_PlayerDropdowns[dropdown].select(g_PlayerDropdowns[dropdown].default(i), i);
-}
-
-/**
- * Called after the first tick.
- */
-function initGUIObjects()
-{
- initSettingObjects();
- initSettingsTabButtons();
- initHotkeys();
- initSPTips();
-
- loadPersistMatchSettings();
- updateGameAttributes();
- sendRegisterGameStanzaImmediate();
-
- if (g_IsTutorial)
- {
- launchTutorial();
- return;
- }
-
- // Don't lift the curtain until the controls are updated the first time
- if (!g_IsNetworked)
- hideLoadingWindow();
-}
-
-/**
- * @param {number} dt - Time in milliseconds since last call.
- */
-function slideSettingsPanel(dt)
-{
- let slideSpeed = Engine.ConfigDB_GetValue("user", "gui.gamesetup.settingsslide") == "true" ? g_SlideSpeed : Infinity;
-
- let settingsPanel = Engine.GetGUIObjectByName("settingsPanel");
- let rightBorder = Engine.GetGUIObjectByName("settingTabButtons").size.left;
- let offset = 0;
- if (g_TabCategorySelected === undefined)
- {
- let maxOffset = rightBorder - settingsPanel.size.left;
- if (maxOffset > 0)
- offset = Math.min(slideSpeed * dt, maxOffset);
- }
- else if (rightBorder > settingsPanel.size.right)
- offset = Math.min(slideSpeed * dt, rightBorder - settingsPanel.size.right);
- else
- {
- let maxOffset = settingsPanel.size.right - rightBorder;
- if (maxOffset > 0)
- offset = -Math.min(slideSpeed * dt, maxOffset);
- }
-
- updateSettingsPanelPosition(offset);
-}
-
-/**
- * Directly change the position of the settingsPanel.
- * @param {number} offset - Number of pixels the panel needs to move.
- */
-function updateSettingsPanelPosition(offset)
-{
- if (!offset)
- return;
-
- let settingsPanel = Engine.GetGUIObjectByName("settingsPanel");
- let settingsPanelSize = settingsPanel.size;
- settingsPanelSize.left += offset;
- settingsPanelSize.right += offset;
- settingsPanel.size = settingsPanelSize;
-
- let settingsBackground = Engine.GetGUIObjectByName("settingsBackground");
- let backgroundSize = settingsBackground.size;
- backgroundSize.left = settingsPanelSize.left;
- settingsBackground.size = backgroundSize;
-
- let chatPanel = Engine.GetGUIObjectByName("chatPanel");
- let chatSize = chatPanel.size;
-
- chatSize.right = settingsPanelSize.left - g_ChatSettingsMargin;
- chatPanel.size = chatSize;
- chatPanel.hidden = g_MiscControls.chatPanel.hidden();
-
- let spTips = Engine.GetGUIObjectByName("spTips");
- spTips.hidden = g_MiscControls.spTips.hidden();
-}
-
-function hideLoadingWindow()
-{
- let loadingWindow = Engine.GetGUIObjectByName("loadingWindow");
- if (loadingWindow.hidden)
- return;
-
- loadingWindow.hidden = true;
- Engine.GetGUIObjectByName("setupWindow").hidden = false;
-
- if (!Engine.GetGUIObjectByName("chatPanel").hidden)
- Engine.GetGUIObjectByName("chatInput").focus();
-}
-
-/**
- * Settings under the settings tabs use a generic name.
- * Player settings use custom names.
- */
-function getGUIObjectNameFromSetting(setting)
-{
- let idxOffset = 0;
- for (let category of g_SettingsTabsGUI)
- {
- let idx = category.settings.indexOf(setting);
- if (idx != -1)
- return [
- "setting",
- g_Dropdowns[setting] ? "Dropdown" : "Checkbox",
- "[" + (idx + idxOffset) + "]"
- ];
- idxOffset += category.settings.length;
- }
-
- // Assume there is a GUI object with exactly that setting name
- return [setting, "", ""];
-}
-
-/**
- * Initialize all settings dropdowns and checkboxes.
- */
-function initSettingObjects()
-{
- // Copy all initOrder values into one object
- let initOrder = {};
- for (let dropdown in g_Dropdowns)
- initOrder[dropdown] = g_Dropdowns[dropdown].initOrder;
- for (let checkbox in g_Checkboxes)
- initOrder[checkbox] = g_Checkboxes[checkbox].initOrder;
-
- // Sort the object on initOrder so we can init the settings in an arbitrary order
- for (let setting of Object.keys(initOrder).sort((a, b) => initOrder[a] - initOrder[b]))
- if (g_Dropdowns[setting])
- initDropdown(setting);
- else if (g_Checkboxes[setting])
- initCheckbox(setting);
- else
- warn('The setting "' + setting + '" is not defined.');
-
- for (let dropdown in g_PlayerDropdowns)
- initPlayerDropdowns(dropdown);
-}
-
-function initDropdown(name, playerIdx)
-{
- let [guiName, guiType, guiIdx] = getGUIObjectNameFromSetting(name);
- let idxName = playerIdx === undefined ? "" : "[" + playerIdx + "]";
- let data = (playerIdx === undefined ? g_Dropdowns : g_PlayerDropdowns)[name];
-
- let dropdown = Engine.GetGUIObjectByName(guiName + guiType + guiIdx + idxName);
-
- dropdown.list = data.labels(playerIdx).map((label, id) =>
- data.colors && data.colors(playerIdx) ?
- coloredText(label, data.colors(playerIdx)[id]) :
- label);
-
- dropdown.list_data = data.ids(playerIdx);
-
- dropdown.onSelectionChange = function() {
-
- if (!g_IsController ||
- g_IsInGuiUpdate ||
- !this.list_data[this.selected] ||
- data.hidden && data.hidden(playerIdx) ||
- data.enabled && !data.enabled(playerIdx))
- return;
-
- data.select(this.selected, playerIdx);
-
- supplementDefaults();
- updateGameAttributes();
- };
-
- if (data.tooltip)
- dropdown.onHoverChange = function() {
- this.tooltip = data.tooltip(this.hovered, playerIdx);
- };
-}
-
-function initPlayerDropdowns(name)
-{
- for (let i = 0; i < g_MaxPlayers; ++i)
- initDropdown(name, i);
-}
-
-function initCheckbox(name)
-{
- let [guiName, guiType, guiIdx] = getGUIObjectNameFromSetting(name);
- Engine.GetGUIObjectByName(guiName + guiType + guiIdx).onPress = function() {
-
- let obj = g_Checkboxes[name];
-
- if (!g_IsController ||
- g_IsInGuiUpdate ||
- obj.enabled && !obj.enabled() ||
- obj.hidden && obj.hidden())
- return;
-
- obj.set(this.checked);
-
- supplementDefaults();
- updateGameAttributes();
- };
-}
-
-function initHotkeys()
-{
- for (let hotkeyName in g_Hotkeys)
- Engine.SetGlobalHotkey(hotkeyName, g_Hotkeys[hotkeyName]);
-}
-
-function initSettingsTabButtons()
-{
- for (let tab in g_SettingsTabsGUI)
- g_SettingsTabsGUI[tab].tooltip =
- sprintf(translate("Toggle the %(name)s settings tab."), { "name": g_SettingsTabsGUI[tab].label }) +
- colorizeHotkey("\n" + translate("Use %(hotkey)s to move a settings tab down."), "tab.next") +
- colorizeHotkey("\n" + translate("Use %(hotkey)s to move a settings tab up."), "tab.prev");
-
- let settingTabButtons = Engine.GetGUIObjectByName("settingTabButtons");
- let settingTabButtonsSize = settingTabButtons.size;
- settingTabButtonsSize.bottom = settingTabButtonsSize.top + g_SettingsTabsGUI.length * (g_TabButtonHeight + g_TabButtonDist);
- settingTabButtonsSize.right = g_MiscControls.lobbyButton.hidden() ?
- settingTabButtonsSize.right :
- Engine.GetGUIObjectByName("lobbyButton").size.left - g_LobbyButtonSpacing;
- settingTabButtons.size = settingTabButtonsSize;
-
- let settingTabButtonsBackground = Engine.GetGUIObjectByName("settingTabButtonsBackground");
- settingTabButtonsBackground.size = settingTabButtonsSize;
-
- let gameDescription = Engine.GetGUIObjectByName("mapInfoDescriptionFrame");
- let gameDescriptionSize = gameDescription.size;
- gameDescriptionSize.top = settingTabButtonsSize.bottom + 3;
- gameDescription.size = gameDescriptionSize;
-
- if (!g_IsController)
- {
- g_TabCategorySelected = undefined;
- updateSettingsPanelPosition(Engine.GetGUIObjectByName("settingTabButtons").size.left - Engine.GetGUIObjectByName("settingsPanel").size.left);
- }
-
- placeTabButtons(
- g_SettingsTabsGUI,
- g_TabButtonHeight,
- g_TabButtonDist,
- category => {
- selectPanel(category == g_TabCategorySelected ? undefined : category);
- },
- () => {
- updateGUIObjects();
- Engine.GetGUIObjectByName("settingsPanel").hidden = false;
- });
-}
-
-function initSPTips()
-{
- if (g_IsNetworked || Engine.ConfigDB_GetValue("user", "gui.gamesetup.enabletips") !== "true")
- return;
-
- Engine.GetGUIObjectByName("spTips").hidden = false;
- Engine.GetGUIObjectByName("displaySPTips").checked = true;
- Engine.GetGUIObjectByName("aiTips").caption = Engine.TranslateLines(Engine.ReadFile("gui/gamesetup/ai.txt"));
-}
-
-/**
- * Distribute the currently visible settings over the settings panel.
- * First calculate the number of columns required, then place the objects.
- */
-function distributeSettings()
-{
- let setupWindowSize = Engine.GetGUIObjectByName("setupWindow").getComputedSize();
- let columnWidth = Math.min(
- g_MaxColumnWidth,
- (setupWindowSize.right - setupWindowSize.left + Engine.GetGUIObjectByName("settingTabButtons").size.left) / 2);
-
- let settingsPanel = Engine.GetGUIObjectByName("settingsPanel");
- let actualSettingsPanelSize = settingsPanel.getComputedSize();
-
- let maxPerColumn = Math.floor((actualSettingsPanelSize.bottom - actualSettingsPanelSize.top) / g_SettingHeight);
- let childCount = settingsPanel.children.filter(child => !child.hidden).length;
- let perColumn = childCount / Math.ceil(childCount / maxPerColumn);
-
- let yPos = g_SettingDist;
- let column = 0;
- let thisColumn = 0;
- let settingsPanelSize = settingsPanel.size;
- for (let child of settingsPanel.children)
- {
- if (child.hidden)
- continue;
-
- if (thisColumn >= perColumn)
- {
- yPos = g_SettingDist;
- ++column;
- thisColumn = 0;
- }
-
- child.size = new GUISize(
- column * columnWidth,
- yPos,
- column * columnWidth + columnWidth - 10,
- yPos + g_SettingHeight - g_SettingDist);
-
- yPos += g_SettingHeight;
- ++thisColumn;
- }
-
- settingsPanelSize.right = settingsPanelSize.left + (column + 1) * columnWidth;
- settingsPanel.size = settingsPanelSize;
-}
-
-/**
- * Called when the client disconnects.
- * The other cases from NetClient should never occur in the gamesetup.
- */
-function handleNetStatusMessage(message)
-{
- if (message.status != "disconnected")
- {
- error("Unrecognised netstatus type " + message.status);
- return;
- }
-
- cancelSetup();
- reportDisconnect(message.reason, true);
-}
-
-/**
- * Called whenever a client clicks on ready (or not ready).
- */
-function handleReadyMessage(message)
-{
- --g_ReadyChanged;
-
- if (g_ReadyChanged < 1 && g_PlayerAssignments[message.guid].player != -1)
- addChatMessage({
- "type": "ready",
- "status": message.status,
- "guid": message.guid
- });
-
- g_PlayerAssignments[message.guid].status = message.status;
- updateGUIObjects();
-}
-
-/**
- * Called after every player is ready and the host decided to finally start the game.
- */
-function handleGamestartMessage(message)
-{
- // Immediately inform the lobby server instead of waiting for the load to finish
- if (g_IsController && Engine.HasXmppClient())
- {
- sendRegisterGameStanzaImmediate();
- let clients = formatClientsForStanza();
- Engine.SendChangeStateGame(clients.connectedPlayers, clients.list);
- }
-
- Engine.SwitchGuiPage("page_loading.xml", {
- "attribs": g_GameAttributes,
- "playerAssignments": g_PlayerAssignments
- });
-}
-
-/**
- * Called whenever the host changed any setting.
- */
-function handleGamesetupMessage(message)
-{
- if (!message.data)
- return;
-
- g_GameAttributes = message.data;
-
- if (!!g_GameAttributes.settings.RatingEnabled)
- {
- g_GameAttributes.settings.CheatsEnabled = false;
- g_GameAttributes.settings.LockTeams = true;
- g_GameAttributes.settings.LastManStanding = false;
- }
-
- Engine.SetRankedGame(!!g_GameAttributes.settings.RatingEnabled);
-
- resetReadyData();
-
- updateGUIObjects();
-
- hideLoadingWindow();
-}
-
-/**
- * Called whenever a client joins/leaves or any gamesetting is changed.
- */
-function handlePlayerAssignmentMessage(message)
-{
- let playerChange = false;
-
- for (let guid in message.newAssignments)
- if (!g_PlayerAssignments[guid])
- {
- onClientJoin(guid, message.newAssignments);
- playerChange = true;
- }
-
- for (let guid in g_PlayerAssignments)
- if (!message.newAssignments[guid])
- {
- onClientLeave(guid);
- playerChange = true;
- }
-
- g_PlayerAssignments = message.newAssignments;
-
- sanitizePlayerData(g_GameAttributes.settings.PlayerData);
- updateGUIObjects();
-
- if (playerChange)
- sendRegisterGameStanzaImmediate();
- else
- sendRegisterGameStanza();
-}
-
-function onClientJoin(newGUID, newAssignments)
-{
- let playername = newAssignments[newGUID].name;
-
- addChatMessage({
- "type": "connect",
- "guid": newGUID,
- "username": playername
- });
-
- if (newGUID != Engine.GetPlayerGUID() && Object.keys(g_PlayerAssignments).length)
- soundNotification("gamesetup.join");
-
- let isRejoiningPlayer = newAssignments[newGUID].player != -1;
-
- // Assign the client (or only buddies if prefered) to an unused playerslot and rejoining players to their old slot
- if (!isRejoiningPlayer && playername != newAssignments[Engine.GetPlayerGUID()].name)
- {
- let assignOption = Engine.ConfigDB_GetValue("user", "gui.gamesetup.assignplayers");
- if (assignOption == "disabled" ||
- assignOption == "buddies" && g_Buddies.indexOf(splitRatingFromNick(playername).nick) == -1)
- return;
- }
-
- let freeSlot = g_GameAttributes.settings.PlayerData.findIndex((v, i) =>
- Object.keys(g_PlayerAssignments).every(guid => g_PlayerAssignments[guid].player != i + 1)
- );
-
- // Client is not and cannot become assigned as player
- if (!isRejoiningPlayer && freeSlot == -1)
- return;
-
- // Assign the joining client to the free slot
- if (g_IsController && !isRejoiningPlayer)
- Engine.AssignNetworkPlayer(freeSlot + 1, newGUID);
-
- resetReadyData();
-}
-
-function onClientLeave(guid)
-{
- addChatMessage({
- "type": "disconnect",
- "guid": guid
- });
-
- if (g_PlayerAssignments[guid].player != -1)
- resetReadyData();
-}
-
-/**
- * Doesn't translate, so that lobby clients can do that locally
- * (even if they don't have that map).
- */
-function getMapDisplayName(map)
-{
- if (map == "random")
- return map;
-
- let mapData = loadMapData(map);
- if (!mapData || !mapData.settings || !mapData.settings.Name)
- return map;
-
- return mapData.settings.Name;
-}
-
-/**
- * Filter maps with filterFunc and by chosen map type.
- *
- * @param {function} filterFunc - Filter function that should be applied.
- * @return {Array} the maps that match the filterFunc and the chosen map type.
- */
-function getFilteredMaps(filterFunc)
-{
- if (!g_MapPath[g_GameAttributes.mapType])
- {
- error("Unexpected map type: " + g_GameAttributes.mapType);
- return [];
- }
-
- let maps = [];
- // TODO: Should verify these are valid maps before adding to list
- for (let mapFile of listFiles(g_GameAttributes.mapPath, g_GameAttributes.mapType == "random" ? ".json" : ".xml", false))
- {
- if (mapFile.startsWith("_"))
- continue;
-
- let file = g_GameAttributes.mapPath + mapFile;
- let mapData = loadMapData(file);
-
- if (!mapData || !mapData.settings || filterFunc && !filterFunc(mapData.settings.Keywords || []))
- continue;
-
- maps.push({
- "file": file,
- "name": translate(getMapDisplayName(file)),
- "color": g_ColorRegular,
- "description": translate(mapData.settings.Description)
- });
- }
- return maps;
-}
-
-/**
- * Initialize the dropdown containing all map filters for the selected maptype.
- */
-function reloadMapFilterList()
-{
- g_MapFilterList = prepareForDropdown(g_MapFilters.filter(
- mapFilter => getFilteredMaps(mapFilter.filter).length
- ));
-
- initDropdown("mapFilter");
- reloadMapList();
-}
-
-/**
- * Initialize the dropdown containing all maps for the selected maptype and mapfilter.
- */
-function reloadMapList()
-{
- let filterID = g_MapFilterList.id.findIndex(id => id == g_GameAttributes.mapFilter);
- let filterFunc = g_MapFilterList.filter[filterID];
- let mapList = getFilteredMaps(filterFunc).sort(sortNameIgnoreCase);
-
- if (g_GameAttributes.mapType == "random")
- mapList.unshift({
- "file": "random",
- "name": translateWithContext("map selection", "Random"),
- "color": g_ColorRandom,
- "description": translate("Pick any of the given maps at random.")
- });
-
- g_MapSelectionList = prepareForDropdown(mapList);
- initDropdown("mapSelection");
-}
-
-/**
- * Initialize the dropdowns specific to each map.
- */
-function reloadMapSpecific()
-{
- reloadBiomeList();
- reloadTriggerDifficulties();
-}
-
-function reloadBiomeList()
-{
- let biomeList;
-
- if (g_GameAttributes.mapType == "random" && g_GameAttributes.settings.SupportedBiomes)
- {
- if (typeof g_GameAttributes.settings.SupportedBiomes == "string")
- biomeList = g_Settings.Biomes.filter(biome => biome.Id.startsWith(g_GameAttributes.settings.SupportedBiomes));
- else
- biomeList = g_Settings.Biomes.filter(
- biome => g_GameAttributes.settings.SupportedBiomes.indexOf(biome.Id) != -1);
- }
-
- g_BiomeList = biomeList && prepareForDropdown(
- [{
- "Id": "random",
- "Title": translateWithContext("biome", "Random"),
- "Description": translate("Pick a biome at random."),
- "Color": g_ColorRandom
- }].concat(biomeList.map(biome => ({
- "Id": biome.Id,
- "Title": biome.Title,
- "Description": biome.Description,
- "Color": g_ColorRegular
- }))));
-
- initDropdown("biome");
- updateGUIDropdown("biome");
-}
-
-function reloadTriggerDifficulties()
-{
- g_TriggerDifficultyList = undefined;
-
- if (!g_GameAttributes.settings.SupportedTriggerDifficulties)
- return;
-
- let triggerDifficultyList;
- if (g_GameAttributes.settings.SupportedTriggerDifficulties.Values === true)
- triggerDifficultyList = g_Settings.TriggerDifficulties;
- else
- {
- triggerDifficultyList = g_Settings.TriggerDifficulties.filter(
- diff => g_GameAttributes.settings.SupportedTriggerDifficulties.Values.indexOf(diff.Name) != -1);
- if (!triggerDifficultyList.length)
- return;
- }
-
- g_TriggerDifficultyList = prepareForDropdown(
- triggerDifficultyList.map(diff => ({
- "Id": diff.Difficulty,
- "Title": diff.Title,
- "Description": diff.Tooltip,
- "Default": diff.Name == g_GameAttributes.settings.SupportedTriggerDifficulties.Default
- })));
-
- initDropdown("triggerDifficulty");
- updateGUIDropdown("triggerDifficulty");
-}
-
-function getGameSpeedChoices(allowFastForward)
-{
- return prepareForDropdown(g_Settings.GameSpeeds.filter(speed => !speed.FastForward || allowFastForward));
-}
-
-function reloadGameSpeedChoices()
-{
- g_GameSpeeds = getGameSpeedChoices(Object.keys(g_PlayerAssignments).every(guid => g_PlayerAssignments[guid].player == -1));
- initDropdown("gameSpeed");
- supplementDefaults();
-}
-
-function loadMapData(name)
-{
- if (!name || !g_MapPath[g_GameAttributes.mapType])
- return undefined;
-
- if (name == "random")
- return { "settings": { "Name": "", "Description": "" } };
-
- if (!g_MapData[name])
- g_MapData[name] = g_GameAttributes.mapType == "random" ?
- Engine.ReadJSONFile(name + ".json") :
- Engine.LoadMapSettings(name);
-
- return g_MapData[name];
-}
-
-/**
- * Sets the gameattributes the way they were the last time the user left the gamesetup.
- */
-function loadPersistMatchSettings()
-{
- if (!g_IsController || Engine.ConfigDB_GetValue("user", "persistmatchsettings") != "true" || g_IsTutorial)
- return;
-
- let settingsFile = g_IsNetworked ? g_MatchSettings_MP : g_MatchSettings_SP;
- if (!Engine.FileExists(settingsFile))
- return;
-
- let data = Engine.ReadJSONFile(settingsFile);
- if (!data || !data.attributes || !data.attributes.settings)
- return;
-
- if (data.engine_info.engine_version != Engine.GetEngineInfo().engine_version ||
- !hasSameMods(data.engine_info.mods, Engine.GetEngineInfo().mods))
- return;
-
- g_IsInGuiUpdate = true;
-
- let mapName = data.attributes.map || "";
- let mapSettings = data.attributes.settings;
-
- g_GameAttributes = data.attributes;
-
- if (!g_IsNetworked)
- mapSettings.CheatsEnabled = true;
-
- // Replace unselectable civs with random civ
- let playerData = mapSettings.PlayerData;
- if (playerData && g_GameAttributes.mapType != "scenario")
- for (let i in playerData)
- if (!g_CivData[playerData[i].Civ] || !g_CivData[playerData[i].Civ].SelectableInGameSetup)
- playerData[i].Civ = "random";
-
- // Apply map settings
- let newMapData = loadMapData(mapName);
- if (newMapData && newMapData.settings)
- {
- for (let prop in newMapData.settings)
- mapSettings[prop] = newMapData.settings[prop];
-
- if (playerData)
- mapSettings.PlayerData = playerData;
- }
-
- if (mapSettings.PlayerData)
- sanitizePlayerData(mapSettings.PlayerData);
-
- // Reload, as the maptype or mapfilter might have changed
- reloadMapFilterList();
- reloadMapSpecific();
-
- g_GameAttributes.settings.RatingEnabled = Engine.HasXmppClient();
- Engine.SetRankedGame(g_GameAttributes.settings.RatingEnabled);
-
- supplementDefaults();
-
- g_IsInGuiUpdate = false;
-}
-
-function savePersistMatchSettings()
-{
- if (g_IsTutorial)
- return;
-
- Engine.WriteJSONFile(
- g_IsNetworked ? g_MatchSettings_MP : g_MatchSettings_SP,
- {
- "attributes":
- // Delete settings if disabled, so that players are not confronted with old settings after enabling the setting again
- Engine.ConfigDB_GetValue("user", "persistmatchsettings") == "true" ?
- g_GameAttributes :
- {},
- "engine_info": Engine.GetEngineInfo()
- });
-}
-
-function sanitizePlayerData(playerData)
-{
- // Remove gaia
- if (playerData.length && !playerData[0])
- playerData.shift();
-
- playerData.forEach((pData, index) => {
-
- // Use defaults if the map doesn't specify a value
- for (let prop in g_DefaultPlayerData[index])
- if (!(prop in pData))
- pData[prop] = clone(g_DefaultPlayerData[index][prop]);
-
- // Replace colors with the best matching color of PlayerDefaults
- if (g_GameAttributes.mapType != "scenario")
- {
- let colorDistances = g_PlayerColorPickerList.map(color => colorDistance(color, pData.Color));
- let smallestDistance = colorDistances.find(distance => colorDistances.every(distance2 => (distance2 >= distance)));
- pData.Color = g_PlayerColorPickerList.find(color => colorDistance(color, pData.Color) == smallestDistance);
- }
-
- // If there is a player in that slot, then there can't be an AI
- if (Object.keys(g_PlayerAssignments).some(guid => g_PlayerAssignments[guid].player == index + 1))
- pData.AI = "";
- });
-
- ensureUniquePlayerColors(playerData);
-}
-
-function cancelSetup()
-{
- if (g_IsController)
- savePersistMatchSettings();
-
- Engine.DisconnectNetworkGame();
-
- if (Engine.HasXmppClient())
- {
- Engine.LobbySetPlayerPresence("available");
-
- if (g_IsController)
- Engine.SendUnregisterGame();
-
- Engine.SwitchGuiPage("page_lobby.xml", { "dialog": false });
- }
- else
- Engine.SwitchGuiPage("page_pregame.xml");
-}
-
-/**
- * Can't init the GUI before the first tick.
- * Process netmessages afterwards.
- */
-function onTick()
-{
- if (!g_Settings)
- return;
-
- // First tick happens before first render, so don't load yet
- if (g_LoadingState == 0)
- ++g_LoadingState;
- else if (g_LoadingState == 1)
- {
- initGUIObjects();
- ++g_LoadingState;
- }
- else if (g_LoadingState == 2)
- handleNetMessages();
-
- updateTimers();
-
- let now = Date.now();
- let tickLength = now - g_LastTickTime;
- g_LastTickTime = now;
-
- slideSettingsPanel(tickLength);
-}
-
-/**
- * Handles all pending messages sent by the net client.
- */
-function handleNetMessages()
-{
- if (!g_IsNetworked)
- return;
-
- while (true)
- {
- let message = Engine.PollNetworkClient();
- if (!message)
- break;
-
- log("Net message: " + uneval(message));
-
- if (g_NetMessageTypes[message.type])
- g_NetMessageTypes[message.type](message);
- else
- error("Unrecognised net message type " + message.type);
- }
-}
-
-/**
- * Called when the map or the number of players changes.
- */
-function unassignInvalidPlayers(maxPlayers)
-{
- if (g_IsNetworked)
- // Remove invalid playerIDs from the servers playerassignments copy
- for (let playerID = +maxPlayers + 1; playerID <= g_MaxPlayers; ++playerID)
- Engine.AssignNetworkPlayer(playerID, "");
-
- else if (g_PlayerAssignments.local.player > maxPlayers)
- g_PlayerAssignments.local.player = -1;
-}
-
-function ensureUniquePlayerColors(playerData)
-{
- for (let i = playerData.length - 1; i >= 0; --i)
- // If someone else has that color, assign an unused color
- if (playerData.some((pData, j) => i != j && sameColor(playerData[i].Color, pData.Color)))
- playerData[i].Color = g_PlayerColorPickerList.find(color => playerData.every(pData => !sameColor(color, pData.Color)));
-}
-
-function selectMap(name)
-{
- // Reset some map specific properties which are not necessarily redefined on each map
- for (let prop of ["TriggerScripts", "CircularMap", "Garrison", "DisabledTemplates", "Biome", "SupportedBiomes", "SupportedTriggerDifficulties", "TriggerDifficulty"])
- g_GameAttributes.settings[prop] = undefined;
-
- let mapData = loadMapData(name);
- let mapSettings = mapData && mapData.settings ? clone(mapData.settings) : {};
-
- if (g_GameAttributes.mapType != "random")
- delete g_GameAttributes.settings.Nomad;
-
- if (g_GameAttributes.mapType == "scenario")
- {
- delete g_GameAttributes.settings.RelicDuration;
- delete g_GameAttributes.settings.WonderDuration;
- delete g_GameAttributes.settings.LastManStanding;
- delete g_GameAttributes.settings.RegicideGarrison;
- }
-
- if (mapSettings.PlayerData)
- sanitizePlayerData(mapSettings.PlayerData);
-
- // Copy any new settings
- g_GameAttributes.map = name;
- g_GameAttributes.script = mapSettings.Script;
- if (g_GameAttributes.map !== "random")
- for (let prop in mapSettings)
- g_GameAttributes.settings[prop] = mapSettings[prop];
-
- reloadMapSpecific();
- unassignInvalidPlayers(g_GameAttributes.settings.PlayerData.length);
- supplementDefaults();
-}
-
-function isControlArrayElementHidden(playerIdx)
-{
- return playerIdx !== undefined && playerIdx >= g_GameAttributes.settings.PlayerData.length;
-}
-
-/**
- * @param playerIdx - Only specified for dropdown arrays.
- */
-function updateGUIDropdown(name, playerIdx = undefined)
-{
- let [guiName, guiType, guiIdx] = getGUIObjectNameFromSetting(name);
- let idxName = playerIdx === undefined ? "" : "[" + playerIdx + "]";
-
- let dropdown = Engine.GetGUIObjectByName(guiName + guiType + guiIdx + idxName);
- let label = Engine.GetGUIObjectByName(guiName + "Text" + guiIdx + idxName);
- let frame = Engine.GetGUIObjectByName(guiName + "Frame" + guiIdx + idxName);
- let title = Engine.GetGUIObjectByName(guiName + "Title" + guiIdx + idxName);
-
- if (guiType == "Dropdown")
- Engine.GetGUIObjectByName(guiName + "Checkbox" + guiIdx).hidden = true;
-
- let indexHidden = isControlArrayElementHidden(playerIdx);
- let obj = (playerIdx === undefined ? g_Dropdowns : g_PlayerDropdowns)[name];
-
- let hidden = indexHidden || !!obj.hidden && obj.hidden(playerIdx);
- let selected = hidden ? -1 : dropdown.list_data.indexOf(String(obj.get(playerIdx)));
- let enabled = !indexHidden && (!obj.enabled || obj.enabled(playerIdx));
-
- dropdown.enabled = g_IsController && enabled;
- dropdown.hidden = !g_IsController || !enabled || hidden;
- dropdown.selected = selected;
- dropdown.tooltip = !indexHidden && obj.tooltip ? obj.tooltip(-1, playerIdx) : "";
-
- if (frame)
- frame.hidden = hidden;
-
- if (title && obj.title && !indexHidden)
- title.caption = sprintf(translateWithContext("Title for specific setting", "%(setting)s:"), { "setting": obj.title(playerIdx) });
-
- if (label && !indexHidden)
- {
- label.hidden = g_IsController && enabled || hidden;
- label.caption = selected == -1 ? translateWithContext("settings value", "Unknown") : dropdown.list[selected];
- }
-}
-
-/**
- * Not used for the player assignments, so playerCheckboxes are not implemented,
- * hence no index.
- */
-function updateGUICheckbox(name)
-{
- let obj = g_Checkboxes[name];
-
- let checked = obj.get();
- let hidden = !!obj.hidden && obj.hidden();
- let enabled = !obj.enabled || obj.enabled();
-
- let [guiName, guiType, guiIdx] = getGUIObjectNameFromSetting(name);
- let checkbox = Engine.GetGUIObjectByName(guiName + guiType + guiIdx);
- let label = Engine.GetGUIObjectByName(guiName + "Text" + guiIdx);
- let frame = Engine.GetGUIObjectByName(guiName + "Frame" + guiIdx);
- let title = Engine.GetGUIObjectByName(guiName + "Title" + guiIdx);
-
- if (guiType == "Checkbox")
- Engine.GetGUIObjectByName(guiName + "Dropdown" + guiIdx).hidden = true;
-
- checkbox.checked = checked;
- checkbox.enabled = g_IsController && enabled;
- checkbox.hidden = hidden || !g_IsController;
- checkbox.tooltip = obj.tooltip ? obj.tooltip() : "";
-
- label.caption = checked ? translate("Yes") : translate("No");
- label.hidden = hidden || g_IsController;
-
- if (frame)
- frame.hidden = hidden;
-
- if (title && obj.title)
- title.caption = sprintf(translate("%(setting)s:"), { "setting": obj.title() });
-}
-
-function updateGUIMiscControl(name, playerIdx)
-{
- let idxName = playerIdx === undefined ? "" : "[" + playerIdx + "]";
- let obj = (playerIdx === undefined ? g_MiscControls : g_PlayerMiscElements)[name];
-
- let control = Engine.GetGUIObjectByName(name + idxName);
- if (!control)
- warn("No GUI object with name '" + name + "'");
-
- let hide = isControlArrayElementHidden(playerIdx);
- control.hidden = hide;
-
- if (hide)
- return;
-
- for (let property in obj)
- control[property] = obj[property](playerIdx);
-}
-
-function launchGame()
-{
- if (!g_GameAttributes.map || g_GameStarted)
- return;
-
- // Prevent reseting the readystate or calling this function twice
- g_GameStarted = true;
- updateGUIMiscControl("startGame");
-
- savePersistMatchSettings();
-
- // Select random map
- if (g_GameAttributes.map == "random")
- selectMap(pickRandom(g_Dropdowns.mapSelection.ids().slice(1)));
-
- if (g_GameAttributes.settings.Biome == "random")
- g_GameAttributes.settings.Biome = pickRandom(
- typeof g_GameAttributes.settings.SupportedBiomes == "string" ?
- g_BiomeList.Id.slice(1).filter(biomeID => biomeID.startsWith(g_GameAttributes.settings.SupportedBiomes)) :
- g_GameAttributes.settings.SupportedBiomes);
-
- g_GameAttributes.settings.VictoryScripts = g_GameAttributes.settings.VictoryConditions.reduce(
- (scripts, victoryConditionName) => scripts.concat(g_VictoryConditions[g_VictoryConditions.map(data =>
- data.Name).indexOf(victoryConditionName)].Scripts.filter(script => scripts.indexOf(script) == -1)),
- []);
-
- g_GameAttributes.settings.TriggerScripts = g_GameAttributes.settings.VictoryScripts.concat(g_GameAttributes.settings.TriggerScripts || []);
-
- g_GameAttributes.settings.mapType = g_GameAttributes.mapType;
-
- // 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);
-
- // Determine random civs and botnames
- for (let i in g_GameAttributes.settings.PlayerData)
- {
- // Pick a random civ of a random culture
- let chosenCiv = g_GameAttributes.settings.PlayerData[i].Civ || "random";
- if (chosenCiv == "random")
- {
- let culture = pickRandom(cultures);
- chosenCiv = pickRandom(Object.keys(g_CivData).filter(civ =>
- g_CivData[civ].Culture == culture && g_CivData[civ].SelectableInGameSetup));
- }
- g_GameAttributes.settings.PlayerData[i].Civ = chosenCiv;
-
- // Pick one of the available botnames for the chosen civ
- if (g_GameAttributes.mapType === "scenario" || !g_GameAttributes.settings.PlayerData[i].AI)
- continue;
-
- let chosenName = pickRandom(g_CivData[chosenCiv].AINames);
-
- if (!g_IsNetworked)
- chosenName = translate(chosenName);
-
- // Count how many players use the chosenName
- let usedName = g_GameAttributes.settings.PlayerData.filter(pData => pData.Name && pData.Name.indexOf(chosenName) !== -1).length;
-
- g_GameAttributes.settings.PlayerData[i].Name = !usedName ? chosenName :
- sprintf(translate("%(playerName)s %(romanNumber)s"), {
- "playerName": chosenName,
- "romanNumber": g_RomanNumbers[usedName+1]
- });
- }
-
- // Copy playernames for the purpose of replays
- for (let guid in g_PlayerAssignments)
- {
- let player = g_PlayerAssignments[guid];
- if (player.player > 0) // not observer or GAIA
- g_GameAttributes.settings.PlayerData[player.player - 1].Name = player.name;
- }
-
- // Seed used for both map generation and simulation
- g_GameAttributes.settings.Seed = randIntExclusive(0, Math.pow(2, 32));
- g_GameAttributes.settings.AISeed = randIntExclusive(0, Math.pow(2, 32));
-
- // Used for identifying rated game reports for the lobby
- g_GameAttributes.matchID = Engine.GetMatchID();
-
- if (g_IsNetworked)
- {
- Engine.SetNetworkGameAttributes(g_GameAttributes);
- Engine.StartNetworkGame();
- }
- else
- {
- // Find the player ID which the user has been assigned to
- let playerID = -1;
- for (let i in g_GameAttributes.settings.PlayerData)
- {
- let assignBox = Engine.GetGUIObjectByName("playerAssignment[" + i + "]");
- if (assignBox.list_data[assignBox.selected] == "guid:local")
- playerID = +i + 1;
- }
-
- Engine.StartGame(g_GameAttributes, playerID);
- Engine.SwitchGuiPage("page_loading.xml", {
- "attribs": g_GameAttributes,
- "playerAssignments": g_PlayerAssignments
- });
- }
-}
-
-function launchTutorial()
-{
- g_GameAttributes.mapType = "scenario";
- selectMap("maps/tutorials/starting_economy_walkthrough");
- launchGame();
-}
-
-/**
- * Don't set any attributes here, just show the changes in the GUI.
- *
- * Unless the mapsettings don't specify a property and the user didn't set it in g_GameAttributes previously.
- */
-function updateGUIObjects()
-{
- g_IsInGuiUpdate = true;
-
- reloadMapFilterList();
- reloadMapSpecific();
- reloadGameSpeedChoices();
- reloadPlayerAssignmentChoices();
-
- // Hide exceeding dropdowns and checkboxes
- for (let setting of Engine.GetGUIObjectByName("settingsPanel").children)
- setting.hidden = true;
-
- // Show the relevant ones
- if (g_TabCategorySelected !== undefined)
- {
- for (let name in g_Dropdowns)
- if (g_SettingsTabsGUI[g_TabCategorySelected].settings.indexOf(name) != -1)
- updateGUIDropdown(name);
-
- for (let name in g_Checkboxes)
- if (g_SettingsTabsGUI[g_TabCategorySelected].settings.indexOf(name) != -1)
- updateGUICheckbox(name);
- }
-
- for (let i = 0; i < g_MaxPlayers; ++i)
- {
- for (let name in g_PlayerDropdowns)
- updateGUIDropdown(name, i);
-
- for (let name in g_PlayerMiscElements)
- updateGUIMiscControl(name, i);
- }
-
- for (let name in g_MiscControls)
- updateGUIMiscControl(name);
-
- distributeSettings();
-
- g_IsInGuiUpdate = false;
-
- // Refresh AI config page
- if (g_LastViewedAIPlayer != -1)
- {
- let playerIndex = g_LastViewedAIPlayer;
- Engine.PopGuiPage();
- openAIConfig(playerIndex);
- }
-}
-
-/**
- * Broadcast the changed settings to all clients and the lobbybot.
- */
-function updateGameAttributes()
-{
- if (g_IsInGuiUpdate || !g_IsController)
- return;
-
- if (g_IsNetworked)
- {
- Engine.SetNetworkGameAttributes(g_GameAttributes);
- if (g_LoadingState >= 2)
- sendRegisterGameStanza();
- resetReadyData();
- }
- else
- updateGUIObjects();
-}
-
-function openAIConfig(playerSlot)
-{
- if (!g_GameAttributes.settings.PlayerData[playerSlot])
- return;
-
- g_LastViewedAIPlayer = playerSlot;
-
- Engine.PushGuiPage(
- "page_aiconfig.xml",
- {
- "playerSlot": playerSlot,
- "id": g_GameAttributes.settings.PlayerData[playerSlot].AI,
- "difficulty": g_GameAttributes.settings.PlayerData[playerSlot].AIDiff,
- "behavior": g_GameAttributes.settings.PlayerData[playerSlot].AIBehavior
- },
- ai => {
- g_LastViewedAIPlayer = -1;
-
- if (!ai || !ai.save || !g_IsController)
- return;
-
- g_GameAttributes.settings.PlayerData[ai.playerSlot].AI = ai.id;
- g_GameAttributes.settings.PlayerData[ai.playerSlot].AIDiff = ai.difficulty;
- g_GameAttributes.settings.PlayerData[ai.playerSlot].AIBehavior = ai.behavior;
-
- updateGameAttributes();
- });
-}
-
-function reloadPlayerAssignmentChoices()
-{
- let playerChoices = sortGUIDsByPlayerID().map(guid => ({
- "Choice": "guid:" + guid,
- "Color": g_PlayerAssignments[guid].player == -1 ? g_PlayerAssignmentColors.observer : g_PlayerAssignmentColors.player,
- "Name": g_PlayerAssignments[guid].name
- }));
-
- // Only display hidden AIs if the map preselects them
- let aiChoices = g_Settings.AIDescriptions
- .filter(ai => !ai.data.hidden || g_GameAttributes.settings.PlayerData.some(pData => pData.AI == ai.id))
- .map(ai => ({
- "Choice": "ai:" + ai.id,
- "Name": sprintf(translate("AI: %(ai)s"), {
- "ai": translate(ai.data.name)
- }),
- "Color": g_PlayerAssignmentColors.AI
- }));
-
- let unassignedSlot = [{
- "Choice": "unassigned",
- "Name": translate("Unassigned"),
- "Color": g_PlayerAssignmentColors.unassigned
- }];
- g_PlayerAssignmentList = prepareForDropdown(playerChoices.concat(aiChoices).concat(unassignedSlot));
-
- initPlayerDropdowns("playerAssignment");
-}
-
-function swapPlayers(guidToSwap, newSlot)
-{
- // Player slots are indexed from 0 as Gaia is omitted.
- let newPlayerID = newSlot + 1;
- let playerID = g_PlayerAssignments[guidToSwap].player;
-
- // Attempt to swap the player or AI occupying the target slot,
- // if any, into the slot this player is currently in.
- if (playerID != -1)
- {
- for (let guid in g_PlayerAssignments)
- {
- // Move the player in the destination slot into the current slot.
- if (g_PlayerAssignments[guid].player != newPlayerID)
- continue;
-
- if (g_IsNetworked)
- Engine.AssignNetworkPlayer(playerID, guid);
- else
- g_PlayerAssignments[guid].player = playerID;
- break;
- }
-
- // Transfer the AI from the target slot to the current slot.
- g_GameAttributes.settings.PlayerData[playerID - 1].AI = g_GameAttributes.settings.PlayerData[newSlot].AI;
- g_GameAttributes.settings.PlayerData[playerID - 1].AIDiff = g_GameAttributes.settings.PlayerData[newSlot].AIDiff;
- g_GameAttributes.settings.PlayerData[playerID - 1].AIBehavior = g_GameAttributes.settings.PlayerData[newSlot].AIBehavior;
-
- // Swap civilizations and colors if they aren't fixed
- if (g_GameAttributes.mapType != "scenario")
- {
- [g_GameAttributes.settings.PlayerData[playerID - 1].Civ, g_GameAttributes.settings.PlayerData[newSlot].Civ] =
- [g_GameAttributes.settings.PlayerData[newSlot].Civ, g_GameAttributes.settings.PlayerData[playerID - 1].Civ];
- [g_GameAttributes.settings.PlayerData[playerID - 1].Color, g_GameAttributes.settings.PlayerData[newSlot].Color] =
- [g_GameAttributes.settings.PlayerData[newSlot].Color, g_GameAttributes.settings.PlayerData[playerID - 1].Color];
- }
- }
-
- if (g_IsNetworked)
- Engine.AssignNetworkPlayer(newPlayerID, guidToSwap);
- else
- g_PlayerAssignments[guidToSwap].player = newPlayerID;
-
- g_GameAttributes.settings.PlayerData[newSlot].AI = "";
-}
-
-function submitChatInput()
-{
- let chatInput = Engine.GetGUIObjectByName("chatInput");
- let text = chatInput.caption;
- if (!text.length)
- return;
-
- chatInput.caption = "";
-
- if (!executeNetworkCommand(text))
- Engine.SendNetworkChat(text);
-
- chatInput.focus();
-}
-
-function systemMessage(message)
-{
- return setStringTags(sprintf(translate("== %(message)s"), { "message": message }), g_SenderFontTag);
-}
-
-function colorizePlayernameByGUID(guid, username = "")
-{
- // TODO: Maybe the host should have the moderator-prefix?
- if (!username)
- username = g_PlayerAssignments[guid] ? escapeText(g_PlayerAssignments[guid].name) : translate("Unknown Player");
- let playerID = g_PlayerAssignments[guid] ? g_PlayerAssignments[guid].player : -1;
-
- let color = g_ColorRegular;
- if (playerID > 0)
- {
- color = g_GameAttributes.settings.PlayerData[playerID - 1].Color;
-
- // 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);
-}
-
-function addChatMessage(msg)
-{
- if (!g_FormatChatMessage[msg.type])
- return;
-
- if (msg.type == "chat")
- {
- let userName = g_PlayerAssignments[Engine.GetPlayerGUID()].name;
- if (userName != g_PlayerAssignments[msg.guid].name &&
- msg.text.toLowerCase().indexOf(splitRatingFromNick(userName).nick.toLowerCase()) != -1)
- soundNotification("nick");
- }
-
- let user = colorizePlayernameByGUID(msg.guid || -1, msg.username || "");
-
- let text = g_FormatChatMessage[msg.type](msg, user);
-
- if (!text)
- return;
-
- if (Engine.ConfigDB_GetValue("user", "chat.timestamp") == "true")
- text = sprintf(translate("%(time)s %(message)s"), {
- "time": sprintf(translate("\\[%(time)s]"), {
- "time": Engine.FormatMillisecondsIntoDateStringLocal(Date.now(), translate("HH:mm"))
- }),
- "message": text
- });
-
- g_ChatMessages.push(text);
-
- Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
-}
-
-function clearChatMessages()
-{
- g_ChatMessages.length = 0;
- Engine.GetGUIObjectByName("chatText").caption = "";
-}
-
-function setReady(ready, sendMessage)
-{
- g_IsReady = ready;
-
- if (sendMessage)
- Engine.SendNetworkReady(g_IsReady);
-
- updateGUIObjects();
-}
-
-function resetReadyData()
-{
- if (g_GameStarted)
- return;
-
- if (g_ReadyChanged < 1)
- addChatMessage({ "type": "settings" });
- else if (g_ReadyChanged == 2 && !g_ReadyInit)
- return; // duplicate calls on init
- else
- g_ReadyInit = false;
-
- g_ReadyChanged = 2;
- if (!g_IsNetworked)
- g_IsReady = 2;
- else if (g_IsController)
- {
- Engine.ClearAllPlayerReady();
- setReady(2, true);
- }
- else if (g_IsReady != 2)
- setReady(0, false);
-}
-
-/**
- * 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.
- */
-function 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])
- ++connectedPlayers;
- else
- pData.Team = "observer";
-
- playerData.push(pData);
- }
-
- return {
- "list": playerDataToStringifiedTeamList(playerData),
- "connectedPlayers": connectedPlayers
- };
-}
-
/**
- * Send the relevant gamesettings to the lobbybot immediately.
+ * This instance owns all handlers that control the two synchronized states g_GameAttributes and g_PlayerAssignments.
*/
-function sendRegisterGameStanzaImmediate()
-{
- if (!g_IsController || !Engine.HasXmppClient())
- return;
-
- if (g_GameStanzaTimer !== undefined)
- {
- clearTimeout(g_GameStanzaTimer);
- g_GameStanzaTimer = undefined;
- }
-
- let clients = formatClientsForStanza();
-
- let stanza = {
- "name": g_ServerName,
- "port": g_ServerPort,
- "hostUsername": Engine.LobbyGetNick(),
- "mapName": g_GameAttributes.map,
- "niceMapName": getMapDisplayName(g_GameAttributes.map),
- "mapSize": g_GameAttributes.mapType == "random" ? g_GameAttributes.settings.Size : "Default",
- "mapType": g_GameAttributes.mapType,
- "victoryConditions": g_GameAttributes.settings.VictoryConditions.join(","),
- "nbp": clients.connectedPlayers,
- "maxnbp": g_GameAttributes.settings.PlayerData.length,
- "players": clients.list,
- "stunIP": g_StunEndpoint ? g_StunEndpoint.ip : "",
- "stunPort": g_StunEndpoint ? g_StunEndpoint.port : "",
- "mods": JSON.stringify(Engine.GetEngineInfo().mods),
- };
-
- // Only send the stanza if the relevant settings actually changed
- if (g_LastGameStanza && Object.keys(stanza).every(prop => g_LastGameStanza[prop] == stanza[prop]))
- return;
+var g_GamesetupPage;
- g_LastGameStanza = stanza;
- Engine.SendRegisterGame(stanza);
-}
+// TODO: Remove these two global functions by specifying the JS class name in the XML of the GUI page.
-/**
- * Send the relevant gamesettings to the lobbybot in a deferred manner.
- */
-function sendRegisterGameStanza()
+function init(initData, hotloadData)
{
- if (!g_IsController || !Engine.HasXmppClient())
- return;
-
- if (g_GameStanzaTimer !== undefined)
- clearTimeout(g_GameStanzaTimer);
-
- g_GameStanzaTimer = setTimeout(sendRegisterGameStanzaImmediate, g_GameStanzaTimeout * 1000);
+ g_GamesetupPage = new GamesetupPage(initData, hotloadData);
}
-function storeCivInfoPage(data)
+function getHotloadData()
{
- if (data.nextPage)
- Engine.PushGuiPage(
- data.nextPage,
- { "civ": data.civ },
- storeCivInfoPage);
- else
- g_CivInfo = data;
+ return g_GamesetupPage.getHotloadData();
}
Index: ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/GameDetails.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/GameDetails.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/GameDetails.js (revision 23374)
@@ -1,141 +1,141 @@
/**
* The purpose of this class is to display information about the selected game.
*/
class GameDetails
{
- constructor(dialog, gameList)
+ constructor(dialog, gameList, mapCache)
{
+ this.mapCache = mapCache;
+
this.playernameArgs = {};
this.playerCountArgs = {};
this.gameStartArgs = {};
this.lastGame = {};
this.gameDetails = Engine.GetGUIObjectByName("gameDetails");
this.sgMapName = Engine.GetGUIObjectByName("sgMapName");
this.sgGame = Engine.GetGUIObjectByName("sgGame");
this.sgPlayersNames = Engine.GetGUIObjectByName("sgPlayersNames");
this.sgMapSize = Engine.GetGUIObjectByName("sgMapSize");
this.sgMapPreview = Engine.GetGUIObjectByName("sgMapPreview");
this.sgMapDescription = Engine.GetGUIObjectByName("sgMapDescription");
gameList.registerSelectionChangeHandler(this.onGameListSelectionChange.bind(this));
this.resize(dialog);
}
resize(dialog)
{
let bottom = Engine.GetGUIObjectByName(dialog ? "leaveButton" : "joinButton").size.top - 5;
let size = this.gameDetails.size;
size.bottom = bottom;
this.gameDetails.size = size;
}
/**
* Populate the game info area with information on the current game selection.
*/
onGameListSelectionChange(game)
{
this.gameDetails.hidden = !game;
if (!game)
return;
Engine.ProfileStart("GameDetails");
let stanza = game.stanza;
+ let displayData = game.displayData;
+
if (stanza.mapType != this.lastGame.mapType || stanza.mapName != this.lastGame.mapName)
{
- let mapData = getMapDescriptionAndPreview(stanza.mapType, stanza.mapName);
- this.sgMapPreview.sprite = getMapPreviewImage(mapData.preview);
- this.mapDescription = mapData.description;
+ this.sgMapName.caption = displayData.mapName;
+ this.sgMapPreview.sprite = this.mapCache.getMapPreview(stanza.mapType, stanza.mapName);
}
- let displayData = game.displayData;
- this.sgMapName.caption = displayData.mapName;
-
{
let txt;
if (game.isCompatible)
txt =
setStringTags(this.VictoryConditionsFormat, this.CaptionTags) + " " +
(stanza.victoryConditions ?
stanza.victoryConditions.split(",").map(translateVictoryCondition).join(this.Comma) :
translateWithContext("victory condition", "Endless Game"));
else
txt =
setStringTags(this.ModsFormat, this.CaptionTags) + " " +
escapeText(modsToString(game.mods, Engine.GetEngineInfo().mods));
txt +=
"\n" + setStringTags(this.MapTypeFormat, this.CaptionTags) + " " + displayData.mapType +
"\n" + setStringTags(this.MapSizeFormat, this.CaptionTags) + " " + displayData.mapSize +
- "\n" + setStringTags(this.MapDescriptionFormat, this.CaptionTags) + " " + this.mapDescription;
+ "\n" + setStringTags(this.MapDescriptionFormat, this.CaptionTags) + " " + displayData.mapDescription;
this.sgMapDescription.caption = txt;
}
{
let txt = escapeText(stanza.name);
this.playernameArgs.playername = escapeText(stanza.hostUsername);
txt += "\n" + sprintf(this.HostFormat, this.playernameArgs);
this.playerCountArgs.current = escapeText(stanza.nbp);
this.playerCountArgs.total = escapeText(stanza.maxnbp);
txt += "\n" + sprintf(this.PlayerCountFormat, this.playerCountArgs);
if (stanza.startTime)
{
this.gameStartArgs.time = Engine.FormatMillisecondsIntoDateStringLocal(+stanza.startTime * 1000, this.TimeFormat);
txt += "\n" + sprintf(this.GameStartFormat, this.gameStartArgs);
}
this.sgGame.caption = txt;
}
{
let textHeight = this.sgGame.getTextSize().height;
let sgGameSize = this.sgGame.size;
sgGameSize.bottom = textHeight;
this.sgGame.size = sgGameSize;
let sgPlayersNamesSize = this.sgPlayersNames.size;
sgPlayersNamesSize.top = textHeight + 5;
this.sgPlayersNames.size = sgPlayersNamesSize;
}
this.sgPlayersNames.caption = formatPlayerInfo(game.players);
this.lastGame = game;
Engine.ProfileStop();
}
}
GameDetails.prototype.HostFormat = translate("Host: %(playername)s");
GameDetails.prototype.PlayerCountFormat = translate("Players: %(current)s/%(total)s");
GameDetails.prototype.VictoryConditionsFormat = translate("Victory Conditions:");
// Translation: Comma used to concatenate victory conditions
GameDetails.prototype.Comma = translate(", ");
GameDetails.prototype.ModsFormat = translate("Mods:");
// Translation: %(time)s is the hour and minute here.
GameDetails.prototype.GameStartFormat = translate("Game started at %(time)s");
GameDetails.prototype.TimeFormat = translate("HH:mm");
GameDetails.prototype.MapTypeFormat = translate("Map Type:");
GameDetails.prototype.MapSizeFormat = translate("Map Size:");
GameDetails.prototype.MapDescriptionFormat = translate("Map Description:");
GameDetails.prototype.CaptionTags = {
"font": "sans-bold-14"
};
Index: ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/replaymenu/replay_menu.js (revision 23374)
@@ -1,364 +1,365 @@
/**
* 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_SummarySelectedData;
+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;
}
loadReplays(data && data.replaySelectionData, false);
if (!g_Replays)
{
Engine.SwitchGuiPage("page_pregame.xml");
return;
}
initHotkeyTooltips();
displayReplayList();
if (data && data.summarySelectedData)
g_SummarySelectedData = data.summarySelectedData;
}
/**
* 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.settings.mapType)
- attribs.settings.mapType = "skirmish";
+ 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), 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.settings.mapType);
+ 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));
- let mapData = getMapDescriptionAndPreview(replay.attribs.settings.mapType, replay.attribs.map, replay.attribs);
- Engine.GetGUIObjectByName("sgMapPreview").sprite = getMapPreviewImage(mapData.preview);
- Engine.GetGUIObjectByName("sgMapDescription").caption = mapData.description;
+ Engine.GetGUIObjectByName("sgMapPreview").sprite = g_MapCache.getMapPreview(replay.attribs.mapType, replay.attribs.map, replay.attribs);
+ 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/maps/random/polar_sea.json
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/polar_sea.json (revision 23373)
+++ ps/trunk/binaries/data/mods/public/maps/random/polar_sea.json (revision 23374)
@@ -1,14 +1,19 @@
{
"settings" : {
"Name" : "Polar Sea",
"Script" : "polar_sea.js",
"Description" : "Players start in a cold polar region barren of vegetation. In the sea fish and whales abound, while the fragile icy land teems with huntable walruses and deadly wolves. These wolves, made ravenous by the harsh and forbidding climate, drawn by the scent of prey, have started appearing in terrifying numbers. A wise and strong ruler will not only achieve victory over his enemies, but also keep the number of these beasts at bay, lest they undermine his economy and cause his downfall. [color=\"red\"]Warning: It is inadvisable to disable treasures, since there is no gatherable wood. Not recommended for inexperienced players.[/color]",
"Keywords": ["trigger"],
"Preview" : "polar_sea.png",
"CircularMap" : true,
"TriggerScripts" : [
"scripts/TriggerHelper.js",
"random/polar_sea_triggers.js"
+ ],
+ "DisabledTechnologies": [
+ "gather_lumbering_ironaxes",
+ "gather_lumbering_sharpaxes",
+ "gather_lumbering_strongeraxes"
]
}
}
Index: ps/trunk/binaries/data/mods/public/simulation/helpers/InitGame.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/InitGame.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/InitGame.js (revision 23374)
@@ -1,87 +1,77 @@
/**
* Called when the map has been loaded, but before the simulation has started.
* Only called when a new game is started, not when loading a saved game.
*/
function PreInitGame()
{
// We need to replace skirmish "default" entities with real ones.
// This needs to happen before AI initialization (in InitGame).
// And we need to flush destroyed entities otherwise the AI gets the wrong game state in
// the beginning and a bunch of "destroy" messages on turn 0, which just shouldn't happen.
Engine.BroadcastMessage(MT_SkirmishReplace, {});
Engine.FlushDestroyedEntities();
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let i = 1; i < numPlayers; ++i) // ignore gaia
{
let cmpTechnologyManager = QueryPlayerIDInterface(i, IID_TechnologyManager);
if (cmpTechnologyManager)
cmpTechnologyManager.UpdateAutoResearch();
}
// Explore the map inside the players' territory borders
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
cmpRangeManager.ExploreTerritories();
}
function InitGame(settings)
{
// No settings when loading a map in Atlas, so do nothing
if (!settings)
{
// Map dependent initialisations of components (i.e. garrisoned units)
Engine.BroadcastMessage(MT_InitGame, {});
return;
}
if (settings.ExploreMap)
{
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
for (let i = 1; i < settings.PlayerData.length; ++i)
cmpRangeManager.ExploreAllTiles(i);
}
// Sandbox, Very Easy, Easy, Medium, Hard, Very Hard
// rate apply on resource stockpiling as gathering and trading
// time apply on building, upgrading, packing, training and technologies
let rate = [ 0.42, 0.56, 0.75, 1.00, 1.25, 1.56 ];
let time = [ 1.40, 1.25, 1.10, 1.00, 1.00, 1.00 ];
let cmpModifiersManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ModifiersManager);
let cmpAIManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIManager);
for (let i = 0; i < settings.PlayerData.length; ++i)
{
let cmpPlayer = QueryPlayerIDInterface(i);
cmpPlayer.SetCheatsEnabled(!!settings.CheatsEnabled);
- if (settings.PlayerData[i] && settings.PlayerData[i].AI && settings.PlayerData[i].AI != "")
+
+ if (settings.PlayerData[i] && !!settings.PlayerData[i].AI)
{
let AIDiff = +settings.PlayerData[i].AIDiff;
cmpAIManager.AddPlayer(settings.PlayerData[i].AI, i, AIDiff, settings.PlayerData[i].AIBehavior || "random");
cmpPlayer.SetAI(true);
AIDiff = Math.min(AIDiff, rate.length - 1);
cmpModifiersManager.AddModifiers("AI Bonus", {
"ResourceGatherer/BaseSpeed": [{ "affects": ["Unit", "Structure"], "multiply": rate[AIDiff] }],
"Trader/GainMultiplier": [{ "affects": ["Unit", "Structure"], "multiply": rate[AIDiff] }],
"Cost/BuildTime": [{ "affects": ["Unit", "Structure"], "multiply": time[AIDiff] }],
}, cmpPlayer.entity);
}
- if (settings.PopulationCap)
- cmpPlayer.SetMaxPopulation(settings.PopulationCap);
-
- if (settings.mapType !== "scenario" && settings.StartingResources)
- {
- let resourceCounts = cmpPlayer.GetResourceCounts();
- let newResourceCounts = {};
- for (let resouces in resourceCounts)
- newResourceCounts[resouces] = settings.StartingResources;
- cmpPlayer.SetResourceCounts(newResourceCounts);
- }
}
// Map or player data (handicap...) dependent initialisations of components (i.e. garrisoned units)
Engine.BroadcastMessage(MT_InitGame, {});
cmpAIManager.TryLoadSharedComponent();
cmpAIManager.RunGamestateInit();
}
Engine.RegisterGlobal("PreInitGame", PreInitGame);
Engine.RegisterGlobal("InitGame", InitGame);
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.xml (revision 23374)
@@ -0,0 +1,11 @@
+
+
+
+
+ Loading
+
+
+
+ Loading map data. Please wait…
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tips.txt
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tips.txt (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tips.txt (revision 23374)
@@ -0,0 +1 @@
+Although reasonably easy for an experienced player, the default AI level is quite challenging for new players yet to master the basic mechanisms of the game. Beginners are advised to start by playing against a lower level AI (Sandbox or Very Easy). Change the AI level by clicking the gear icon next to the player you want to modify in the selection panel above.
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tips.txt
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.xml (revision 23374)
@@ -0,0 +1,7 @@
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/Game.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/Game.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/Game.js (revision 23374)
@@ -1,338 +1,341 @@
/**
* This class represents a multiplayer match hosted by a player in the lobby.
* Having this represented as a class allows to leverage significant performance
* gains by caching computed, escaped, translated strings and sorting keys.
*
* Additionally class representation allows implementation of events such as
* a new match being hosted, a match having ended, or a buddy having joined a match.
*
* Ensure that escapeText is applied to player controlled data for display.
*
* Users of the properties of this class:
* GameList, GameDetails, MapFilters, JoinButton, any user of GameList.selectedGame()
*/
class Game
{
- constructor()
+ constructor(mapCache)
{
+ this.mapCache = mapCache;
+
// Stanza data, object with exclusively string values
// Used to compare which part of the stanza data changed,
// perform partial updates and trigger event notifications.
this.stanza = {};
for (let name of this.StanzaKeys)
this.stanza[name] = "";
// This will be displayed in the GameList and GameDetails
// Important: Player input must be processed with escapeText
this.displayData = {
"tags": {}
};
// Cache the values used for sorting
this.sortValues = {
"state": "",
"compatibility": "",
"hasBuddyString": ""
};
// Array of objects, result of stringifiedTeamListToPlayerData
this.players = [];
// Whether the current player has the same mods launched as the host of this game
this.isCompatible = undefined;
// Used to display which mods are missing if the player attempts a join
this.mods = undefined;
// Used by the rating column and rating filer
this.gameRating = undefined;
// 'Persistent temporary' sprintf arguments object to avoid repeated object construction
this.playerCountArgs = {};
}
/**
* Called from GameList to ensure call order.
*/
onBuddyChange()
{
this.updatePlayers(this.stanza);
}
/**
* This function computes values that will either certainly or
* most likely be used later (i.e. by filtering, sorting and gamelist display).
*
* The performance benefit arises from the fact that for a new gamelist stanza
* many if not most games and game properties did not change.
*/
update(newStanza, sortKey)
{
let oldStanza = this.stanza;
let displayData = this.displayData;
let sortValues = this.sortValues;
if (oldStanza.name != newStanza.name)
{
Engine.ProfileStart("gameName");
sortValues.gameName = newStanza.name.toLowerCase();
this.updateGameName(newStanza);
Engine.ProfileStop();
}
if (oldStanza.state != newStanza.state)
{
Engine.ProfileStart("gameState");
this.updateGameTags(newStanza);
sortValues.state = this.GameStatusOrder.indexOf(newStanza.state);
Engine.ProfileStop();
}
if (oldStanza.niceMapName != newStanza.niceMapName)
{
Engine.ProfileStart("niceMapName");
- displayData.mapName = escapeText(translateMapTitle(newStanza.niceMapName));
+ displayData.mapName = escapeText(this.mapCache.translateMapName(newStanza.niceMapName));
+ displayData.mapDescription = this.mapCache.getTranslatedMapDescription(newStanza.mapType, newStanza.mapName);
Engine.ProfileStop();
}
if (oldStanza.mapName != newStanza.mapName)
{
Engine.ProfileStart("mapName");
sortValues.mapName = displayData.mapName;
Engine.ProfileStop();
}
if (oldStanza.mapType != newStanza.mapType)
{
Engine.ProfileStart("mapType");
displayData.mapType = g_MapTypes.Title[g_MapTypes.Name.indexOf(newStanza.mapType)] || "";
sortValues.mapType = newStanza.mapType;
Engine.ProfileStop();
}
if (oldStanza.mapSize != newStanza.mapSize)
{
Engine.ProfileStart("mapSize");
displayData.mapSize = translateMapSize(newStanza.mapSize);
sortValues.mapSize = newStanza.mapSize;
Engine.ProfileStop();
}
let playersChanged = oldStanza.players != newStanza.players;
if (playersChanged)
{
Engine.ProfileStart("playerData");
this.updatePlayers(newStanza);
Engine.ProfileStop();
}
if (oldStanza.nbp != newStanza.nbp ||
oldStanza.maxnbp != newStanza.maxnbp ||
playersChanged)
{
Engine.ProfileStart("playerCount");
displayData.playerCount = this.getTranslatedPlayerCount(newStanza);
sortValues.maxnbp = newStanza.maxnbp;
Engine.ProfileStop();
}
if (oldStanza.mods != newStanza.mods)
{
Engine.ProfileStart("mods");
this.updateMods(newStanza);
Engine.ProfileStop();
}
this.stanza = newStanza;
this.sortValue = this.sortValues[sortKey];
}
updatePlayers(newStanza)
{
let players;
{
Engine.ProfileStart("stringifiedTeamListToPlayerData");
players = stringifiedTeamListToPlayerData(newStanza.players);
this.players = players;
Engine.ProfileStop();
}
{
Engine.ProfileStart("parsePlayers");
let observerCount = 0;
let hasBuddies = 0;
let playerRatingTotal = 0;
for (let player of players)
{
let playerNickRating = splitRatingFromNick(player.Name);
if (player.Team == "observer")
++observerCount;
else
playerRatingTotal += playerNickRating.rating || g_DefaultLobbyRating;
// Sort games with playing buddies above games with spectating buddies
if (hasBuddies < 2 && g_Buddies.indexOf(playerNickRating.nick) != -1)
hasBuddies = player.Team == "observer" ? 1 : 2;
}
this.observerCount = observerCount;
this.hasBuddies = hasBuddies;
let displayData = this.displayData;
let sortValues = this.sortValues;
displayData.buddy = this.hasBuddies ? setStringTags(g_BuddySymbol, displayData.tags) : "";
sortValues.hasBuddyString = String(hasBuddies);
sortValues.buddy = sortValues.hasBuddyString + sortValues.gameName;
let playerCount = players.length - observerCount;
let gameRating =
playerCount ?
Math.round(playerRatingTotal / playerCount) :
g_DefaultLobbyRating;
this.gameRating = gameRating;
sortValues.gameRating = gameRating;
Engine.ProfileStop();
}
}
updateMods(newStanza)
{
{
Engine.ProfileStart("JSON.parse");
try
{
this.mods = JSON.parse(newStanza.mods);
}
catch (e)
{
this.mods = [];
}
Engine.ProfileStop();
}
{
Engine.ProfileStart("hasSameMods");
let isCompatible = this.mods && hasSameMods(this.mods, Engine.GetEngineInfo().mods);
if (this.isCompatible != isCompatible)
{
this.isCompatible = isCompatible;
this.updateGameTags(newStanza);
this.sortValues.compatibility = String(isCompatible);
}
Engine.ProfileStop();
}
}
updateGameTags(newStanza)
{
let displayData = this.displayData;
displayData.tags = this.isCompatible ? this.StateTags[newStanza.state] : this.IncompatibleTags;
displayData.buddy = this.hasBuddies ? setStringTags(g_BuddySymbol, displayData.tags) : "";
this.updateGameName(newStanza);
}
updateGameName(newStanza)
{
let displayData = this.displayData;
displayData.gameName = setStringTags(escapeText(newStanza.name), displayData.tags);
let sortValues = this.sortValues;
sortValues.gameName = sortValues.compatibility + sortValues.state + sortValues.gameName;
sortValues.buddy = sortValues.hasBuddyString + sortValues.gameName;
}
getTranslatedPlayerCount(newStanza)
{
let playerCountArgs = this.playerCountArgs;
playerCountArgs.current = setStringTags(escapeText(newStanza.nbp), this.PlayerCountTags.CurrentPlayers);
playerCountArgs.max = setStringTags(escapeText(newStanza.maxnbp), this.PlayerCountTags.MaxPlayers);
let txt;
if (this.observerCount)
{
playerCountArgs.observercount = setStringTags(this.observerCount, this.PlayerCountTags.Observers);
txt = this.PlayerCountObservers;
}
else
txt = this.PlayerCountNoObservers;
return sprintf(txt, playerCountArgs);
}
}
/**
* These are all keys that occur in a gamelist stanza sent by XPartaMupp.
*/
Game.prototype.StanzaKeys = [
"name",
"ip",
"port",
"stunIP",
"stunPort",
"hostUsername",
"state",
"nbp",
"maxnbp",
"players",
"mapName",
"niceMapName",
"mapSize",
"mapType",
"victoryConditions",
"startTime",
"mods"
];
/**
* Initial sorting order of the gamelist.
*/
Game.prototype.GameStatusOrder = [
"init",
"waiting",
"running"
];
// Translation: The number of players and observers in this game
Game.prototype.PlayerCountObservers = translate("%(current)s/%(max)s +%(observercount)s");
// Translation: The number of players in this game
Game.prototype.PlayerCountNoObservers = translate("%(current)s/%(max)s");
/**
* Compatible games will be listed in these colors.
*/
Game.prototype.StateTags = {
"init": {
"color": "0 219 0"
},
"waiting": {
"color": "255 127 0"
},
"running": {
"color": "219 0 0"
}
};
/**
* Games that require different mods than the ones launched by the current player are grayed out.
*/
Game.prototype.IncompatibleTags = {
"color": "gray"
};
/**
* Color for the player count number in the games list.
*/
Game.prototype.PlayerCountTags = {
"CurrentPlayers": {
"color": "0 160 160"
},
"MaxPlayers": {
"color": "0 160 160"
},
"Observers": {
"color": "0 128 128"
}
};
Index: ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js (revision 23374)
@@ -1,203 +1,204 @@
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", {
- "tutorial": true
+ "mapType": "scenario",
+ "map": "maps/tutorials/starting_economy_walkthrough"
});
}
},
{
"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("History"),
"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("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", {});
+ Engine.SwitchGuiPage("page_gamesetup.xml");
}
},
{
"caption": translate("Campaigns"),
"tooltip": translate("Relive history through historical military campaigns. \\[NOT YET IMPLEMENTED]"),
"enabled": false
},
{
"caption": translate("Load Game"),
"tooltip": translate("Load a saved game."),
"onPress": () => {
Engine.PushGuiPage("page_loadgame.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("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("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/maps/random/jebel_barkal.json
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/jebel_barkal.json (revision 23373)
+++ ps/trunk/binaries/data/mods/public/maps/random/jebel_barkal.json (revision 23374)
@@ -1,24 +1,23 @@
{
"settings" : {
"Name" : "Jebel Barkal",
"Script" : "jebel_barkal.js",
"Description" : "Starting near the fertile banks of the Nile, the players besiege the heavily defended city Napata which lies at the foot of the hill Jebel Barkal, the \"Pure Mountain\". It is the Southern home of Amun, and according to Kushites and Egyptians alike, the birthplace of man. Known as the Throne of the Two Lands, the ancient religious capital of Napata lay in its shadow. This is where Kings were made... and unmade! Abutting a rich floodplain downstream from the 4th cataract, this area became the breadbasket of ancient Kush.",
"Preview" : "jebel_barkal.png",
"Keywords": ["trigger"],
"CircularMap": true,
"TriggerScripts" : [
"scripts/TriggerHelper.js",
"random/jebel_barkal_triggers.js"
],
"SupportedTriggerDifficulties": {
"Values": [
"Very Easy",
"Easy",
"Medium",
"Hard",
"Very Hard"
- ],
- "Default": "Medium"
+ ]
}
}
}
Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/trigger_difficulties.json
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/data/settings/trigger_difficulties.json (revision 23373)
+++ ps/trunk/binaries/data/mods/public/simulation/data/settings/trigger_difficulties.json (revision 23374)
@@ -1,36 +1,37 @@
{
"TranslatedKeys": ["Title", "Tooltip"],
"Data":
[
{
"Difficulty": 1,
"Name": "Very Easy",
"Title": "Very Easy",
"Tooltip": "Choose this difficulty if you are discovering 0 A.D."
},
{
"Difficulty": 2,
"Name": "Easy",
"Title": "Easy",
"Tooltip": "Choose this difficulty if you do not like being knocked down."
},
{
"Difficulty": 3,
"Name": "Medium",
"Title": "Medium",
- "Tooltip": "Choose this difficulty if you have already a good experience with 0 A.D."
+ "Tooltip": "Choose this difficulty if you have already a good experience with 0 A.D.",
+ "Default": true
},
{
"Difficulty": 4,
"Name": "Hard",
"Title": "Hard",
"Tooltip": "Choose this difficulty if you want to be really challenged."
},
{
"Difficulty": 5,
"Name": "Very Hard",
"Title": "Very Hard",
"Tooltip": "Choose this difficulty if you do not mind being swept out."
}
]
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.js (revision 23374)
@@ -0,0 +1,25 @@
+/**
+ * The purpose of this page is to display a placeholder in multiplayer until the settings from the server have been received.
+ * This is not technically necessary, but only performed to avoid confusion or irritation when showing the clients first the
+ * default settings and then switching to the server settings quickly thereafter.
+ */
+class LoadingWindow
+{
+ constructor(netMessages)
+ {
+ if (g_IsNetworked)
+ netMessages.registerNetMessageHandler("gamesetup", this.hideLoadingWindow.bind(this));
+ else
+ this.hideLoadingWindow();
+ }
+
+ hideLoadingWindow()
+ {
+ let loadingWindow = Engine.GetGUIObjectByName("loadingWindow");
+ if (loadingWindow.hidden)
+ return;
+
+ loadingWindow.hidden = true;
+ Engine.GetGUIObjectByName("setupWindow").hidden = false;
+ }
+}
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/LoadingWindow.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/SoundNotification.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/SoundNotification.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/SoundNotification.js (revision 23374)
@@ -0,0 +1,28 @@
+class SoundNotification
+{
+ constructor(netMessages, playerAssignmentsControl)
+ {
+ netMessages.registerNetMessageHandler("chat", this.onClientChat.bind(this));
+ playerAssignmentsControl.registerClientJoinHandler(this.onClientJoin.bind(this));
+ }
+
+ onClientJoin(guid)
+ {
+ if (guid != Engine.GetPlayerGUID())
+ soundNotification(this.ConfigJoinNotification);
+ }
+
+ onClientChat(message)
+ {
+ if (message.guid != Engine.GetPlayerGUID() &&
+ message.text.toLowerCase().indexOf(
+ splitRatingFromNick(g_PlayerAssignments[Engine.GetPlayerGUID()].name).nick.toLowerCase()) != -1)
+ soundNotification(this.ConfigNickNotification);
+ }
+}
+
+SoundNotification.prototype.ConfigJoinNotification =
+ "gamesetup.join";
+
+SoundNotification.prototype.ConfigNickNotification =
+ "nick";
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/SoundNotification.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.js (revision 23374)
@@ -0,0 +1,17 @@
+class Tooltip
+{
+ constructor(cancelButton)
+ {
+ this.onscreenToolTip = Engine.GetGUIObjectByName("onscreenToolTip");
+ cancelButton.registerCancelButtonResizeHandler(this.onCancelButtonResize.bind(this));
+ }
+
+ onCancelButtonResize(cancelButton)
+ {
+ let size = this.onscreenToolTip.size;
+ size.right = cancelButton.size.left - this.Margin;
+ this.onscreenToolTip.size = size;
+ }
+}
+
+Tooltip.prototype.Margin = 10;
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/Tooltip.js
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameDetails.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameDetails.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/loadgame/SavegameDetails.js (revision 23374)
@@ -1,43 +1,48 @@
/**
* 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 = translate(metadata.initAttributes.settings.Name);
- Engine.GetGUIObjectByName("savedInfoPreview").sprite = getMapPreviewImage(
- getMapDescriptionAndPreview(metadata.initAttributes.mapType, metadata.initAttributes.map, metadata.initAttributes).preview);
+ Engine.GetGUIObjectByName("savedMapName").caption =
+ this.mapCache.getMapName(metadata.initAttributes.mapType, metadata.initAttributes.map);
+
+ Engine.GetGUIObjectByName("savedInfoPreview").sprite =
+ this.mapCache.getMapPreview(metadata.initAttributes.mapType, metadata.initAttributes.map, metadata.initAttributes);
+
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);
+ 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/lobby/LobbyPage/LobbyPage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/LobbyPage.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/LobbyPage.js (revision 23374)
@@ -1,73 +1,74 @@
/**
* This class stores the handlers for all GUI objects in the lobby page,
* (excluding other pages in the same context such as leaderboard and profile page).
*/
class LobbyPage
{
constructor(dialog, xmppMessages, leaderboardPage, profilePage)
{
Engine.ProfileStart("Create LobbyPage");
+ let mapCache = new MapCache();
let buddyButton = new BuddyButton(xmppMessages);
- let gameList = new GameList(xmppMessages, buddyButton);
+ let gameList = new GameList(xmppMessages, buddyButton, mapCache);
let playerList = new PlayerList(xmppMessages, buddyButton, gameList);
this.lobbyPage = {
"buttons": {
"buddyButton": buddyButton,
"hostButton": new HostButton(dialog, xmppMessages),
"joinButton": new JoinButton(dialog, gameList),
"leaderboardButton": new LeaderboardButton(xmppMessages, leaderboardPage),
"profileButton": new ProfileButton(xmppMessages, profilePage),
"quitButton": new QuitButton(dialog, leaderboardPage, profilePage)
},
"panels": {
"chatPanel": new ChatPanel(xmppMessages),
- "gameDetails": new GameDetails(dialog, gameList),
+ "gameDetails": new GameDetails(dialog, gameList, mapCache),
"gameList": gameList,
"playerList": playerList,
"profilePanel": new ProfilePanel(xmppMessages, playerList, leaderboardPage),
"subject": new Subject(dialog, xmppMessages, gameList)
},
"eventHandlers": {
"announcementHandler": new AnnouncementHandler(xmppMessages),
"connectionHandler": new ConnectionHandler(xmppMessages),
}
};
if (dialog)
this.setDialogStyle();
Engine.ProfileStop();
}
setDialogStyle()
{
{
let lobbyPage = Engine.GetGUIObjectByName("lobbyPage");
lobbyPage.sprite = "ModernDialog";
let size = lobbyPage.size;
size.left = this.WindowMargin;
size.top = this.WindowMargin;
size.right = -this.WindowMargin;
size.bottom = -this.WindowMargin;
lobbyPage.size = size;
}
{
let lobbyPageTitle = Engine.GetGUIObjectByName("lobbyPageTitle");
let size = lobbyPageTitle.size;
size.top -= this.WindowMargin / 2;
size.bottom -= this.WindowMargin / 2;
lobbyPageTitle.size = size;
}
{
let lobbyPanels = Engine.GetGUIObjectByName("lobbyPanels");
let size = lobbyPanels.size;
size.top -= this.WindowMargin / 2;
lobbyPanels.size = size;
}
}
}
LobbyPage.prototype.WindowMargin = 40;
Index: ps/trunk/binaries/data/mods/public/gui/session/session.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 23374)
@@ -1,806 +1,805 @@
const g_IsReplay = Engine.IsVisualReplay();
const g_CivData = loadCivData(false, true);
-const g_Ceasefire = prepareForDropdown(g_Settings && g_Settings.Ceasefire);
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_StartingResources = prepareForDropdown(g_Settings && g_Settings.StartingResources);
-const g_VictoryDurations = prepareForDropdown(g_Settings && g_Settings.VictoryDurations);
const g_VictoryConditions = g_Settings && g_Settings.VictoryConditions;
var g_Ambient;
var g_Chat;
var g_Cheats;
var g_DeveloperOverlay;
var g_DiplomacyColors;
var g_DiplomacyDialog;
var g_GameSpeedControl;
var g_Menu;
var g_MiniMapPanel;
var g_NetworkStatusOverlay;
var g_ObjectivesDialog;
var g_OutOfSyncNetwork;
var g_OutOfSyncReplay;
var g_PanelEntityManager;
var g_PauseControl;
var g_PauseOverlay;
var g_PlayerViewControl;
var g_QuitConfirmationDefeat;
var g_QuitConfirmationReplay;
var g_RangeOverlayManager;
var g_ResearchProgress;
var g_TimeNotificationOverlay;
var g_TopPanel;
var g_TradeDialog;
/**
* Map, player and match settings set in gamesetup.
*/
const g_GameAttributes = deepfreeze(Engine.GuiInterfaceCall("GetInitAttributes"));
/**
* True if this is a multiplayer game.
*/
const g_IsNetworked = Engine.HasNetClient();
/**
* Is this user in control of game settings (i.e. is a network server, or offline player).
*/
var g_IsController = !g_IsNetworked || Engine.HasNetServer();
/**
* Whether we have finished the synchronization and
* can start showing simulation related message boxes.
*/
var g_IsNetworkedActive = false;
/**
* True if the connection to the server has been lost.
*/
var g_Disconnected = false;
/**
* True if the current user has observer capabilities.
*/
var g_IsObserver = false;
/**
* True if the current user has rejoined (or joined the game after it started).
*/
var g_HasRejoined = false;
/**
* The playerID selected in the change perspective tool.
*/
var g_ViewedPlayer = Engine.GetPlayerID();
/**
* True if the camera should focus on attacks and player commands
* and select the affected units.
*/
var g_FollowPlayer = false;
/**
* Cache the basic player data (name, civ, color).
*/
var g_Players = [];
/**
* Last time when onTick was called().
* Used for animating the main menu.
*/
var g_LastTickTime = Date.now();
/**
* Recalculate which units have their status bars shown with this frequency in milliseconds.
*/
var g_StatusBarUpdate = 200;
/**
* For restoring selection, order and filters when returning to the replay menu
*/
var g_ReplaySelectionData;
/**
* Remembers which clients are assigned to which player slots.
* The keys are GUIDs or "local" in single-player.
*/
var g_PlayerAssignments;
/**
* Whether the entire UI should be hidden (useful for promotional screenshots).
* Can be toggled with a hotkey.
*/
var g_ShowGUI = true;
/**
* Whether status bars should be shown for all of the player's units.
*/
var g_ShowAllStatusBars = false;
/**
* Cache of simulation state and template data (apart from TechnologyData, updated on every simulation update).
*/
var g_SimState;
var g_EntityStates = {};
var g_TemplateData = {};
var g_TechnologyData = {};
var g_ResourceData = new Resources();
/**
* These handlers are called each time a new turn was simulated.
* Use this as sparely as possible.
*/
var g_SimulationUpdateHandlers = new Set();
/**
* These handlers are called after the player states have been initialized.
*/
var g_PlayersInitHandlers = new Set();
/**
* These handlers are called when a player has been defeated or won the game.
*/
var g_PlayerFinishedHandlers = new Set();
/**
* These events are fired whenever the player added or removed entities from the selection.
*/
var g_EntitySelectionChangeHandlers = new Set();
/**
* These events are fired when the user has performed a hotkey assignment change.
* Currently only fired on init, but to be fired from any hotkey editor dialog.
*/
var g_HotkeyChangeHandlers = new Set();
/**
* List of additional entities to highlight.
*/
var g_ShowGuarding = false;
var g_ShowGuarded = false;
var g_AdditionalHighlight = [];
/**
* Order in which the panel entities are shown.
*/
var g_PanelEntityOrder = ["Hero", "Relic"];
/**
* Unit classes to be checked for the idle-worker-hotkey.
*/
var g_WorkerTypes = ["FemaleCitizen", "Trader", "FishingBoat", "Citizen"];
/**
* Unit classes to be checked for the military-only-selection modifier and for the idle-warrior-hotkey.
*/
var g_MilitaryTypes = ["Melee", "Ranged"];
function GetSimState()
{
if (!g_SimState)
g_SimState = deepfreeze(Engine.GuiInterfaceCall("GetSimulationState"));
return g_SimState;
}
function GetMultipleEntityStates(ents)
{
if (!ents.length)
return null;
let entityStates = Engine.GuiInterfaceCall("GetMultipleEntityStates", ents);
for (let item of entityStates)
g_EntityStates[item.entId] = item.state && deepfreeze(item.state);
return entityStates;
}
function GetEntityState(entId)
{
if (!g_EntityStates[entId])
{
let entityState = Engine.GuiInterfaceCall("GetEntityState", entId);
g_EntityStates[entId] = entityState && deepfreeze(entityState);
}
return g_EntityStates[entId];
}
/**
* Returns template data calling GetTemplateData defined in GuiInterface.js
* and deepfreezing returned object.
* @param {string} templateName - Data of this template will be returned.
* @param {number|undefined} owner - Modifications of this player will be applied to the template.
* If undefined, id of player calling this method will be used.
*/
function GetTemplateData(templateName, owner)
{
if (!(templateName in g_TemplateData))
{
let template = Engine.GuiInterfaceCall("GetTemplateData", { "templateName": templateName, "owner": owner });
translateObjectKeys(template, ["specific", "generic", "tooltip"]);
g_TemplateData[templateName] = deepfreeze(template);
}
return g_TemplateData[templateName];
}
function GetTechnologyData(technologyName, civ)
{
if (!g_TechnologyData[civ])
g_TechnologyData[civ] = {};
if (!(technologyName in g_TechnologyData[civ]))
{
let template = GetTechnologyDataHelper(TechnologyTemplates.Get(technologyName), civ, g_ResourceData);
translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]);
g_TechnologyData[civ][technologyName] = deepfreeze(template);
}
return g_TechnologyData[civ][technologyName];
}
function init(initData, hotloadData)
{
if (!g_Settings)
{
Engine.EndGame();
Engine.SwitchGuiPage("page_pregame.xml");
return;
}
// Fallback used by atlas
g_PlayerAssignments = initData ? initData.playerAssignments : { "local": { "player": 1 } };
// Fallback used by atlas and autostart games
if (g_PlayerAssignments.local && !g_PlayerAssignments.local.name)
g_PlayerAssignments.local.name = singleplayerName();
if (initData)
{
g_ReplaySelectionData = initData.replaySelectionData;
g_HasRejoined = initData.isRejoining;
if (initData.savedGUIData)
restoreSavedGameData(initData.savedGUIData);
}
+ let mapCache = new MapCache();
g_Cheats = new Cheats();
g_DiplomacyColors = new DiplomacyColors();
g_PlayerViewControl = new PlayerViewControl();
g_PlayerViewControl.registerViewedPlayerChangeHandler(g_DiplomacyColors.updateDisplayedPlayerColors.bind(g_DiplomacyColors));
g_DiplomacyColors.registerDiplomacyColorsChangeHandler(g_PlayerViewControl.rebuild.bind(g_PlayerViewControl));
g_DiplomacyColors.registerDiplomacyColorsChangeHandler(updateGUIObjects);
g_PauseControl = new PauseControl();
g_PlayerViewControl.registerPreViewedPlayerChangeHandler(removeStatusBarDisplay);
g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates);
g_Ambient = new Ambient();
g_Chat = new Chat(g_PlayerViewControl, g_Cheats);
g_DeveloperOverlay = new DeveloperOverlay(g_PlayerViewControl, g_Selection);
g_DiplomacyDialog = new DiplomacyDialog(g_PlayerViewControl, g_DiplomacyColors);
g_GameSpeedControl = new GameSpeedControl(g_PlayerViewControl);
g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat);
g_MiniMapPanel = new MiniMapPanel(g_PlayerViewControl, g_DiplomacyColors, g_WorkerTypes);
g_NetworkStatusOverlay = new NetworkStatusOverlay();
- g_ObjectivesDialog = new ObjectivesDialog(g_PlayerViewControl);
+ g_ObjectivesDialog = new ObjectivesDialog(g_PlayerViewControl, mapCache);
g_OutOfSyncNetwork = new OutOfSyncNetwork();
g_OutOfSyncReplay = new OutOfSyncReplay();
g_PanelEntityManager = new PanelEntityManager(g_PlayerViewControl, g_Selection, g_PanelEntityOrder);
g_PauseOverlay = new PauseOverlay(g_PauseControl);
g_QuitConfirmationDefeat = new QuitConfirmationDefeat();
g_QuitConfirmationReplay = new QuitConfirmationReplay();
g_RangeOverlayManager = new RangeOverlayManager(g_Selection);
g_ResearchProgress = new ResearchProgress(g_PlayerViewControl, g_Selection);
g_TradeDialog = new TradeDialog(g_PlayerViewControl);
g_TopPanel = new TopPanel(g_PlayerViewControl, g_DiplomacyDialog, g_TradeDialog, g_ObjectivesDialog, g_GameSpeedControl);
g_TimeNotificationOverlay = new TimeNotificationOverlay(g_PlayerViewControl);
initBatchTrain();
initSelectionPanels();
LoadModificationTemplates();
updatePlayerData();
initializeMusic(); // before changing the perspective
Engine.SetBoundingBoxDebugOverlay(false);
for (let handler of g_PlayersInitHandlers)
handler();
for (let handler of g_HotkeyChangeHandlers)
handler();
if (hotloadData)
{
g_Selection.selected = hotloadData.selection;
g_PlayerAssignments = hotloadData.playerAssignments;
g_Players = hotloadData.player;
}
// TODO: use event instead
onSimulationUpdate();
setTimeout(displayGamestateNotifications, 1000);
}
function registerPlayersInitHandler(handler)
{
g_PlayersInitHandlers.add(handler);
}
function registerPlayersFinishedHandler(handler)
{
g_PlayerFinishedHandlers.add(handler);
}
function registerSimulationUpdateHandler(handler)
{
g_SimulationUpdateHandlers.add(handler);
}
function unregisterSimulationUpdateHandler(handler)
{
g_SimulationUpdateHandlers.delete(handler);
}
function registerEntitySelectionChangeHandler(handler)
{
g_EntitySelectionChangeHandlers.add(handler);
}
function unregisterEntitySelectionChangeHandler(handler)
{
g_EntitySelectionChangeHandlers.delete(handler);
}
function registerHotkeyChangeHandler(handler)
{
g_HotkeyChangeHandlers.add(handler);
}
function updatePlayerData()
{
let simState = GetSimState();
if (!simState)
return;
let playerData = [];
for (let i = 0; i < simState.players.length; ++i)
{
let playerState = simState.players[i];
playerData.push({
"name": playerState.name,
"civ": playerState.civ,
"color": {
"r": playerState.color.r * 255,
"g": playerState.color.g * 255,
"b": playerState.color.b * 255,
"a": playerState.color.a * 255
},
"team": playerState.team,
"teamsLocked": playerState.teamsLocked,
"cheatsEnabled": playerState.cheatsEnabled,
"state": playerState.state,
"isAlly": playerState.isAlly,
"isMutualAlly": playerState.isMutualAlly,
"isNeutral": playerState.isNeutral,
"isEnemy": playerState.isEnemy,
"guid": undefined, // network guid for players controlled by hosts
"offline": g_Players[i] && !!g_Players[i].offline
});
}
for (let guid in g_PlayerAssignments)
{
let playerID = g_PlayerAssignments[guid].player;
if (!playerData[playerID])
continue;
playerData[playerID].guid = guid;
playerData[playerID].name = g_PlayerAssignments[guid].name;
}
g_Players = playerData;
}
/**
* Returns the entity itself except when garrisoned where it returns its garrisonHolder
*/
function getEntityOrHolder(ent)
{
let entState = GetEntityState(ent);
if (entState && !entState.position && entState.unitAI && entState.unitAI.orders.length &&
entState.unitAI.orders[0].type == "Garrison")
return getEntityOrHolder(entState.unitAI.orders[0].data.target);
return ent;
}
function initializeMusic()
{
initMusic();
if (g_ViewedPlayer != -1 && g_CivData[g_Players[g_ViewedPlayer].civ].Music)
global.music.storeTracks(g_CivData[g_Players[g_ViewedPlayer].civ].Music);
global.music.setState(global.music.states.PEACE);
}
function resetTemplates()
{
// Update GUI and clear player-dependent cache
g_TemplateData = {};
Engine.GuiInterfaceCall("ResetTemplateModified");
// TODO: do this more selectively
onSimulationUpdate();
}
/**
* Returns true if the player with that ID is in observermode.
*/
function isPlayerObserver(playerID)
{
let playerStates = GetSimState().players;
return !playerStates[playerID] || playerStates[playerID].state != "active";
}
/**
* Returns true if the current user can issue commands for that player.
*/
function controlsPlayer(playerID)
{
let playerStates = GetSimState().players;
return !!playerStates[Engine.GetPlayerID()] &&
playerStates[Engine.GetPlayerID()].controlsAll ||
Engine.GetPlayerID() == playerID &&
!!playerStates[playerID] &&
playerStates[playerID].state != "defeated";
}
/**
* Called when one or more players have won or were defeated.
*
* @param {array} - IDs of the players who have won or were defeated.
* @param {object} - a plural string stating the victory reason.
* @param {boolean} - whether these players have won or lost.
*/
function playersFinished(players, victoryString, won)
{
addChatMessage({
"type": "playerstate",
"message": victoryString,
"players": players
});
updatePlayerData();
// TODO: The other calls in this function should move too
for (let handler of g_PlayerFinishedHandlers)
handler(players, won);
if (players.indexOf(Engine.GetPlayerID()) == -1 || Engine.IsAtlasRunning())
return;
global.music.setState(
won ?
global.music.states.VICTORY :
global.music.states.DEFEAT
);
}
function resumeGame()
{
g_PauseControl.implicitResume();
}
function closeOpenDialogs()
{
g_Menu.close();
g_Chat.closePage();
g_DiplomacyDialog.close();
g_ObjectivesDialog.close();
g_TradeDialog.close();
}
function endGame()
{
// Before ending the game
let replayDirectory = Engine.GetCurrentReplayDirectory();
let simData = Engine.GuiInterfaceCall("GetReplayMetadata");
let playerID = Engine.GetPlayerID();
Engine.EndGame();
// After the replay file was closed in EndGame
// Done here to keep EndGame small
if (!g_IsReplay)
Engine.AddReplayToCache(replayDirectory);
if (g_IsController && Engine.HasXmppClient())
Engine.SendUnregisterGame();
Engine.SwitchGuiPage("page_summary.xml", {
"sim": simData,
"gui": {
"dialog": false,
"assignedPlayer": playerID,
"disconnected": g_Disconnected,
"isReplay": g_IsReplay,
"replayDirectory": !g_HasRejoined && replayDirectory,
"replaySelectionData": g_ReplaySelectionData
}
});
}
// Return some data that we'll use when hotloading this file after changes
function getHotloadData()
{
return {
"selection": g_Selection.selected,
"playerAssignments": g_PlayerAssignments,
"player": g_Players,
};
}
function getSavedGameData()
{
return {
"groups": g_Groups.groups
};
}
function restoreSavedGameData(data)
{
// Restore camera if any
if (data.camera)
Engine.SetCameraData(data.camera.PosX, data.camera.PosY, data.camera.PosZ,
data.camera.RotX, data.camera.RotY, data.camera.Zoom);
// Clear selection when loading a game
g_Selection.reset();
// Restore control groups
for (let groupNumber in data.groups)
{
g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups;
g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents;
}
updateGroups();
}
/**
* Called every frame.
*/
function onTick()
{
if (!g_Settings)
return;
let now = Date.now();
let tickLength = now - g_LastTickTime;
g_LastTickTime = now;
handleNetMessages();
updateCursorAndTooltip();
if (g_Selection.dirty)
{
g_Selection.dirty = false;
// When selection changed, get the entityStates of new entities
GetMultipleEntityStates(g_Selection.toList().filter(entId => !g_EntityStates[entId]));
for (let handler of g_EntitySelectionChangeHandlers)
handler();
updateGUIObjects();
// Display rally points for selected structures.
if (Engine.GetPlayerID() != -1)
Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() });
}
else if (g_ShowAllStatusBars && now % g_StatusBarUpdate <= tickLength)
recalculateStatusBarDisplay();
updateTimers();
Engine.GuiInterfaceCall("ClearRenamedEntities");
}
function onSimulationUpdate()
{
// Templates change depending on technologies and auras, so they have to be reloaded after such a change.
// g_TechnologyData data never changes, so it shouldn't be deleted.
g_EntityStates = {};
if (Engine.GuiInterfaceCall("IsTemplateModified"))
{
g_TemplateData = {};
Engine.GuiInterfaceCall("ResetTemplateModified");
}
g_SimState = undefined;
if (!GetSimState())
return;
GetMultipleEntityStates(g_Selection.toList());
for (let handler of g_SimulationUpdateHandlers)
handler();
// TODO: Move to handlers
updateCinemaPath();
handleNotifications();
updateGUIObjects();
}
function toggleGUI()
{
g_ShowGUI = !g_ShowGUI;
updateCinemaPath();
}
function updateCinemaPath()
{
let isPlayingCinemaPath = GetSimState().cinemaPlaying && !g_Disconnected;
Engine.GetGUIObjectByName("session").hidden = !g_ShowGUI || isPlayingCinemaPath;
Engine.Renderer_SetSilhouettesEnabled(!isPlayingCinemaPath && Engine.ConfigDB_GetValue("user", "silhouettes") == "true");
}
// TODO: Use event subscription onSimulationUpdate, onEntitySelectionChange, onPlayerViewChange, ... instead
function updateGUIObjects()
{
g_Selection.update();
if (g_ShowAllStatusBars)
recalculateStatusBarDisplay();
if (g_ShowGuarding || g_ShowGuarded)
updateAdditionalHighlight();
updateGroups();
updateSelectionDetails();
updateBuildingPlacementPreview();
if (!g_IsObserver)
{
// Update music state on basis of battle state.
let battleState = Engine.GuiInterfaceCall("GetBattleState", g_ViewedPlayer);
if (battleState)
global.music.setState(global.music.states[battleState]);
}
}
function updateGroups()
{
g_Groups.update();
// Determine the sum of the costs of a given template
let getCostSum = (ent) => {
let cost = GetTemplateData(GetEntityState(ent).template).cost;
return cost ? Object.keys(cost).map(key => cost[key]).reduce((sum, cur) => sum + cur) : 0;
};
for (let i in Engine.GetGUIObjectByName("unitGroupPanel").children)
{
Engine.GetGUIObjectByName("unitGroupLabel[" + i + "]").caption = i;
let button = Engine.GetGUIObjectByName("unitGroupButton[" + i + "]");
button.hidden = g_Groups.groups[i].getTotalCount() == 0;
button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); }; })(i);
button.ondoublepress = (function(i) { return function() { performGroup("snap", i); }; })(i);
button.onpressright = (function(i) { return function() { performGroup("breakUp", i); }; })(i);
// Choose the icon of the most common template (or the most costly if it's not unique)
if (g_Groups.groups[i].getTotalCount() > 0)
{
let icon = GetTemplateData(GetEntityState(g_Groups.groups[i].getEntsGrouped().reduce((pre, cur) => {
if (pre.ents.length == cur.ents.length)
return getCostSum(pre.ents[0]) > getCostSum(cur.ents[0]) ? pre : cur;
return pre.ents.length > cur.ents.length ? pre : cur;
}).ents[0]).template).icon;
Engine.GetGUIObjectByName("unitGroupIcon[" + i + "]").sprite =
icon ? ("stretched:session/portraits/" + icon) : "groupsIcon";
}
setPanelObjectPosition(button, i, 1);
}
}
/**
* Toggles the display of status bars for all of the player's entities.
*
* @param {Boolean} remove - Whether to hide all previously shown status bars.
*/
function recalculateStatusBarDisplay(remove = false)
{
let entities;
if (g_ShowAllStatusBars && !remove)
entities = g_ViewedPlayer == -1 ?
Engine.PickNonGaiaEntitiesOnScreen() :
Engine.PickPlayerEntitiesOnScreen(g_ViewedPlayer);
else
{
let selected = g_Selection.toList();
for (let ent in g_Selection.highlighted)
selected.push(g_Selection.highlighted[ent]);
// Remove selected entities from the 'all entities' array,
// to avoid disabling their status bars.
entities = Engine.GuiInterfaceCall(
g_ViewedPlayer == -1 ? "GetNonGaiaEntities" : "GetPlayerEntities", {
"viewedPlayer": g_ViewedPlayer
}).filter(idx => selected.indexOf(idx) == -1);
}
Engine.GuiInterfaceCall("SetStatusBars", {
"entities": entities,
"enabled": g_ShowAllStatusBars && !remove,
"showRank": Engine.ConfigDB_GetValue("user", "gui.session.rankabovestatusbar") == "true",
"showExperience": Engine.ConfigDB_GetValue("user", "gui.session.experiencestatusbar") == "true"
});
}
function removeStatusBarDisplay()
{
if (g_ShowAllStatusBars)
recalculateStatusBarDisplay(true);
}
/**
* Inverts the given configuration boolean and returns the current state.
* For example "silhouettes".
*/
function toggleConfigBool(configName)
{
let enabled = Engine.ConfigDB_GetValue("user", configName) != "true";
Engine.ConfigDB_CreateAndWriteValueToFile("user", configName, String(enabled), "config/user.cfg");
return enabled;
}
// Update the additional list of entities to be highlighted.
function updateAdditionalHighlight()
{
let entsAdd = []; // list of entities units to be highlighted
let entsRemove = [];
let highlighted = g_Selection.toList();
for (let ent in g_Selection.highlighted)
highlighted.push(g_Selection.highlighted[ent]);
if (g_ShowGuarding)
// flag the guarding entities to add in this additional highlight
for (let sel in g_Selection.selected)
{
let state = GetEntityState(g_Selection.selected[sel]);
if (!state.guard || !state.guard.entities.length)
continue;
for (let ent of state.guard.entities)
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1)
entsAdd.push(ent);
}
if (g_ShowGuarded)
// flag the guarded entities to add in this additional highlight
for (let sel in g_Selection.selected)
{
let state = GetEntityState(g_Selection.selected[sel]);
if (!state.unitAI || !state.unitAI.isGuarding)
continue;
let ent = state.unitAI.isGuarding;
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1)
entsAdd.push(ent);
}
// flag the entities to remove (from the previously added) from this additional highlight
for (let ent of g_AdditionalHighlight)
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1 && entsRemove.indexOf(ent) == -1)
entsRemove.push(ent);
_setHighlight(entsAdd, g_HighlightedAlpha, true);
_setHighlight(entsRemove, 0, false);
g_AdditionalHighlight = entsAdd;
}
Index: ps/trunk/binaries/data/mods/public/simulation/data/settings/player_defaults.json
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/data/settings/player_defaults.json (revision 23373)
+++ ps/trunk/binaries/data/mods/public/simulation/data/settings/player_defaults.json (revision 23374)
@@ -1,77 +1,77 @@
{
"PlayerData":
[
{
"Name": "Gaia",
"Civ": "gaia",
"Color": { "r": 255, "g": 255, "b": 255 },
"AI": "",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 1",
"Civ": "athen",
"Color": { "r": 21, "g": 55, "b": 149 },
- "AI": "",
+ "AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 2",
"Civ": "cart",
"Color": { "r": 150, "g": 20, "b": 20 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 3",
"Civ": "gaul",
"Color": { "r": 86 , "g": 180, "b": 31 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 4",
"Civ": "iber",
"Color": { "r": 231, "g": 200, "b": 5 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 5",
"Civ": "mace",
"Color": { "r": 50, "g": 170, "b": 170 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 6",
"Civ": "maur",
"Color": { "r": 160, "g": 80, "b": 200 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 7",
"Civ": "pers",
"Color": { "r": 220, "g": 115, "b": 16 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
},
{
"Name": "Player 8",
"Civ": "rome",
"Color": { "r": 64, "g": 64, "b": 64 },
"AI": "petra",
"AIDiff": 3,
"AIBehavior": "random"
}
]
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.xml (revision 23374)
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/MapPreview.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.xml (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.xml (revision 23374)
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ Show this message in the future.
+
+
+
+
+
Property changes on: ps/trunk/binaries/data/mods/public/gui/gamesetup/Panels/TipsPanel.xml
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 23374)
@@ -1,210 +1,30 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
-
Match Setup
-
-
-
- Loading
-
-
-
- Loading map data. Please wait…
-
-
-
-
-
-
-
- onTick();
-
-
-
- distributeSettings();
-
-
-
-
-
-
-
- Player Name
-
-
-
- Color
-
-
-
- Player Placement
-
-
-
- Civilization
-
-
-
-
-
-
- Team
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Show this message in the future.
-
-
- Engine.ConfigDB_CreateAndWriteValueToFile("user", "gui.gamesetup.enabletips", String(this.checked), "config/user.cfg");
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Show the multiplayer lobby in a dialog window.
-
-
-
-
-
-
-
-
-
-
-
-
-
- Send
- submitChatInput();
-
-
-
-
-
-
-
-
-
- Cheats enabled.
-
-
-
-
-
+
-
-
-
+
+
Index: ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js (revision 23374)
@@ -1,221 +1,223 @@
/**
* Each property of this class handles one specific map filter and is defined in external files.
*/
class GameListFilters
{
}
/**
* This class displays the list of multiplayer matches that are currently being set up or running,
* filtered and sorted depending on player selection.
*/
class GameList
{
- constructor(xmppMessages, buddyButton)
+ constructor(xmppMessages, buddyButton, mapCache)
{
+ this.mapCache = mapCache;
+
// Array of Game class instances, where the keys are ip+port strings, used for quick lookups
this.games = {};
// Array of Game class instances sorted by display order
this.gameList = [];
this.selectionChangeHandlers = new Set();
this.gamesBox = Engine.GetGUIObjectByName("gamesBox");
this.gamesBox.onSelectionChange = this.onSelectionChange.bind(this);
this.gamesBox.onSelectionColumnChange = this.onFilterChange.bind(this);
let ratingColumn = Engine.ConfigDB_GetValue("user", "lobby.columns.gamerating") == "true";
this.gamesBox.hidden_mapType = ratingColumn;
this.gamesBox.hidden_gameRating = !ratingColumn;
// Avoid repeated array construction
this.list_buddy = [];
this.list_gameName = [];
this.list_mapName = [];
this.list_mapSize = [];
this.list_mapType = [];
this.list_maxnbp = [];
this.list_gameRating = [];
this.list = [];
this.filters = [];
for (let name in GameListFilters)
this.filters.push(new GameListFilters[name](this.onFilterChange.bind(this)));
xmppMessages.registerXmppMessageHandler("game", "gamelist", this.rebuildGameList.bind(this));
xmppMessages.registerXmppMessageHandler("system", "disconnected", this.rebuildGameList.bind(this));
buddyButton.registerBuddyChangeHandler(this.onBuddyChange.bind(this));
this.rebuildGameList();
}
registerSelectionChangeHandler(handler)
{
this.selectionChangeHandlers.add(handler);
}
onFilterChange()
{
this.rebuildGameList();
}
onBuddyChange()
{
for (let name in this.games)
this.games[name].onBuddyChange();
this.rebuildGameList();
}
onSelectionChange()
{
let game = this.selectedGame();
for (let handler of this.selectionChangeHandlers)
handler(game);
}
selectedGame()
{
return this.gameList[this.gamesBox.selected] || undefined;
}
rebuildGameList()
{
Engine.ProfileStart("rebuildGameList");
Engine.ProfileStart("getGameList");
let selectedGame = this.selectedGame();
let gameListData = Engine.GetGameList();
Engine.ProfileStop();
{
Engine.ProfileStart("updateGames");
let selectedColumn = this.gamesBox.selected_column;
let newGames = {};
for (let stanza of gameListData)
{
let game = this.games[stanza.ip] || undefined;
let exists = !!game;
if (!exists)
- game = new Game();
+ game = new Game(this.mapCache);
game.update(stanza, selectedColumn);
newGames[stanza.ip] = game;
}
this.games = newGames;
Engine.ProfileStop();
}
{
Engine.ProfileStart("filterGameList");
this.gameList.length = 0;
for (let ip in this.games)
{
let game = this.games[ip];
if (this.filters.every(filter => filter.filter(game)))
this.gameList.push(game);
}
Engine.ProfileStop();
}
{
Engine.ProfileStart("sortGameList");
let sortOrder = this.gamesBox.selected_column_order;
this.gameList.sort((game1, game2) => {
if (game1.sortValue < game2.sortValue) return -sortOrder;
if (game1.sortValue > game2.sortValue) return +sortOrder;
return 0;
});
Engine.ProfileStop();
}
let selectedGameIndex = -1;
{
Engine.ProfileStart("setupGameList");
let length = this.gameList.length;
this.list_buddy.length = length;
this.list_gameName.length = length;
this.list_mapName.length = length;
this.list_mapSize.length = length;
this.list_mapType.length = length;
this.list_maxnbp.length = length;
this.list_gameRating.length = length;
this.list.length = length;
this.gameList.forEach((game, i) => {
let displayData = game.displayData;
this.list_buddy[i] = displayData.buddy;
this.list_gameName[i] = displayData.gameName;
this.list_mapName[i] = displayData.mapName;
this.list_mapSize[i] = displayData.mapSize;
this.list_mapType[i] = displayData.mapType;
this.list_maxnbp[i] = displayData.playerCount;
this.list_gameRating[i] = game.gameRating;
this.list[i] = "";
if (selectedGame && game.stanza.ip == selectedGame.stanza.ip && game.stanza.port == selectedGame.stanza.port)
selectedGameIndex = i;
});
Engine.ProfileStop();
}
{
Engine.ProfileStart("copyToGUI");
let gamesBox = this.gamesBox;
gamesBox.list_buddy = this.list_buddy;
gamesBox.list_gameName = this.list_gameName;
gamesBox.list_mapName = this.list_mapName;
gamesBox.list_mapSize = this.list_mapSize;
gamesBox.list_mapType = this.list_mapType;
gamesBox.list_maxnbp = this.list_maxnbp;
gamesBox.list_gameRating = this.list_gameRating;
// Change these last, otherwise crash
gamesBox.list = this.list;
gamesBox.list_data = this.list;
gamesBox.auto_scroll = false;
Engine.ProfileStop();
gamesBox.selected = selectedGameIndex;
}
Engine.ProfileStop();
}
/**
* Select the game where the selected player is currently playing, observing or offline.
* Selects in that order to account for players that occur in multiple games.
*/
selectGameFromPlayername(playerName)
{
if (!playerName)
return;
let foundAsObserver = false;
for (let i = 0; i < this.gameList.length; ++i)
for (let player of this.gameList[i].players)
{
if (playerName != splitRatingFromNick(player.Name).nick)
continue;
this.gamesBox.auto_scroll = true;
if (player.Team == "observer")
{
foundAsObserver = true;
this.gamesBox.selected = i;
}
else if (!player.Offline)
{
this.gamesBox.selected = i;
return;
}
if (!foundAsObserver)
this.gamesBox.selected = i;
}
}
}
Index: ps/trunk/binaries/data/mods/public/gui/session/objectives/ObjectivesDialog.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/objectives/ObjectivesDialog.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/gui/session/objectives/ObjectivesDialog.js (revision 23374)
@@ -1,59 +1,59 @@
class ObjectivesDialog
{
- constructor(playerViewControl)
+ 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();
+ Engine.GetGUIObjectByName("gameDescriptionText").caption = getGameDescription(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/maps/random/polar_sea_triggers.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/random/polar_sea_triggers.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/maps/random/polar_sea_triggers.js (revision 23374)
@@ -1,112 +1,99 @@
const debugLog = false;
var attackerTemplate = "gaia/fauna_arctic_wolf_domestic";
var minWaveSize = 1;
var maxWaveSize = 3;
var firstWaveTime = 5;
var minWaveTime = 2;
var maxWaveTime = 4;
/**
* Attackers will focus the targetCount closest units that have the targetClasses type.
*/
var targetClasses = "Organic+!Domestic";
var targetCount = 3;
-var disabledTechnologies = [
- "gather_lumbering_ironaxes",
- "gather_lumbering_sharpaxes",
- "gather_lumbering_strongeraxes"
-];
-
-Trigger.prototype.InitDisableTechnologies = function()
-{
- for (let i = 1; i < TriggerHelper.GetNumberOfPlayers(); ++i)
- QueryPlayerIDInterface(i).SetDisabledTechnologies(disabledTechnologies);
-};
-
Trigger.prototype.SpawnWolvesAndAttack = function()
{
let waveSize = Math.round(Math.random() * (maxWaveSize - minWaveSize) + minWaveSize);
let attackers = TriggerHelper.SpawnUnitsFromTriggerPoints("A", attackerTemplate, waveSize, 0);
if (debugLog)
print("Spawned " + waveSize + " " + attackerTemplate + " at " + Object.keys(attackers).length + " points\n");
let allTargets;
let players = new Array(TriggerHelper.GetNumberOfPlayers()).fill(0).map((v, i) => i + 1);
for (let spawnPoint in attackers)
{
// TriggerHelper.SpawnUnits is guaranteed to spawn
let firstAttacker = attackers[spawnPoint][0];
if (!firstAttacker)
continue;
let attackerPos = TriggerHelper.GetEntityPosition2D(firstAttacker);
if (!attackerPos)
continue;
// The returned entities are sorted by RangeManager already
let targets = Attacking.EntitiesNearPoint(attackerPos, 200, players).filter(ent => {
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses);
});
let goodTargets = targets.slice(0, targetCount);
// Look through all targets if there aren't enough nearby ones
if (goodTargets.length < targetCount)
{
if (!allTargets)
allTargets = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager).GetNonGaiaEntities().filter(ent => {
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), targetClasses);
});
let getDistance = target => {
let targetPos = TriggerHelper.GetEntityPosition2D(target);
return targetPos ? attackerPos.distanceToSquared(targetPos) : Infinity;
};
goodTargets = [];
let goodDists = [];
for (let target of allTargets)
{
let dist = getDistance(target);
let i = goodDists.findIndex(element => dist < element);
if (i != -1 || goodTargets.length < targetCount)
{
if (i == -1)
i = goodTargets.length;
goodTargets.splice(i, 0, target);
goodDists.splice(i, 0, dist);
if (goodTargets.length > targetCount)
{
goodTargets.pop();
goodDists.pop();
}
}
}
}
for (let target of goodTargets)
ProcessCommand(0, {
"type": "attack",
"entities": attackers[spawnPoint],
"target": target,
"queued": true
});
}
this.DoAfterDelay((Math.random() * (maxWaveTime - minWaveTime) + minWaveTime) * 60 * 1000, "SpawnWolvesAndAttack", {});
};
{
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
- cmpTrigger.InitDisableTechnologies();
cmpTrigger.DoAfterDelay(firstWaveTime * 60 * 1000, "SpawnWolvesAndAttack", {});
}
Index: ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js (revision 23373)
+++ ps/trunk/binaries/data/mods/public/simulation/helpers/Player.js (revision 23374)
@@ -1,361 +1,407 @@
/**
* Used to create player entities prior to reading the rest of a map,
* all other initialization must be done after loading map (terrain/entities).
* DO NOT use other components here, as they may fail unpredictably.
* settings is the object containing settings for this map.
* newPlayers if true will remove old player entities or add new ones until
* the new number of player entities is obtained
* (used when loading a map or when Atlas changes the number of players).
*/
function LoadPlayerSettings(settings, newPlayers)
{
var playerDefaults = Engine.ReadJSONFile("simulation/data/settings/player_defaults.json").PlayerData;
// Default settings
if (!settings)
settings = {};
// Add gaia to simplify iteration
// (if gaia is not already the first civ such as when called from Atlas' ActorViewer)
if (settings.PlayerData && settings.PlayerData[0] &&
(!settings.PlayerData[0].Civ || settings.PlayerData[0].Civ != "gaia"))
settings.PlayerData.unshift(null);
var playerData = settings.PlayerData;
// Disable the AIIinterface when no AI players are present
if (playerData && !playerData.some(v => v && !!v.AI))
Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface).Disable();
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var numPlayers = cmpPlayerManager.GetNumPlayers();
var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
// Remove existing players or add new ones
if (newPlayers)
{
var settingsNumPlayers = 9; // default 8 players + gaia
if (playerData)
settingsNumPlayers = playerData.length; // includes gaia (see above)
else
warn("Player.js: Setup has no player data - using defaults");
while (settingsNumPlayers > numPlayers)
{
// Add player entity to engine
var entID = Engine.AddEntity(GetPlayerTemplateName(getSetting(playerData, playerDefaults, numPlayers, "Civ")));
var cmpPlayer = Engine.QueryInterface(entID, IID_Player);
if (!cmpPlayer)
throw new Error("Player.js: Error creating player entity " + numPlayers);
cmpPlayerManager.AddPlayer(entID);
++numPlayers;
}
while (settingsNumPlayers < numPlayers)
{
cmpPlayerManager.RemoveLastPlayer();
--numPlayers;
}
}
// Even when no new player, we must check the template compatibility as player template may be civ dependent
for (var i = 0; i < numPlayers; ++i)
{
var template = GetPlayerTemplateName(getSetting(playerData, playerDefaults, i, "Civ"));
var entID = cmpPlayerManager.GetPlayerByID(i);
if (cmpTemplateManager.GetCurrentTemplateName(entID) === template)
continue;
// We need to recreate this player to have the right template
entID = Engine.AddEntity(template);
cmpPlayerManager.ReplacePlayer(i, entID);
}
// Initialize the player data
for (var i = 0; i < numPlayers; ++i)
{
let cmpPlayer = QueryPlayerIDInterface(i);
cmpPlayer.SetName(getSetting(playerData, playerDefaults, i, "Name"));
cmpPlayer.SetCiv(getSetting(playerData, playerDefaults, i, "Civ"));
var color = getSetting(playerData, playerDefaults, i, "Color");
cmpPlayer.SetColor(color.r, color.g, color.b);
// Special case for gaia
if (i == 0)
{
// Gaia should be its own ally.
cmpPlayer.SetAlly(0);
// Gaia is everyone's enemy
for (var j = 1; j < numPlayers; ++j)
cmpPlayer.SetEnemy(j);
continue;
}
- if (getSetting(playerData, playerDefaults, i, "PopulationLimit") !== undefined)
- cmpPlayer.SetMaxPopulation(getSetting(playerData, playerDefaults, i, "PopulationLimit"));
+ // PopulationLimit
+ {
+ let maxPopulation =
+ settings.PlayerData[i].PopulationLimit !== undefined ?
+ settings.PlayerData[i].PopulationLimit :
+ settings.PopulationCap !== undefined ?
+ settings.PopulationCap :
+ playerDefaults[i].PopulationLimit !== undefined ?
+ playerDefaults[i].PopulationLimit :
+ undefined;
+
+ if (maxPopulation !== undefined)
+ cmpPlayer.SetMaxPopulation(maxPopulation);
+ }
+
+ // StartingResources
+ if (settings.PlayerData[i].Resources !== undefined)
+ cmpPlayer.SetResourceCounts(settings.PlayerData[i].Resources);
+ else if (settings.StartingResources)
+ {
+ let resourceCounts = cmpPlayer.GetResourceCounts();
+ let newResourceCounts = {};
+ for (let resouces in resourceCounts)
+ newResourceCounts[resouces] = settings.StartingResources;
+ cmpPlayer.SetResourceCounts(newResourceCounts);
+ }
+ else if (playerDefaults[i].Resources !== undefined)
+ cmpPlayer.SetResourceCounts(playerDefaults[i].Resources);
- if (getSetting(playerData, playerDefaults, i, "Resources") !== undefined)
- cmpPlayer.SetResourceCounts(getSetting(playerData, playerDefaults, i, "Resources"));
+ // StartingTechnologies
+ {
+ let startingTechnologies =
+ settings.PlayerData[i].StartingTechnologies ||
+ settings.StartingTechnologies ||
+ playerDefaults[i].StartingTechnologies ||
+ [];
- if (getSetting(playerData, playerDefaults, i, "StartingTechnologies") !== undefined)
- cmpPlayer.SetStartingTechnologies(getSetting(playerData, playerDefaults, i, "StartingTechnologies"));
+ if (startingTechnologies.length)
+ cmpPlayer.SetStartingTechnologies(startingTechnologies);
+ }
- if (getSetting(playerData, playerDefaults, i, "DisabledTechnologies") !== undefined)
- cmpPlayer.SetDisabledTechnologies(getSetting(playerData, playerDefaults, i, "DisabledTechnologies"));
-
- let disabledTemplates = [];
- if (settings.DisabledTemplates !== undefined)
- disabledTemplates = settings.DisabledTemplates;
- if (getSetting(playerData, playerDefaults, i, "DisabledTemplates") !== undefined)
- disabledTemplates = disabledTemplates.concat(getSetting(playerData, playerDefaults, i, "DisabledTemplates"));
- if (disabledTemplates.length)
- cmpPlayer.SetDisabledTemplates(disabledTemplates);
+ // DisabledTechnologies
+ {
+ let disabledTechnologies =
+ settings.PlayerData[i].DisabledTechnologies ||
+ settings.DisabledTechnologies ||
+ playerDefaults[i].DisabledTechnologies ||
+ [];
+
+ if (disabledTechnologies.length)
+ cmpPlayer.SetDisabledTechnologies(disabledTechnologies);
+ }
+
+ // DisabledTemplates
+ {
+ let disabledTemplates =
+ settings.PlayerData[i].DisabledTemplates ||
+ settings.DisabledTemplates ||
+ playerDefaults[i].DisabledTemplates ||
+ [];
+
+ if (disabledTemplates.length)
+ cmpPlayer.SetDisabledTemplates(disabledTemplates);
+ }
+ // DisableSpies
if (settings.DisableSpies)
{
cmpPlayer.AddDisabledTechnology("unlock_spies");
cmpPlayer.AddDisabledTemplate("special/spy");
}
// If diplomacy explicitly defined, use that; otherwise use teams
if (getSetting(playerData, playerDefaults, i, "Diplomacy") !== undefined)
cmpPlayer.SetDiplomacy(getSetting(playerData, playerDefaults, i, "Diplomacy"));
else
{
// Init diplomacy
var myTeam = getSetting(playerData, playerDefaults, i, "Team");
// Set all but self as enemies as SetTeam takes care of allies
for (var j = 0; j < numPlayers; ++j)
{
if (i == j)
cmpPlayer.SetAlly(j);
else
cmpPlayer.SetEnemy(j);
}
cmpPlayer.SetTeam(myTeam === undefined ? -1 : myTeam);
}
cmpPlayer.SetFormations(
getSetting(playerData, playerDefaults, i, "Formations") ||
Engine.ReadJSONFile("simulation/data/civs/" + cmpPlayer.GetCiv() + ".json").Formations);
var startCam = getSetting(playerData, playerDefaults, i, "StartingCamera");
if (startCam !== undefined)
cmpPlayer.SetStartingCamera(startCam.Position, startCam.Rotation);
}
// NOTE: We need to do the team locking here, as otherwise
// SetTeam can't ally the players.
if (settings.LockTeams)
for (let i = 0; i < numPlayers; ++i)
QueryPlayerIDInterface(i).SetLockTeams(true);
}
// Get a setting if it exists or return default
function getSetting(settings, defaults, idx, property)
{
if (settings && settings[idx] && (property in settings[idx]))
return settings[idx][property];
// Use defaults
if (defaults && defaults[idx] && (property in defaults[idx]))
return defaults[idx][property];
return undefined;
}
function GetPlayerTemplateName(civ)
{
let path = "special/player/player";
if (Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager).TemplateExists(path + "_" + civ))
return path + "_" + civ;
return path;
}
/**
* @param id An entity's ID
* @returns The entity ID of the owner player (not his player ID) or ent if ent is a player entity.
*/
function QueryOwnerEntityID(ent)
{
let cmpPlayer = Engine.QueryInterface(ent, IID_Player);
if (cmpPlayer)
return ent;
let cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
if (!cmpOwnership)
return null;
let owner = cmpOwnership.GetOwner();
if (owner == INVALID_PLAYER)
return null;
let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (!cmpPlayerManager)
return null;
return cmpPlayerManager.GetPlayerByID(owner);
}
/**
* Similar to Engine.QueryInterface but applies to the player entity
* that owns the given entity.
* iid is typically IID_Player.
*/
function QueryOwnerInterface(ent, iid = IID_Player)
{
var cmpOwnership = Engine.QueryInterface(ent, IID_Ownership);
if (!cmpOwnership)
return null;
var owner = cmpOwnership.GetOwner();
if (owner == INVALID_PLAYER)
return null;
return QueryPlayerIDInterface(owner, iid);
}
/**
* Similar to Engine.QueryInterface but applies to the player entity
* with the given ID number.
* iid is typically IID_Player.
*/
function QueryPlayerIDInterface(id, iid = IID_Player)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var playerEnt = cmpPlayerManager.GetPlayerByID(id);
if (!playerEnt)
return null;
return Engine.QueryInterface(playerEnt, iid);
}
/**
* Similar to Engine.QueryInterface but first checks if the entity
* mirages the interface.
*/
function QueryMiragedInterface(ent, iid)
{
var cmp = Engine.QueryInterface(ent, IID_Mirage);
if (cmp && !cmp.Mirages(iid))
return null;
else if (!cmp)
cmp = Engine.QueryInterface(ent, iid);
return cmp;
}
/**
* Similar to Engine.QueryInterface, but checks for all interfaces
* implementing a builder list (currently Foundation and Repairable)
* TODO Foundation and Repairable could both implement a BuilderList component
*/
function QueryBuilderListInterface(ent)
{
return Engine.QueryInterface(ent, IID_Foundation) || Engine.QueryInterface(ent, IID_Repairable);
}
/**
* Returns true if the entity 'target' is owned by an ally of
* the owner of 'entity'.
*/
function IsOwnedByAllyOfEntity(entity, target)
{
return IsOwnedByEntityHelper(entity, target, "IsAlly");
}
function IsOwnedByMutualAllyOfEntity(entity, target)
{
return IsOwnedByEntityHelper(entity, target, "IsMutualAlly");
}
function IsOwnedByEntityHelper(entity, target, check)
{
// Figure out which player controls us
let owner = 0;
let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
if (cmpOwnership)
owner = cmpOwnership.GetOwner();
// Figure out which player controls the target entity
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(owner);
return cmpPlayer && cmpPlayer[check](targetOwner);
}
/**
* Returns true if the entity 'target' is owned by player
*/
function IsOwnedByPlayer(player, target)
{
var cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
return cmpOwnershipTarget && player == cmpOwnershipTarget.GetOwner();
}
function IsOwnedByGaia(target)
{
return IsOwnedByPlayer(0, target);
}
/**
* Returns true if the entity 'target' is owned by an ally of player
*/
function IsOwnedByAllyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsAlly");
}
function IsOwnedByMutualAllyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsMutualAlly");
}
-function IsOwnedByNeutralOfPlayer(player,target)
+function IsOwnedByNeutralOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsNeutral");
}
function IsOwnedByEnemyOfPlayer(player, target)
{
return IsOwnedByHelper(player, target, "IsEnemy");
}
function IsOwnedByHelper(player, target, check)
{
let targetOwner = 0;
let cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
if (cmpOwnershipTarget)
targetOwner = cmpOwnershipTarget.GetOwner();
let cmpPlayer = QueryPlayerIDInterface(player);
return cmpPlayer && cmpPlayer[check](targetOwner);
}
Engine.RegisterGlobal("LoadPlayerSettings", LoadPlayerSettings);
Engine.RegisterGlobal("QueryOwnerEntityID", QueryOwnerEntityID);
Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
Engine.RegisterGlobal("QueryPlayerIDInterface", QueryPlayerIDInterface);
Engine.RegisterGlobal("QueryMiragedInterface", QueryMiragedInterface);
Engine.RegisterGlobal("QueryBuilderListInterface", QueryBuilderListInterface);
Engine.RegisterGlobal("IsOwnedByAllyOfEntity", IsOwnedByAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfEntity", IsOwnedByMutualAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByPlayer", IsOwnedByPlayer);
Engine.RegisterGlobal("IsOwnedByGaia", IsOwnedByGaia);
Engine.RegisterGlobal("IsOwnedByAllyOfPlayer", IsOwnedByAllyOfPlayer);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfPlayer", IsOwnedByMutualAllyOfPlayer);
Engine.RegisterGlobal("IsOwnedByNeutralOfPlayer", IsOwnedByNeutralOfPlayer);
Engine.RegisterGlobal("IsOwnedByEnemyOfPlayer", IsOwnedByEnemyOfPlayer);