Index: ps/trunk/binaries/data/mods/public/gui/session/menu.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/menu.js (revision 15105) +++ ps/trunk/binaries/data/mods/public/gui/session/menu.js (revision 15106) @@ -1,699 +1,699 @@ const PAUSE = translate("Pause"); const RESUME = translate("Resume"); /* * MENU POSITION CONSTANTS */ // Menu / panel border size const MARGIN = 4; // Includes the main menu button const NUM_BUTTONS = 8; // Regular menu buttons const BUTTON_HEIGHT = 32; // The position where the bottom of the menu will end up (currently 228) const END_MENU_POSITION = (BUTTON_HEIGHT * NUM_BUTTONS) + MARGIN; // Menu starting position: bottom const MENU_BOTTOM = 0; // Menu starting position: top const MENU_TOP = MENU_BOTTOM - END_MENU_POSITION; // Menu starting position: overall const INITIAL_MENU_POSITION = "100%-164 " + MENU_TOP + " 100% " + MENU_BOTTOM; // Number of pixels per millisecond to move const MENU_SPEED = 1.2; // Trade menu: available resources and step for probability changes const RESOURCES = ["food", "wood", "stone", "metal"]; const STEP = 5; var isMenuOpen = false; var menu; var isDiplomacyOpen = false; var isTradeOpen = false; // Redefined every time someone makes a tribute (so we can save some data in a closure). Called in input.js handleInputBeforeGui. var flushTributing = function() {}; // Ignore size defined in XML and set the actual menu size here function initMenuPosition() { menu = Engine.GetGUIObjectByName("menu"); menu.size = INITIAL_MENU_POSITION; } // ============================================================================= // Overall Menu // ============================================================================= // // Slide menu function updateMenuPosition(dt) { if (isMenuOpen) { var maxOffset = END_MENU_POSITION - menu.size.bottom; if (maxOffset > 0) { var offset = Math.min(MENU_SPEED * dt, maxOffset); var size = menu.size; size.top += offset; size.bottom += offset; menu.size = size; } } else { var maxOffset = menu.size.top - MENU_TOP; if (maxOffset > 0) { var offset = Math.min(MENU_SPEED * dt, maxOffset); var size = menu.size; size.top -= offset; size.bottom -= offset; menu.size = size; } } } // Opens the menu by revealing the screen which contains the menu function openMenu() { isMenuOpen = true; } // Closes the menu and resets position function closeMenu() { isMenuOpen = false; } function toggleMenu() { if (isMenuOpen == true) closeMenu(); else openMenu(); } // Menu buttons // ============================================================================= function settingsMenuButton() { closeMenu(); closeOpenDialogs(); openSettings(); } function chatMenuButton() { closeMenu(); closeOpenDialogs(); openChat(); } function diplomacyMenuButton() { closeMenu(); closeOpenDialogs(); openDiplomacy(); } function pauseMenuButton() { togglePause(); } function resignMenuButton() { closeMenu(); closeOpenDialogs(); pauseGame(); var btCaptions = [translate("No"), translate("Yes")]; var btCode = [resumeGame, resignGame]; messageBox(400, 200, translate("Are you sure you want to resign?"), translate("Confirmation"), 0, btCaptions, btCode); } function exitMenuButton() { closeMenu(); closeOpenDialogs(); pauseGame(); if (g_IsNetworked && g_IsController) { - var btCode = [leaveGame, resumeGame]; + var btCode = [resumeGame, leaveGame]; var message = translate("Are you sure you want to quit? Leaving will disconnect all other players."); } else if (g_IsNetworked && !g_GameEnded && !g_IsObserver) { - var btCode = [networkReturnQuestion, resumeGame]; + var btCode = [resumeGame, networkReturnQuestion]; var message = translate("Are you sure you want to quit?"); } else { var btCode = [resumeGame, leaveGame]; var message = translate("Are you sure you want to quit?"); } messageBox(400, 200, message, translate("Confirmation"), 0, [translate("No"), translate("Yes")], btCode); } function networkReturnQuestion() { var btCaptions = [translate("I resign"), translate("I will return")]; - var btCode = [leaveGame, leaveGame]; + var btCode = [resignGame, leaveGame]; var btArgs = [false, true]; messageBox(400, 200, translate("Do you want to resign or will you return soon?"), translate("Confirmation"), 0, btCaptions, btCode, btArgs); } function openDeleteDialog(selection) { closeMenu(); closeOpenDialogs(); var deleteSelectedEntities = function (selectionArg) { Engine.PostNetworkCommand({"type": "delete-entities", "entities": selectionArg}); }; var btCaptions = [translate("No"), translate("Yes")]; var btCode = [resumeGame, deleteSelectedEntities]; messageBox(400, 200, translate("Destroy everything currently selected?"), translate("Delete"), 0, btCaptions, btCode, [null, selection]); } // Menu functions // ============================================================================= function openSave() { closeMenu(); closeOpenDialogs(); pauseGame(); var savedGameData = getSavedGameData(); Engine.PushGuiPage("page_savegame.xml", {"savedGameData":savedGameData, "callback":"resumeGame"}); } function openSettings() { pauseGame(); Engine.PushGuiPage("page_options.xml", {"callback":"resumeGame"}); } function openChat() { Engine.GetGUIObjectByName("chatInput").focus(); // Grant focus to the input area Engine.GetGUIObjectByName("chatDialogPanel").hidden = false; } function closeChat() { Engine.GetGUIObjectByName("chatInput").caption = ""; // Clear chat input Engine.GetGUIObjectByName("chatInput").blur(); // Remove focus Engine.GetGUIObjectByName("chatDialogPanel").hidden = true; } function toggleChatWindow(teamChat) { var chatWindow = Engine.GetGUIObjectByName("chatDialogPanel"); var chatInput = Engine.GetGUIObjectByName("chatInput"); if (chatWindow.hidden) chatInput.focus(); // Grant focus to the input area else { if (chatInput.caption.length) { submitChatInput(); return; } chatInput.caption = ""; // Clear chat input } Engine.GetGUIObjectByName("toggleTeamChat").checked = teamChat; chatWindow.hidden = !chatWindow.hidden; } function setDiplomacy(data) { Engine.PostNetworkCommand({"type": "diplomacy", "to": data.to, "player": data.player}); } function tributeResource(data) { Engine.PostNetworkCommand({"type": "tribute", "player": data.player, "amounts": data.amounts}); } function openDiplomacy() { if (isTradeOpen) closeTrade(); isDiplomacyOpen = true; var we = Engine.GetPlayerID(); var players = getPlayerData(g_PlayerAssignments); // Get offset for one line var onesize = Engine.GetGUIObjectByName("diplomacyPlayer[0]").size; var rowsize = onesize.bottom - onesize.top; // We don't include gaia for (var i = 1; i < players.length; i++) { // Apply offset var row = Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]"); var size = row.size; size.top = rowsize*(i-1); size.bottom = rowsize*i; row.size = size; // Set background colour var playerColor = players[i].color.r+" "+players[i].color.g+" "+players[i].color.b; row.sprite = "colour: "+playerColor + " 32"; Engine.GetGUIObjectByName("diplomacyPlayerName["+(i-1)+"]").caption = "[color=\"" + playerColor + "\"]" + players[i].name + "[/color]"; Engine.GetGUIObjectByName("diplomacyPlayerCiv["+(i-1)+"]").caption = g_CivData[players[i].civ].Name; Engine.GetGUIObjectByName("diplomacyPlayerTeam["+(i-1)+"]").caption = (players[i].team < 0) ? translateWithContext("team", "None") : players[i].team+1; if (i != we) Engine.GetGUIObjectByName("diplomacyPlayerTheirs["+(i-1)+"]").caption = (players[i].isAlly[we] ? translate("Ally") : (players[i].isNeutral[we] ? translate("Neutral") : translate("Enemy"))); // Don't display the options for ourself, or if we or the other player aren't active anymore if (i == we || players[we].state != "active" || players[i].state != "active") { // Hide the unused/unselectable options for each (var a in ["TributeFood", "TributeWood", "TributeStone", "TributeMetal", "Ally", "Neutral", "Enemy"]) Engine.GetGUIObjectByName("diplomacyPlayer"+a+"["+(i-1)+"]").hidden = true; continue; } // Tribute for each (var resource in ["food", "wood", "stone", "metal"]) { var button = Engine.GetGUIObjectByName("diplomacyPlayerTribute"+toTitleCase(resource)+"["+(i-1)+"]"); button.onpress = (function(player, resource, button){ // Implement something like how unit batch training works. Shift+click to send 500, shift+click+click to send 1000, etc. // Also see input.js (searching for "INPUT_MASSTRIBUTING" should get all the relevant parts). var multiplier = 1; return function() { var isBatchTrainPressed = Engine.HotkeyIsPressed("session.masstribute"); if (isBatchTrainPressed) { inputState = INPUT_MASSTRIBUTING; multiplier += multiplier == 1 ? 4 : 5; } var amounts = { "food": (resource == "food" ? 100 : 0) * multiplier, "wood": (resource == "wood" ? 100 : 0) * multiplier, "stone": (resource == "stone" ? 100 : 0) * multiplier, "metal": (resource == "metal" ? 100 : 0) * multiplier, }; button.tooltip = formatTributeTooltip(players[player], resource, amounts[resource]); // This is in a closure so that we have access to `player`, `amounts`, and `multiplier` without some // evil global variable hackery. flushTributing = function() { tributeResource({"player": player, "amounts": amounts}); multiplier = 1; button.tooltip = formatTributeTooltip(players[player], resource, 100); }; if (!isBatchTrainPressed) flushTributing(); }; })(i, resource, button); button.hidden = false; button.tooltip = formatTributeTooltip(players[i], resource, 100); } // Skip our own teams on teams locked if (players[we].teamsLocked && players[we].team != -1 && players[we].team == players[i].team) continue; // Diplomacy settings // Set up the buttons for each (var setting in ["ally", "neutral", "enemy"]) { var button = Engine.GetGUIObjectByName("diplomacyPlayer"+toTitleCase(setting)+"["+(i-1)+"]"); if (setting == "ally") { if (players[we].isAlly[i]) button.caption = translate("x"); else button.caption = ""; } else if (setting == "neutral") { if (players[we].isNeutral[i]) button.caption = translate("x"); else button.caption = ""; } else // "enemy" { if (players[we].isEnemy[i]) button.caption = translate("x"); else button.caption = ""; } button.onpress = (function(e){ return function() { setDiplomacy(e) } })({"player": i, "to": setting}); button.hidden = false; } } Engine.GetGUIObjectByName("diplomacyDialogPanel").hidden = false; } function closeDiplomacy() { isDiplomacyOpen = false; Engine.GetGUIObjectByName("diplomacyDialogPanel").hidden = true; } function toggleDiplomacy() { if (isDiplomacyOpen) closeDiplomacy(); else openDiplomacy(); } function openTrade() { if (isDiplomacyOpen) closeDiplomacy(); isTradeOpen = true; var updateButtons = function() { for (var res in button) { button[res].label.caption = proba[res] + "%"; if (res == selec) { button[res].res.enabled = false; button[res].sel.hidden = false; button[res].up.hidden = true; button[res].dn.hidden = true; } else { button[res].res.enabled = true; button[res].sel.hidden = true; button[res].up.hidden = (proba[res] == 100 || proba[selec] == 0); button[res].dn.hidden = (proba[res] == 0 || proba[selec] == 100); } } } var proba = Engine.GuiInterfaceCall("GetTradingGoods"); var button = {}; var selec = RESOURCES[0]; for (var i = 0; i < RESOURCES.length; ++i) { var buttonResource = Engine.GetGUIObjectByName("tradeResource["+i+"]"); if (i > 0) { var size = Engine.GetGUIObjectByName("tradeResource["+(i-1)+"]").size; var width = size.right - size.left; size.left += width; size.right += width; Engine.GetGUIObjectByName("tradeResource["+i+"]").size = size; } var resource = RESOURCES[i]; proba[resource] = (proba[resource] ? proba[resource] : 0); var buttonResource = Engine.GetGUIObjectByName("tradeResourceButton["+i+"]"); var icon = Engine.GetGUIObjectByName("tradeResourceIcon["+i+"]"); icon.sprite = "stretched:session/icons/resources/" + resource + ".png"; var label = Engine.GetGUIObjectByName("tradeResourceText["+i+"]"); var buttonUp = Engine.GetGUIObjectByName("tradeArrowUp["+i+"]"); var buttonDn = Engine.GetGUIObjectByName("tradeArrowDn["+i+"]"); var iconSel = Engine.GetGUIObjectByName("tradeResourceSelection["+i+"]"); button[resource] = { "res": buttonResource, "up": buttonUp, "dn": buttonDn, "label": label, "sel": iconSel }; buttonResource.onpress = (function(resource){ return function() { if (selec == resource) return; selec = resource; updateButtons(); } })(resource); buttonUp.onpress = (function(resource){ return function() { proba[resource] += Math.min(STEP, proba[selec]); proba[selec] -= Math.min(STEP, proba[selec]); Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); updateButtons(); } })(resource); buttonDn.onpress = (function(resource){ return function() { proba[selec] += Math.min(STEP, proba[resource]); proba[resource] -= Math.min(STEP, proba[resource]); Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); updateButtons(); } })(resource); } updateButtons(); var traderNumber = Engine.GuiInterfaceCall("GetTraderNumber"); var caption = ""; if (traderNumber.landTrader.total == 0) caption = translate("There are no land traders."); else { var inactive = traderNumber.landTrader.total - traderNumber.landTrader.trading - traderNumber.landTrader.garrisoned; var inactiveString = ""; if (inactive > 0) inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfLandTraders)s inactive", "%(numberOfLandTraders)s inactive", inactive), { numberOfLandTraders: inactive }) + "[/color]"; if (traderNumber.landTrader.trading > 0) { var openingTradingString = sprintf(translatePlural("There is %(numberTrading)s land trader trading", "There are %(numberTrading)s land traders trading", traderNumber.landTrader.trading), { numberTrading: traderNumber.landTrader.trading }); if (traderNumber.landTrader.garrisoned > 0) { var garrisonedString = sprintf(translatePlural("%(numberGarrisoned)s garrisoned on a trading merchant ship", "%(numberGarrisoned)s garrisoned on a trading merchant ship", traderNumber.landTrader.garrisoned), { numberGarrisoned: traderNumber.landTrader.garrisoned }); if (inactive > 0) { caption = sprintf(translate("%(openingTradingString)s, %(garrisonedString)s, and %(inactiveString)s."), { openingTradingString: openingTradingString, garrisonedString: garrisonedString, inactiveString: inactiveString }); } else { caption = sprintf(translate("%(openingTradingString)s, and %(garrisonedString)s."), { openingTradingString: openingTradingString, garrisonedString: garrisonedString }); } } else { if (inactive > 0) { caption = sprintf(translate("%(openingTradingString)s, and %(inactiveString)s."), { openingTradingString: openingTradingString, inactiveString: inactiveString }); } else { caption = sprintf(translate("%(openingTradingString)s."), { openingTradingString: openingTradingString, }); } } } else { if (traderNumber.landTrader.garrisoned > 0) { var openingGarrisonedString = sprintf(translatePlural("There is %(numberGarrisoned)s land trader garrisoned on a trading merchant ship", "There are %(numberGarrisoned)s land traders garrisoned on a trading merchant ship", traderNumber.landTrader.garrisoned), { numberGarrisoned: traderNumber.landTrader.garrisoned }); if (inactive > 0) { caption = sprintf(translate("%(openingGarrisonedString)s, and %(inactiveString)s."), { openingGarrisonedString: openingGarrisonedString, inactiveString: inactiveString }); } else { caption = sprintf(translate("%(openingGarrisonedString)s."), { openingGarrisonedString: openingGarrisonedString }); } } else { if (inactive > 0) { inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfLandTraders)s land trader inactive", "%(numberOfLandTraders)s land traders inactive", inactive), { numberOfLandTraders: inactive }) + "[/color]"; caption = sprintf(translatePlural("There is %(inactiveString)s.", "There are %(inactiveString)s.", inactive), { inactiveString: inactiveString }); } // The “else” here is already handled by “if (traderNumber.landTrader.total == 0)” above. } } } Engine.GetGUIObjectByName("landTraders").caption = caption; caption = ""; if (traderNumber.shipTrader.total == 0) caption = translate("There are no merchant ships."); else { var inactive = traderNumber.shipTrader.total - traderNumber.shipTrader.trading; var inactiveString = ""; if (inactive > 0) inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfShipTraders)s inactive", "%(numberOfShipTraders)s inactive", inactive), { numberOfShipTraders: inactive }) + "[/color]"; if (traderNumber.shipTrader.trading > 0) { var openingTradingString = sprintf(translatePlural("There is %(numberTrading)s merchant ship trading", "There are %(numberTrading)s merchant ships trading", traderNumber.shipTrader.trading), { numberTrading: traderNumber.shipTrader.trading }); if (inactive > 0) { caption = sprintf(translate("%(openingTradingString)s, and %(inactiveString)s."), { openingTradingString: openingTradingString, inactiveString: inactiveString }); } else { caption = sprintf(translate("%(openingTradingString)s."), { openingTradingString: openingTradingString, }); } } else { if (inactive > 0) { inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfShipTraders)s merchant ship inactive", "%(numberOfShipTraders)s merchant ships inactive", inactive), { numberOfShipTraders: inactive }) + "[/color]"; caption = sprintf(translatePlural("There is %(inactiveString)s.", "There are %(inactiveString)s.", inactive), { inactiveString: inactiveString }); } // The “else” here is already handled by “if (traderNumber.shipTrader.total == 0)” above. } } Engine.GetGUIObjectByName("shipTraders").caption = caption; Engine.GetGUIObjectByName("tradeDialogPanel").hidden = false; } function closeTrade() { isTradeOpen = false; Engine.GetGUIObjectByName("tradeDialogPanel").hidden = true; } function toggleTrade() { if (isTradeOpen) closeTrade(); else openTrade(); } function toggleGameSpeed() { var gameSpeed = Engine.GetGUIObjectByName("gameSpeed"); gameSpeed.hidden = !gameSpeed.hidden; } /** * Pause the game in single player mode. */ function pauseGame() { if (g_IsNetworked) return; Engine.GetGUIObjectByName("pauseButtonText").caption = RESUME; Engine.GetGUIObjectByName("pauseOverlay").hidden = false; Engine.SetPaused(true); } function resumeGame() { Engine.GetGUIObjectByName("pauseButtonText").caption = PAUSE; Engine.GetGUIObjectByName("pauseOverlay").hidden = true; Engine.SetPaused(false); } function togglePause() { closeMenu(); closeOpenDialogs(); var pauseOverlay = Engine.GetGUIObjectByName("pauseOverlay"); if (pauseOverlay.hidden) { Engine.GetGUIObjectByName("pauseButtonText").caption = RESUME; Engine.SetPaused(true); } else { Engine.SetPaused(false); Engine.GetGUIObjectByName("pauseButtonText").caption = PAUSE; } pauseOverlay.hidden = !pauseOverlay.hidden; } function openManual() { closeMenu(); closeOpenDialogs(); pauseGame(); Engine.PushGuiPage("page_manual.xml", {"page": "manual/intro", "title":translate("Manual"), "url":"http://trac.wildfiregames.com/wiki/0adManual", "callback": "resumeGame"}); } function toggleDeveloperOverlay() { if (Engine.HasXmppClient() && Engine.IsRankedGame()) return; var devCommands = Engine.GetGUIObjectByName("devCommands"); if (devCommands.hidden) submitChatDirectly(translate("The Developer Overlay was opened.")); else submitChatDirectly(translate("The Developer Overlay was closed.")); // Update the options dialog devCommands.hidden = !devCommands.hidden; // Save the changes Engine.ConfigDB_CreateValue("user", "developeroverlay.enable", String(!devCommands.hidden) ); } function closeOpenDialogs() { closeMenu(); closeChat(); closeDiplomacy(); closeTrade(); } function formatTributeTooltip(player, resource, amount) { var playerColor = player.color.r + " " + player.color.g + " " + player.color.b; return sprintf(translate("Tribute %(resourceAmount)s %(resourceType)s to %(playerName)s. Shift-click to tribute %(greaterAmount)s."), { resourceAmount: amount, resourceType: getLocalizedResourceName(resource, "withinSentence"), playerName: "[color=\"" + playerColor + "\"]" + player.name + "[/color]", greaterAmount: (amount < 500 ? 500 : amount + 500) }); } Index: ps/trunk/binaries/data/mods/public/gui/session/session.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 15105) +++ ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 15106) @@ -1,1103 +1,1103 @@ // Network Mode var g_IsNetworked = false; // Is this user in control of game settings (i.e. is a network server, or offline player) var g_IsController; // Match ID for tracking var g_MatchID; // Is this user an observer? var g_IsObserver = false; // Cache the basic player data (name, civ, color) var g_Players = []; // Cache the useful civ data var g_CivData = {}; var g_GameSpeeds = {}; var g_CurrentSpeed; var g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1 } }; // Cache dev-mode settings that are frequently or widely used var g_DevSettings = { controlAll: false }; // Whether status bars should be shown for all of the player's units. var g_ShowAllStatusBars = false; // Indicate when one of the current player's training queues is blocked // (this is used to support population counter blinking) var g_IsTrainingBlocked = false; // Cache simulation state (updated on every simulation update) var g_SimState; // Cache EntityStates var g_EntityStates = {}; // {id:entState} // Whether the player has lost/won and reached the end of their game var g_GameEnded = false; var g_Disconnected = false; // Lost connection to server // Holds player states from the last tick var g_CachedLastStates = ""; // Colors to flash when pop limit reached const DEFAULT_POPULATION_COLOR = "white"; const POPULATION_ALERT_COLOR = "orange"; // List of additional entities to highlight var g_ShowGuarding = false; var g_ShowGuarded = false; var g_AdditionalHighlight = []; // for saving the hitpoins of the hero (is there a better way to do that?) // Should be possible with AttackDetection but might be an overkill because it would have to loop // always through the list of all ongoing attacks... var g_previousHeroHitPoints = undefined; function GetSimState() { if (!g_SimState) g_SimState = Engine.GuiInterfaceCall("GetSimulationState"); return g_SimState; } function GetEntityState(entId) { if (!(entId in g_EntityStates)) { var entState = Engine.GuiInterfaceCall("GetEntityState", entId); if (entState) entState.extended = false; g_EntityStates[entId] = entState; } return g_EntityStates[entId]; } function GetExtendedEntityState(entId) { if (entId in g_EntityStates) var entState = g_EntityStates[entId]; else var entState = Engine.GuiInterfaceCall("GetEntityState", entId); if (!entState || entState.extended) return entState; var extension = Engine.GuiInterfaceCall("GetExtendedEntityState", entId); for (var prop in extension) entState[prop] = extension[prop]; entState.extended = true; g_EntityStates[entId] = entState; return entState; } // Cache TemplateData var g_TemplateData = {}; // {id:template} var g_TemplateDataWithoutLocalization = {}; function GetTemplateData(templateName) { if (!(templateName in g_TemplateData)) { var template = Engine.GuiInterfaceCall("GetTemplateData", templateName); translateObjectKeys(template, ["specific", "generic", "tooltip"]); g_TemplateData[templateName] = template; } return g_TemplateData[templateName]; } function GetTemplateDataWithoutLocalization(templateName) { if (!(templateName in g_TemplateDataWithoutLocalization)) { var template = Engine.GuiInterfaceCall("GetTemplateData", templateName); g_TemplateDataWithoutLocalization[templateName] = template; } return g_TemplateDataWithoutLocalization[templateName]; } // Cache TechnologyData var g_TechnologyData = {}; // {id:template} function GetTechnologyData(technologyName) { if (!(technologyName in g_TechnologyData)) { var template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName); translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]); g_TechnologyData[technologyName] = template; } return g_TechnologyData[technologyName]; } // Init function init(initData, hotloadData) { if (initData) { g_IsNetworked = initData.isNetworked; // Set network mode g_IsController = initData.isController; // Set controller mode g_PlayerAssignments = initData.playerAssignments; g_MatchID = initData.attribs.matchID; // Cache the player data // (This may be updated at runtime by handleNetMessage) g_Players = getPlayerData(g_PlayerAssignments); if (initData.savedGUIData) restoreSavedGameData(initData.savedGUIData); Engine.GetGUIObjectByName("gameSpeedButton").hidden = g_IsNetworked; } else // Needed for autostart loading option { g_Players = getPlayerData(null); } // Cache civ data g_CivData = loadCivData(); g_CivData["gaia"] = { "Code": "gaia", "Name": translate("Gaia") }; if (Engine.GetPlayerID() <= 0) { g_IsObserver = true; // Hide stuff observers don't use. Engine.GetGUIObjectByName("food").hidden = true; Engine.GetGUIObjectByName("wood").hidden = true; Engine.GetGUIObjectByName("stone").hidden = true; Engine.GetGUIObjectByName("metal").hidden = true; Engine.GetGUIObjectByName("population").hidden = true; Engine.GetGUIObjectByName("diplomacyButton1").hidden = true; Engine.GetGUIObjectByName("tradeButton1").hidden = true; Engine.GetGUIObjectByName("menuResignButton").enabled = false; Engine.GetGUIObjectByName("pauseButton").enabled = false; Engine.GetGUIObjectByName("observerText").hidden = false; } else { // TODO: Get a civ icon for gaia/observers. Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem; Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name; } g_GameSpeeds = initGameSpeeds(); g_CurrentSpeed = Engine.GetSimRate(); var gameSpeed = Engine.GetGUIObjectByName("gameSpeed"); gameSpeed.list = g_GameSpeeds.names; gameSpeed.list_data = g_GameSpeeds.speeds; var idx = g_GameSpeeds.speeds.indexOf(g_CurrentSpeed); gameSpeed.selected = idx != -1 ? idx : g_GameSpeeds["default"]; gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); } initMenuPosition(); // set initial position // Populate player selection dropdown var playerNames = []; var playerIDs = []; for (var player in g_Players) { playerNames.push(g_Players[player].name); playerIDs.push(player); } var viewPlayerDropdown = Engine.GetGUIObjectByName("viewPlayer"); viewPlayerDropdown.list = playerNames; viewPlayerDropdown.list_data = playerIDs; viewPlayerDropdown.selected = Engine.GetPlayerID(); // If in Atlas editor, disable the exit button if (Engine.IsAtlasRunning()) Engine.GetGUIObjectByName("menuExitButton").enabled = false; if (hotloadData) { g_Selection.selected = hotloadData.selection; } else { // Starting for the first time: initMusic(); if (!g_IsObserver){ var civMusic = g_CivData[g_Players[Engine.GetPlayerID()].civ].Music; global.music.storeTracks(civMusic); } global.music.setState(global.music.states.PEACE); playRandomAmbient("temperate"); } if (Engine.ConfigDB_GetValue("user", "gui.session.timeelapsedcounter") === "true") Engine.GetGUIObjectByName("timeElapsedCounter").hidden = false; onSimulationUpdate(); // Report the performance after 5 seconds (when we're still near // the initial camera view) and a minute (when the profiler will // have settled down if framerates as very low), to give some // extremely rough indications of performance setTimeout(function() { reportPerformance(5); }, 5000); setTimeout(function() { reportPerformance(60); }, 60000); } function selectViewPlayer(playerID) { Engine.SetPlayerID(playerID); if (playerID > 0) { Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[playerID].civ].Emblem; Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[playerID].civ].Name; } } function reportPerformance(time) { var settings = Engine.GetMapSettings(); var data = { time: time, map: settings.Name, seed: settings.Seed, // only defined for random maps size: settings.Size, // only defined for random maps profiler: Engine.GetProfilerState() }; Engine.SubmitUserReport("profile", 3, JSON.stringify(data)); } /** * Resign a player. * @param leaveGameAfterResign If player is quitting after resignation. */ function resignGame(leaveGameAfterResign) { var simState = GetSimState(); // Players can't resign if they've already won or lost. if (simState.players[Engine.GetPlayerID()].state != "active" || g_Disconnected) return; // Tell other players that we have given up and been defeated Engine.PostNetworkCommand({ "type": "defeat-player", "playerId": Engine.GetPlayerID() }); global.music.setState(global.music.states.DEFEAT); // Resume the game if not resigning. if (!leaveGameAfterResign) resumeGame(); } /** * Leave the game * @param willRejoin If player is going to be rejoining a networked game. */ function leaveGame(willRejoin) { var extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState"); var mapSettings = Engine.GetMapSettings(); var gameResult; if (g_IsObserver) { // Observers don't win/lose. gameResult = translate("You have left the game."); global.music.setState(global.music.states.VICTORY); } else { var playerState = extendedSimState.players[Engine.GetPlayerID()]; if (g_Disconnected) gameResult = translate("You have been disconnected."); else if (playerState.state == "won") gameResult = translate("You have won the battle!"); else if (playerState.state == "defeated") gameResult = translate("You have been defeated..."); else // "active" { global.music.setState(global.music.states.DEFEAT); if (willRejoin) gameResult = translate("You have left the game."); else { gameResult = translate("You have abandoned the game."); resignGame(true); } } } stopAmbient(); Engine.EndGame(); if (g_IsController && Engine.HasXmppClient()) Engine.SendUnregisterGame(); Engine.SwitchGuiPage("page_summary.xml", { "gameResult" : gameResult, "timeElapsed" : extendedSimState.timeElapsed, "playerStates": extendedSimState.players, "players": g_Players, "mapSettings": mapSettings }); } // Return some data that we'll use when hotloading this file after changes function getHotloadData() { return { selection: g_Selection.selected }; } // Return some data that will be stored in saved game files function getSavedGameData() { var data = {}; data.playerAssignments = g_PlayerAssignments; data.groups = g_Groups.groups; // TODO: any other gui state? return data; } 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 (var groupNumber in data.groups) { g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups; g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents; } updateGroups(); } var lastTickTime = new Date; var lastXmppClientPoll = Date.now(); /** * Called every frame. */ function onTick() { var now = new Date; var tickLength = new Date - lastTickTime; lastTickTime = now; checkPlayerState(); while (true) { var message = Engine.PollNetworkClient(); if (!message) break; handleNetMessage(message); } updateCursorAndTooltip(); // If the selection changed, we need to regenerate the sim display (the display depends on both the // simulation state and the current selection). if (g_Selection.dirty) { g_Selection.dirty = false; onSimulationUpdate(); // Display rally points for selected buildings if (!g_IsObserver) Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() }); } // Run timers updateTimers(); // Animate menu updateMenuPosition(tickLength); // When training is blocked, flash population (alternates colour every 500msec) if (g_IsTrainingBlocked && (Date.now() % 1000) < 500) Engine.GetGUIObjectByName("resourcePop").textcolor = POPULATION_ALERT_COLOR; else Engine.GetGUIObjectByName("resourcePop").textcolor = DEFAULT_POPULATION_COLOR; // Clear renamed entities list Engine.GuiInterfaceCall("ClearRenamedEntities"); } function checkPlayerState() { // Once the game ends, we're done here. if (g_GameEnded || g_IsObserver) return; // Send a game report for each player in this game. var m_simState = GetSimState(); var playerState = m_simState.players[Engine.GetPlayerID()]; var tempStates = ""; for each (var player in m_simState.players) {tempStates += player.state + ",";} if (g_CachedLastStates != tempStates) { g_CachedLastStates = tempStates; reportGame(Engine.GuiInterfaceCall("GetExtendedSimulationState")); } // If the local player hasn't finished playing, we return here to avoid the victory/defeat messages. if (playerState.state == "active") return; // We can't resign once the game is over. Engine.GetGUIObjectByName("menuResignButton").enabled = false; // Make sure nothing is open to avoid stacking. closeMenu(); closeOpenDialogs(); // Make sure this doesn't run again. g_GameEnded = true; if (Engine.IsAtlasRunning()) { // If we're in Atlas, we can't leave the game var btCaptions = [translate("OK")]; var btCode = [null]; var message = translate("Press OK to continue"); } else { - var btCaptions = [translate("Yes"), translate("No")]; - var btCode = [leaveGame, null]; + var btCaptions = [translate("No"), translate("Yes")]; + var btCode = [null, leaveGame]; var message = translate("Do you want to quit?"); } if (playerState.state == "defeated") { global.music.setState(global.music.states.DEFEAT); messageBox(400, 200, message, translate("DEFEATED!"), 0, btCaptions, btCode); } else if (playerState.state == "won") { global.music.setState(global.music.states.VICTORY); // TODO: Reveal map directly instead of this silly proxy. if (!Engine.GetGUIObjectByName("devCommandsRevealMap").checked) Engine.GetGUIObjectByName("devCommandsRevealMap").checked = true; messageBox(400, 200, message, translate("VICTORIOUS!"), 0, btCaptions, btCode); } } function changeGameSpeed(speed) { // For non-networked games only if (!g_IsNetworked) { Engine.SetSimRate(speed); g_CurrentSpeed = speed; } } /** * Recomputes GUI state that depends on simulation state or selection state. Called directly every simulation * update (see session.xml), or from onTick when the selection has changed. */ function onSimulationUpdate() { g_EntityStates = {}; g_TemplateData = {}; g_TechnologyData = {}; g_SimState = Engine.GuiInterfaceCall("GetSimulationState"); // If we're called during init when the game is first loading, there will be no simulation yet, so do nothing if (!g_SimState) return; handleNotifications(); if (g_ShowAllStatusBars) recalculateStatusBarDisplay(); if (g_ShowGuarding || g_ShowGuarded) updateAdditionalHighlight(); updateHero(); updateGroups(); updateDebug(); updatePlayerDisplay(); updateSelectionDetails(); updateBuildingPlacementPreview(); updateTimeElapsedCounter(); updateTimeNotifications(); if (!g_IsObserver) { updateResearchDisplay(); // Update music state on basis of battle state. var battleState = Engine.GuiInterfaceCall("GetBattleState", Engine.GetPlayerID()); if (battleState) global.music.setState(global.music.states[battleState]); } } /** * updates a status bar on the GUI * nameOfBar: name of the bar * points: points to show * maxPoints: max points * direction: gets less from (right to left) 0; (top to bottom) 1; (left to right) 2; (bottom to top) 3; */ function updateGUIStatusBar(nameOfBar, points, maxPoints, direction) { // check, if optional direction parameter is valid. if (!direction || !(direction >= 0 && direction < 4)) direction = 0; // get the bar and update it var statusBar = Engine.GetGUIObjectByName(nameOfBar); if (!statusBar) return; var healthSize = statusBar.size; var value = 100*Math.max(0, Math.min(1, points / maxPoints)); // inverse bar if(direction == 2 || direction == 3) value = 100 - value; if(direction == 0) healthSize.rright = value; else if(direction == 1) healthSize.rbottom = value; else if(direction == 2) healthSize.rleft = value; else if(direction == 3) healthSize.rtop = value; // update bar statusBar.size = healthSize; } function updateHero() { var simState = GetSimState(); var playerState = simState.players[Engine.GetPlayerID()]; var unitHeroPanel = Engine.GetGUIObjectByName("unitHeroPanel"); var heroButton = Engine.GetGUIObjectByName("unitHeroButton"); if (!playerState || playerState.heroes.length <= 0) { g_previousHeroHitPoints = undefined; unitHeroPanel.hidden = true; return; } var heroImage = Engine.GetGUIObjectByName("unitHeroImage"); var heroState = GetExtendedEntityState(playerState.heroes[0]); var template = GetTemplateData(heroState.template); heroImage.sprite = "stretched:session/portraits/" + template.icon; var hero = playerState.heroes[0]; heroButton.onpress = function() { if (!Engine.HotkeyIsPressed("selection.add")) g_Selection.reset(); g_Selection.addList([hero]); }; heroButton.ondoublepress = function() { selectAndMoveTo(getEntityOrHolder(hero)); }; unitHeroPanel.hidden = false; // Setup tooltip var tooltip = "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]"; var healthLabel = "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]"; tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), { label: healthLabel, current: heroState.hitpoints, max: heroState.maxHitpoints }); if (heroState.attack) { var attackLabel = "[font=\"sans-bold-13\"]" + getAttackTypeLabel(heroState.attack.type) + "[/font]"; if (heroState.attack.type == "Ranged") // Show max attack range if ranged attack, also convert to tiles (4m per tile) tooltip += "\n" + sprintf( translate("%(attackLabel)s %(details)s, %(rangeLabel)s %(range)s"), { attackLabel: attackLabel, details: damageTypeDetails(heroState.attack), rangeLabel: "[font=\"sans-bold-13\"]" + translate("Range:") + "[/font]", range: Math.round(heroState.attack.maxRange) + " [font=\"sans-10\"][color=\"orange\"]" + translate("meters") + "[/color][/font]", } ); else tooltip += "\n" + sprintf(translate("%(label)s %(details)s"), { label: attackLabel, details: damageTypeDetails(heroState.attack) }); } var armorLabel = "[font=\"sans-bold-13\"]" + translate("Armor:") + "[/font]"; tooltip += "\n" + sprintf(translate("%(label)s %(details)s"), { label: armorLabel, details: damageTypeDetails(heroState.armour) }); tooltip += "\n" + template.tooltip; heroButton.tooltip = tooltip; // update heros health bar updateGUIStatusBar("heroHealthBar", heroState.hitpoints, heroState.maxHitpoints); // define the hit points if not defined if (!g_previousHeroHitPoints) g_previousHeroHitPoints = heroState.hitpoints; // check, if the health of the hero changed since the last update if (heroState.hitpoints < g_previousHeroHitPoints) { g_previousHeroHitPoints = heroState.hitpoints; // trigger the animation startColorFade("heroHitOverlay", 100, 0, colorFade_attackUnit, true, smoothColorFadeRestart_attackUnit); return; } } function updateGroups() { var guiName = "Group"; g_Groups.update(); for (var i = 0; i < 10; i++) { var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]"); var label = Engine.GetGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i; if (g_Groups.groups[i].getTotalCount() == 0) button.hidden = true; else button.hidden = false; 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); } var numButtons = i; var rowLength = 1; var numRows = Math.ceil(numButtons / rowLength); var buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button[0]").size.bottom; var buttonSpacer = buttonSideLength+1; for (var i = 0; i < numRows; i++) layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, rowLength*i, rowLength*(i+1) ); } function updateDebug() { var simState = GetSimState(); var debug = Engine.GetGUIObjectByName("debug"); if (Engine.GetGUIObjectByName("devDisplayState").checked) { debug.hidden = false; } else { debug.hidden = true; return; } var conciseSimState = deepcopy(simState); conciseSimState.players = "<<>>"; var text = "simulation: " + uneval(conciseSimState); var selection = g_Selection.toList(); if (selection.length) { var entState = GetExtendedEntityState(selection[0]); if (entState) { var template = GetTemplateData(entState.template); text += "\n\nentity: {\n"; for (var k in entState) text += " "+k+":"+uneval(entState[k])+"\n"; text += "}\n\ntemplate: " + uneval(template); } } debug.caption = text; } function updatePlayerDisplay() { var simState = GetSimState(); var playerState = simState.players[Engine.GetPlayerID()]; if (!playerState) return; Engine.GetGUIObjectByName("resourceFood").caption = Math.floor(playerState.resourceCounts.food); Engine.GetGUIObjectByName("resourceWood").caption = Math.floor(playerState.resourceCounts.wood); Engine.GetGUIObjectByName("resourceStone").caption = Math.floor(playerState.resourceCounts.stone); Engine.GetGUIObjectByName("resourceMetal").caption = Math.floor(playerState.resourceCounts.metal); Engine.GetGUIObjectByName("resourcePop").caption = playerState.popCount + "/" + playerState.popLimit; g_IsTrainingBlocked = playerState.trainingBlocked; } function selectAndMoveTo(ent) { var entState = GetEntityState(ent); if (!entState || !entState.position) return; g_Selection.reset(); g_Selection.addList([ent]); var position = entState.position; Engine.CameraMoveTo(position.x, position.z); } function updateResearchDisplay() { var researchStarted = Engine.GuiInterfaceCall("GetStartedResearch", Engine.GetPlayerID()); if (!researchStarted) return; // Set up initial positioning. var buttonSideLength = Engine.GetGUIObjectByName("researchStartedButton[0]").size.right; for (var i = 0; i < 10; ++i) { var button = Engine.GetGUIObjectByName("researchStartedButton[" + i + "]"); var size = button.size; size.top = (4 + buttonSideLength) * i; size.bottom = size.top + buttonSideLength; button.size = size; } var numButtons = 0; for (var tech in researchStarted) { // Show at most 10 in-progress techs. if (numButtons >= 10) break; var template = GetTechnologyData(tech); var button = Engine.GetGUIObjectByName("researchStartedButton[" + numButtons + "]"); button.hidden = false; button.tooltip = getEntityNames(template); button.onpress = (function(e) { return function() { selectAndMoveTo(e) } })(researchStarted[tech].researcher); var icon = "stretched:session/portraits/" + template.icon; Engine.GetGUIObjectByName("researchStartedIcon[" + numButtons + "]").sprite = icon; // Scale the progress indicator. var size = Engine.GetGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size; // Buttons are assumed to be square, so left/right offsets can be used for top/bottom. size.top = size.left + Math.round(researchStarted[tech].progress * (size.right - size.left)); Engine.GetGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size = size; ++numButtons; } // Hide unused buttons. for (var i = numButtons; i < 10; ++i) Engine.GetGUIObjectByName("researchStartedButton[" + i + "]").hidden = true; } function updateTimeElapsedCounter() { var simState = GetSimState(); var timeElapsedCounter = Engine.GetGUIObjectByName("timeElapsedCounter"); if (g_CurrentSpeed != 1.0) timeElapsedCounter.caption = sprintf(translate("%(time)s (%(speed)sx)"), { time: timeToString(simState.timeElapsed), speed: Engine.FormatDecimalNumberIntoString(g_CurrentSpeed) }); else timeElapsedCounter.caption = timeToString(simState.timeElapsed); } // Toggles the display of status bars for all of the player's entities. function recalculateStatusBarDisplay() { if (g_ShowAllStatusBars) var entities = Engine.PickFriendlyEntitiesOnScreen(Engine.GetPlayerID()); else { var selected = g_Selection.toList(); for each (var ent in g_Selection.highlighted) selected.push(ent); // Remove selected entities from the 'all entities' array, to avoid disabling their status bars. var entities = Engine.GuiInterfaceCall("GetPlayerEntities").filter( function(idx) { return (selected.indexOf(idx) == -1); } ); } Engine.GuiInterfaceCall("SetStatusBars", { "entities": entities, "enabled": g_ShowAllStatusBars }); } // Update the additional list of entities to be highlighted. function updateAdditionalHighlight() { var entsAdd = []; // list of entities units to be highlighted var entsRemove = []; var highlighted = g_Selection.toList(); for each (var ent in g_Selection.highlighted) highlighted.push(ent); if (g_ShowGuarding) { // flag the guarding entities to add in this additional highlight for each (var sel in g_Selection.selected) { var state = GetEntityState(sel); if (!state.guard || !state.guard.entities.length) continue; for each (var ent in 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 each (var sel in g_Selection.selected) { var state = GetEntityState(sel); if (!state.unitAI || !state.unitAI.isGuarding) continue; var 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 each (var ent in g_AdditionalHighlight) if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1 && entsRemove.indexOf(ent) == -1) entsRemove.push(ent); _setHighlight(entsAdd , HIGHLIGHTED_ALPHA, true ); _setHighlight(entsRemove, 0 , false); g_AdditionalHighlight = entsAdd; } // Temporarily adding this here const AMBIENT_TEMPERATE = "temperate"; var currentAmbient; function playRandomAmbient(type) { switch (type) { case AMBIENT_TEMPERATE: // Seem to need the underscore at the end of "temperate" to avoid crash // (Might be caused by trying to randomly load day_temperate.xml) // currentAmbient = newRandomSound("ambient", "temperate_", "dayscape"); const AMBIENT = "audio/ambient/dayscape/day_temperate_gen_03.ogg"; Engine.PlayAmbientSound( AMBIENT, true ); break; default: Engine.Console_Write(sprintf(translate("Unrecognized ambient type: %(ambientType)s"), { ambientType: type })); break; } } // Temporarily adding this here function stopAmbient() { if (currentAmbient) { currentAmbient.free(); currentAmbient = null; } } function getBuildString() { return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), { buildDate: Engine.GetBuildTimestamp(0), revision: Engine.GetBuildTimestamp(2) }); } function showTimeWarpMessageBox() { messageBox(500, 250, translate("Note: time warp mode is a developer option, and not intended for use over long periods of time. Using it incorrectly may cause the game to run out of memory or crash."), translate("Time warp mode"), 2); } // Send a report on the game status to the lobby function reportGame(extendedSimState) { if (!Engine.HasXmppClient() || !Engine.IsRankedGame()) return; // units var unitsClasses = [ "total", "Infantry", "Worker", "Female", "Cavalry", "Champion", "Hero", "Ship" ]; var unitsCountersTypes = [ "unitsTrained", "unitsLost", "enemyUnitsKilled" ]; // buildings var buildingsClasses = [ "total", "CivCentre", "House", "Economic", "Outpost", "Military", "Fortress", "Wonder" ]; var buildingsCountersTypes = [ "buildingsConstructed", "buildingsLost", "enemyBuildingsDestroyed" ]; // resources var resourcesTypes = [ "wood", "food", "stone", "metal" ]; var resourcesCounterTypes = [ "resourcesGathered", "resourcesUsed", "resourcesSold", "resourcesBought" ]; var playerStatistics = { }; // Unit Stats for each (var unitCounterType in unitsCountersTypes) { if (!playerStatistics[unitCounterType]) playerStatistics[unitCounterType] = { }; for each (var unitsClass in unitsClasses) playerStatistics[unitCounterType][unitsClass] = ""; } playerStatistics.unitsLostValue = ""; playerStatistics.unitsKilledValue = ""; // Building stats for each (var buildingCounterType in buildingsCountersTypes) { if (!playerStatistics[buildingCounterType]) playerStatistics[buildingCounterType] = { }; for each (var buildingsClass in buildingsClasses) playerStatistics[buildingCounterType][buildingsClass] = ""; } playerStatistics.buildingsLostValue = ""; playerStatistics.enemyBuildingsDestroyedValue = ""; // Resources for each (var resourcesCounterType in resourcesCounterTypes) { if (!playerStatistics[resourcesCounterType]) playerStatistics[resourcesCounterType] = { }; for each (var resourcesType in resourcesTypes) playerStatistics[resourcesCounterType][resourcesType] = ""; } playerStatistics.resourcesGathered.vegetarianFood = ""; playerStatistics.tradeIncome = ""; // Tribute playerStatistics.tributesSent = ""; playerStatistics.tributesReceived = ""; // Total playerStatistics.economyScore = ""; playerStatistics.militaryScore = ""; playerStatistics.totalScore = ""; // Various playerStatistics.treasuresCollected = ""; playerStatistics.feminisation = ""; playerStatistics.percentMapExplored = ""; var mapName = Engine.GetMapSettings().Name; var playerStates = ""; var playerCivs = ""; var teams = ""; var teamsLocked = true; // Serialize the statistics for each player into a comma-separated list. for each (var player in extendedSimState.players) { playerStates += player.state + ","; playerCivs += player.civ + ","; teams += player.team + ","; teamsLocked = teamsLocked && player.teamsLocked; for each (var resourcesCounterType in resourcesCounterTypes) for each (var resourcesType in resourcesTypes) playerStatistics[resourcesCounterType][resourcesType] += player.statistics[resourcesCounterType][resourcesType] + ","; playerStatistics.resourcesGathered.vegetarianFood += player.statistics.resourcesGathered.vegetarianFood + ","; for each (var unitCounterType in unitsCountersTypes) for each (var unitsClass in unitsClasses) playerStatistics[unitCounterType][unitsClass] += player.statistics[unitCounterType][unitsClass] + ","; for each (var buildingCounterType in buildingsCountersTypes) for each (var buildingsClass in buildingsClasses) playerStatistics[buildingCounterType][buildingsClass] += player.statistics[buildingCounterType][buildingsClass] + ","; var total = 0; for each (var res in player.statistics.resourcesGathered) total += res; playerStatistics.economyScore += total + ","; playerStatistics.militaryScore += Math.round((player.statistics.enemyUnitsKilledValue + player.statistics.enemyBuildingsDestroyedValue) / 10) + ","; playerStatistics.totalScore += (total + Math.round((player.statistics.enemyUnitsKilledValue + player.statistics.enemyBuildingsDestroyedValue) / 10)) + ","; playerStatistics.tradeIncome += player.statistics.tradeIncome + ","; playerStatistics.tributesSent += player.statistics.tributesSent + ","; playerStatistics.tributesReceived += player.statistics.tributesReceived + ","; playerStatistics.percentMapExplored += player.statistics.percentMapExplored + ","; playerStatistics.treasuresCollected += player.statistics.treasuresCollected + ","; } // Send the report with serialized data var reportObject = { }; reportObject.timeElapsed = extendedSimState.timeElapsed; reportObject.playerStates = playerStates; reportObject.playerID = Engine.GetPlayerID(); reportObject.matchID = g_MatchID; reportObject.civs = playerCivs; reportObject.teams = teams; reportObject.teamsLocked = String(teamsLocked); reportObject.mapName = mapName; reportObject.economyScore = playerStatistics.economyScore; reportObject.militaryScore = playerStatistics.militaryScore; reportObject.totalScore = playerStatistics.totalScore; for each (var rct in resourcesCounterTypes) { for each (var rt in resourcesTypes) reportObject[rt+rct.substr(9)] = playerStatistics[rct][rt]; // eg. rt = food rct.substr = Gathered rct = resourcesGathered } reportObject.vegetarianFoodGathered = playerStatistics.resourcesGathered.vegetarianFood; for each (var type in unitsClasses) { // eg. type = Infantry (type.substr(0,1)).toLowerCase()+type.substr(1) = infantry reportObject[(type.substr(0,1)).toLowerCase()+type.substr(1)+"UnitsTrained"] = playerStatistics.unitsTrained[type]; reportObject[(type.substr(0,1)).toLowerCase()+type.substr(1)+"UnitsLost"] = playerStatistics.unitsLost[type]; reportObject["enemy"+type+"UnitsKilled"] = playerStatistics.enemyUnitsKilled[type]; } for each (var type in buildingsClasses) { reportObject[(type.substr(0,1)).toLowerCase()+type.substr(1)+"BuildingsConstructed"] = playerStatistics.buildingsConstructed[type]; reportObject[(type.substr(0,1)).toLowerCase()+type.substr(1)+"BuildingsLost"] = playerStatistics.buildingsLost[type]; reportObject["enemy"+type+"BuildingsDestroyed"] = playerStatistics.enemyBuildingsDestroyed[type]; } reportObject.tributesSent = playerStatistics.tributesSent; reportObject.tributesReceived = playerStatistics.tributesReceived; reportObject.percentMapExplored = playerStatistics.percentMapExplored; reportObject.treasuresCollected = playerStatistics.treasuresCollected; reportObject.tradeIncome = playerStatistics.tradeIncome; Engine.SendGameReport(reportObject); }