Ticket #1090: playercomponent_savegamesummaryreplaycleanup_v2.patch

File playercomponent_savegamesummaryreplaycleanup_v2.patch, 54.6 KB (added by elexis, 8 years ago)

fixed order of execution bug, added more cleanup because the patch wasnt big enough

  • binaries/data/mods/public/gui/common/functions_utility.js

    function clearChatMessages()  
    211211    }
    212212}
    213213
    214214/**
    215215 * Returns a formatted string describing the player assignments.
    216  * Including civs, teams, AI settings and player colors
    217  * which are given in data (array of objects per player).
     216 * Needs g_CivData to translate!
    218217 *
     218 * @param {object} playerDataArray - As known from gamesetup and simstate.
     219 * @param {(string[]|false)} playerStates - One of "won", "defeated", "active" for each player.
    219220 * @returns {string}
    220221 */
    221 function formatPlayerInfo(data)
     222function formatPlayerInfo(playerDataArray, playerStates)
    222223{
    223224    let playerDescriptions = {};
    224225    let playerIdx = 0;
    225     for (let playerData of data)
     226
     227    for (let playerData of playerDataArray)
    226228    {
     229        if (playerData == null || playerData.Civ == "gaia")
     230            continue;
     231
    227232        ++playerIdx;
    228233        let teamIdx = playerData.Team;
    229         let showDefeated = playerData.state && playerData.state == "defeated";
    230234        let isAI = playerData.AI && playerData.AI != "";
     235        let playerState = playerStates && playerStates[playerIdx];
     236        let isActive = !playerState || playerState == "active";
    231237
    232         let translated;
    233         if (!isAI && !showDefeated)
    234             translated = translateWithContext("replay", "%(playerName)s (%(civ)s)");
    235         else if (!isAI && showDefeated)
    236             translated = translateWithContext("replay", "%(playerName)s (%(civ)s, defeated)");
    237         else if (isAI && !showDefeated)
    238             translated = translateWithContext("replay", "%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s)");
     238        let playerDescription;
     239        if (isAI)
     240        {
     241            if (isActive)
     242                playerDescription = translateWithContext("replay", "%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s, %(state)s)");
     243            else
     244                playerDescription = translateWithContext("replay", "%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s)");
     245        }
    239246        else
    240             translated = translateWithContext("replay", "%(playerName)s (%(civ)s, %(AIdifficulty)s %(AIname)s, defeated)");
     247        {
     248            if (isActive)
     249                playerDescription = translateWithContext("replay", "%(playerName)s (%(civ)s, %(state)s)");
     250            else
     251                playerDescription = translateWithContext("replay", "%(playerName)s (%(civ)s)");
     252        }
    241253
    242254        // Sort player descriptions by team
    243255        if (!playerDescriptions[teamIdx])
    244256            playerDescriptions[teamIdx] = [];
    245         playerDescriptions[teamIdx].push(sprintf(translated, {
    246             "playerName": '[color="' + rgbToGuiColor(playerData.Color) + '"]' + escapeText(playerData.Name) + "[/color]",
    247             "civ": playerData.Civ,
     257
     258        playerDescriptions[teamIdx].push(sprintf(playerDescription, {
     259            "playerName":
     260                '[color="' +
     261                rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color) +
     262                '"]' + escapeText(playerData.Name) + "[/color]",
     263
     264            "civ":
     265                !playerData.Civ ?
     266                    translate("Unknown Civilization") :
     267                        g_CivData && g_CivData[playerData.Civ] && g_CivData[playerData.Civ].Name ?
     268                        translate(g_CivData[playerData.Civ].Name) :
     269                        playerData.Civ,
     270
     271            "state":
     272                playerState == "defeated" ?
     273                    translateWithContext("playerState", "Defeated") :
     274                    translateWithContext("playerState", "Won"),
     275
    248276            "AIname": isAI ? translateAIName(playerData.AI) : "",
    249277            "AIdifficulty": isAI ? translateAIDifficulty(playerData.AIDiff) : ""
    250278        }));
    251279    }
    252280
    function formatPlayerInfo(data)  
    256284    if (teams.length == 1)
    257285        return playerDescriptions[teams[0]].join("\n") + "\n";
    258286
    259287    // If there are teams, merge "Team N:" + playerDescriptions
    260288    return teams.map(team => {
    261         let teamCaption = (team == -1) ? translate("No Team") : sprintf(translate("Team %(team)s"), { "team": +team + 1 });
    262         return '[font="sans-bold-14"]' + teamCaption + "[/font]:\n" + playerDescriptions[team].join("\n");
     289
     290        let teamCaption = team == -1 ?
     291            translate("No Team") :
     292            sprintf(translate("Team %(team)s"), { "team": +team + 1 });
     293
     294        return sprintf(translateWithContext("replay", "%(team)s:\n%(playerDescriptions)s"), {
     295            "team": '[font="sans-bold-14"]' + teamCaption + "[/font]",
     296            "playerDescriptions": playerDescriptions[team].join("\n")
     297        });
    263298    }).join("\n\n");
    264299}
  • binaries/data/mods/public/gui/replaymenu/replay_menu.js

     
    22 * Used for checking replay compatibility.
    33 */
    44const g_EngineInfo = Engine.GetEngineInfo();
    55
    66/**
    7  * To show the titles of the selected civs in the replay details.
     7 * Needed for formatPlayerInfo to show the player civs in the details.
    88 */
    99const g_CivData = loadCivData();
    1010
    1111/**
    1212 * Used for creating the mapsize filter.
    function displayReplayList()  
    188188    if (replaySelection.selected != -1)
    189189        g_SelectedReplayDirectory = g_ReplaysFiltered[replaySelection.selected].directory;
    190190
    191191    filterReplays();
    192192
    193     // Create GUI list data
    194193    var list = g_ReplaysFiltered.map(replay => {
    195194        let works = replay.isCompatible;
    196195        return {
    197196            "directories": replay.directory,
    198197            "months": greyout(getReplayDateTime(replay), works),
    function displayReplayList()  
    202201            "durations": greyout(getReplayDuration(replay), works),
    203202            "playerNames": greyout(getReplayPlayernames(replay), works)
    204203        };
    205204    });
    206205
    207     // Extract arrays
    208206    if (list.length)
    209207        list = prepareForDropdown(list);
    210208
    211209    // Push to GUI
    212210    replaySelection.selected = -1;
    function displayReplayList()  
    219217
    220218    // Change these last, otherwise crash
    221219    replaySelection.list = list.directories || [];
    222220    replaySelection.list_data = list.directories || [];
    223221
    224     // Restore selection
    225222    replaySelection.selected = replaySelection.list.findIndex(directory => directory == g_SelectedReplayDirectory);
    226223
    227224    displayReplayDetails();
    228225}
    229226
    230227/**
    231228 * Shows preview image, description and player text in the right panel.
    232229 */
    233230function displayReplayDetails()
    234231{
    235     var selected = Engine.GetGUIObjectByName("replaySelection").selected;
    236     var replaySelected = selected > -1;
     232    let selected = Engine.GetGUIObjectByName("replaySelection").selected;
     233    let replaySelected = selected > -1;
    237234
    238235    Engine.GetGUIObjectByName("replayInfo").hidden = !replaySelected;
    239236    Engine.GetGUIObjectByName("replayInfoEmpty").hidden = replaySelected;
    240237    Engine.GetGUIObjectByName("startReplayButton").enabled = replaySelected;
    241238    Engine.GetGUIObjectByName("deleteReplayButton").enabled = replaySelected;
    242239    Engine.GetGUIObjectByName("summaryButton").hidden = true;
    243240
    244241    if (!replaySelected)
    245242        return;
    246243
    247     var replay = g_ReplaysFiltered[selected];
    248     var mapData = getMapDescriptionAndPreview(replay.attribs.settings.mapType, replay.attribs.map);
     244    let replay = g_ReplaysFiltered[selected];
    249245
    250     // Update GUI
    251246    Engine.GetGUIObjectByName("sgMapName").caption = translate(replay.attribs.settings.Name);
    252247    Engine.GetGUIObjectByName("sgMapSize").caption = translateMapSize(replay.attribs.settings.Size);
    253248    Engine.GetGUIObjectByName("sgMapType").caption = translateMapType(replay.attribs.settings.mapType);
    254249    Engine.GetGUIObjectByName("sgVictory").caption = translateVictoryCondition(replay.attribs.settings.GameType);
    255250    Engine.GetGUIObjectByName("sgNbPlayers").caption = replay.attribs.settings.PlayerData.length;
    256     Engine.GetGUIObjectByName("sgPlayersNames").caption = getReplayTeamText(replay);
     251
     252    let metadata = Engine.GetReplayMetadata(replay.directory);
     253    let mapData = getMapDescriptionAndPreview(replay.attribs.settings.mapType, replay.attribs.map);
     254    let playerStates =
     255        Engine.GetGUIObjectByName("showSpoiler").checked &&
     256        metadata &&
     257        metadata.playerStates &&
     258        metadata.playerStates.map(pState => pState.state);
     259
     260    Engine.GetGUIObjectByName("sgPlayersNames").caption = formatPlayerInfo(replay.attribs.settings.PlayerData, playerStates);
    257261    Engine.GetGUIObjectByName("sgMapDescription").caption = mapData.description;
    258262    Engine.GetGUIObjectByName("summaryButton").hidden = !Engine.HasReplayMetadata(replay.directory);
    259263
    260264    setMapPreviewImage("sgMapPreview", mapData.preview);
    261265}
    function isReplayCompatible(replay)  
    329333 */
    330334function replayHasSameEngineVersion(replay)
    331335{
    332336    return replay.attribs.engine_version && replay.attribs.engine_version == g_EngineInfo.engine_version;
    333337}
    334 
    335 /**
    336  * Returns a description of the player assignments.
    337  * Including civs, teams, AI settings and player colors.
    338  *
    339  * If the spoiler-checkbox is checked, it also shows defeated players.
    340  *
    341  * @returns {string}
    342  */
    343 function getReplayTeamText(replay)
    344 {
    345     // Load replay metadata
    346     const metadata = Engine.GetReplayMetadata(replay.directory);
    347     const spoiler = Engine.GetGUIObjectByName("showSpoiler").checked;
    348 
    349     let data = [];
    350     let playerIdx = 0;
    351     for (let playerData of replay.attribs.settings.PlayerData)
    352     {
    353         ++playerIdx;
    354         data.push({
    355             "Team": playerData.Team,
    356             "Name": playerData.Name,
    357             "Civ": !playerData.Civ ? translate("Unknown Civilization") :
    358                 (g_CivData[playerData.Civ] && g_CivData[playerData.Civ].Name ? translate(g_CivData[playerData.Civ].Name) : playerData.Civ),
    359             "Color": playerData.Color ? playerData.Color : g_Settings.PlayerDefaults[playerIdx].Color,
    360             "AI": playerData.AI,
    361             "AIDiff": playerData.AIDiff,
    362             "Defeated": spoiler && metadata && metadata.playerStates && metadata.playerStates[playerIdx].state == "defeated"
    363         });
    364     }
    365 
    366     return formatPlayerInfo(data);
    367 }
  • binaries/data/mods/public/gui/savedgames/load.js

     
    11var g_SavedGamesMetadata = [];
    22
     3/**
     4 * Needed for formatPlayerInfo to show the player civs in the details.
     5 */
     6const g_CivData = loadCivData();
     7
    38function init()
    49{
    510    let gameSelection = Engine.GetGUIObjectByName("gameSelection");
    611
    712    let savedGames = Engine.GetSavedGames().sort(sortDecreasingDate);
    function selectionChanged()  
    5661    let caption = sprintf(translate("Mods: %(mods)s"), { "mods": metadata.mods.join(translate(", ")) });
    5762    if (!hasSameMods(metadata, Engine.GetEngineInfo()))
    5863        caption = "[color=\"orange\"]" + caption + "[/color]";
    5964    Engine.GetGUIObjectByName("savedMods").caption = caption;
    6065
    61     let data = [];
    62     let playerIdx = 0;
    63     for (let playerData of metadata.initAttributes.settings.PlayerData)
    64     {
    65         if (playerData == null || playerData.Civ == "gaia")
    66             continue;
    67         ++playerIdx;
    68         data.push({
    69             "Team": playerData.Team,
    70             "Name": playerData.Name,
    71             "Civ": playerData.Civ,
    72             "Color": playerData.Color,
    73             "AI": playerData.AI,
    74             "AIDiff": playerData.AIDiff,
    75             "Defeated": metadata.gui.states && metadata.gui.states[playerIdx] == "defeated"
    76         });
    77     }
    78 
    79     Engine.GetGUIObjectByName("savedPlayersNames").caption = formatPlayerInfo(data);
     66    Engine.GetGUIObjectByName("savedPlayersNames").caption = formatPlayerInfo(
     67        metadata.initAttributes.settings.PlayerData,
     68        metadata.gui.states
     69    );
    8070}
    8171
    8272function loadGame()
    8373{
    8474    let gameSelection = Engine.GetGUIObjectByName("gameSelection");
  • binaries/data/mods/public/gui/savedgames/load.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22
    33<objects>
    44
    55    <script file="gui/common/color.js" />
     6    <script file="gui/common/functions_civinfo.js"/>
    67    <script file="gui/common/functions_global_object.js" />
    78    <script file="gui/common/functions_utility.js" />
    89    <script file="gui/common/functions_utility_loadsave.js" />
    910    <script file="gui/common/settings.js" />
    1011    <script file="gui/savedgames/load.js" />
     
    2829                <action on="Press">Engine.PopGuiPage();</action>
    2930            </object>
    3031
    3132            <object name="deleteGameButton" type="button" size="33%+20 100%-60 66%-15 100%-32" style="StoneButton" hotkey="session.savedgames.delete">
    3233                <translatableAttribute id="caption">Delete</translatableAttribute>
    33                 <action on="Press">
    34                     if (!this.enabled)
    35                         return;
    36                     if (Engine.HotkeyIsPressed("session.savedgames.noconfirmation"))
    37                         deleteGameWithoutConfirmation();
    38                     else
    39                         deleteGame();
    40                 </action>
     34                <action on="Press">deleteGame();</action>
    4135            </object>
    4236
    4337            <object name="loadGameButton" type="button" style="StoneButton" size="66%-5 100%-60 100%-25 100%-32">
    4438                <translatableAttribute id="caption">Load</translatableAttribute>
    4539                <action on="Press">loadGame();</action>
  • binaries/data/mods/public/gui/savedgames/save.js

    function selectDescription()  
    1515function init(data)
    1616{
    1717    g_SavedGameData = data && data.savedGameData || {};
    1818    let simulationState = Engine.GuiInterfaceCall("GetSimulationState");
    1919    g_SavedGameData.timeElapsed = simulationState.timeElapsed;
    20     g_SavedGameData.states = [];
    21     for (let player of simulationState.players)
    22         g_SavedGameData.states.push(player.state);
     20    g_SavedGameData.states = simulationState.players.map(pState => pState.state);
    2321
    2422    let gameSelection = Engine.GetGUIObjectByName("gameSelection");
    2523    Engine.GetGUIObjectByName("deleteGameButton").enabled = false;
    2624
    2725    let savedGames = Engine.GetSavedGames().sort(sortDecreasingDate);
  • binaries/data/mods/public/gui/session/menu.js

    function toggleDeveloperOverlay()  
    751751        "translateParameters": [],
    752752        "parameters": {}
    753753    });
    754754}
    755755
     756// TODO: should also close message boxes
    756757function closeOpenDialogs()
    757758{
    758759    closeMenu();
    759760    closeChat();
    760761    closeDiplomacy();
  • binaries/data/mods/public/gui/session/messages.js

    var g_FormatChatMessage = {  
    9999            }
    100100        ),
    101101    "clientlist": msg => getUsernameList(),
    102102    "message": msg => formatChatCommand(msg),
    103103    "defeat": msg => formatDefeatMessage(msg),
     104    "won": msg => formatWinMessage(msg),
    104105    "diplomacy": msg => formatDiplomacyMessage(msg),
    105106    "tribute": msg => formatTributeMessage(msg),
    106107    "attack": msg => formatAttackMessage(msg)
    107108};
    108109
    var g_DiplomacyMessages = {  
    192193    }
    193194};
    194195
    195196/**
    196197 * Defines how the GUI reacts to notifications that are sent by the simulation.
     198 * Don't open new pages (message boxes) here! Otherwise further notifications
     199 * handled in the same turn can't access the GUI objects anymore.
    197200 */
    198201var g_NotificationsTypes =
    199202{
    200203    "chat": function(notification, player)
    201204    {
    var g_NotificationsTypes =  
    244247            "type": "defeat",
    245248            "guid": findGuidForPlayerID(player),
    246249            "player": player,
    247250            "resign": !!notification.resign
    248251        });
    249 
    250         updateDiplomacy();
    251         updateChatAddressees();
     252        playerFinished(player, false);
     253    },
     254    "won": function(notification, player)
     255    {
     256        addChatMessage({
     257            "type": "won",
     258            "guid": findGuidForPlayerID(player),
     259            "player": player
     260        });
     261        playerFinished(player, true);
    252262    },
    253263    "diplomacy": function(notification, player)
    254264    {
    255265        addChatMessage({
    256266            "type": "diplomacy",
    function findGuidForPlayerID(playerID)  
    415425/**
    416426 * Processes all pending notifications sent from the GUIInterface simulation component.
    417427 */
    418428function handleNotifications()
    419429{
    420     let notifications = Engine.GuiInterfaceCall("GetNotifications");
    421     for (let notification of notifications)
     430    for (let notification of Engine.GuiInterfaceCall("GetNotifications"))
    422431    {
    423432        if (!notification.players || !notification.type || !g_NotificationsTypes[notification.type])
    424433        {
    425434            error("Invalid GUI notification: " + uneval(notification));
    426435            continue;
    function formatDefeatMessage(msg)  
    743752            translate("%(player)s has been defeated."),
    744753        { "player": colorizePlayernameByID(msg.player) }
    745754    );
    746755}
    747756
     757function formatWinMessage(msg)
     758{
     759    return sprintf(translate("%(player)s has won."), {
     760        "player": colorizePlayernameByID(msg.player)
     761    });
     762}
     763
    748764function formatDiplomacyMessage(msg)
    749765{
    750766    let messageType;
    751767
    752768    if (g_IsObserver)
  • binaries/data/mods/public/gui/session/session.js

    var g_IsObserver = false;  
    3636 * True if the current user has rejoined (or joined the game after it started).
    3737 */
    3838var g_HasRejoined = false;
    3939
    4040/**
     41 * Shows a message box asking the user to leave if "won" or "defeated".
     42 */
     43var g_ConfirmExit = false;
     44
     45/**
    4146 * True if the current player has paused the game explicitly.
    4247 */
    4348var g_Paused = false;
    4449
    4550/**
    var g_EntityStates = {};  
    117122var g_TemplateData = {};
    118123var g_TemplateDataWithoutLocalization = {};
    119124var g_TechnologyData = {};
    120125
    121126/**
    122  * Cache concatenated list of player states ("active", "defeated" or "won").
    123  */
    124 var g_CachedLastStates = "";
    125 
    126 /**
    127  * Whether the current player has lost/won and reached the end of their game.
    128  * Used for reporting the gamestate and showing the game-end message only once.
    129  */
    130 var g_GameEnded = false;
    131 
    132 /**
    133127 * Top coordinate of the research list.
    134128 * Changes depending on the number of displayed counters.
    135129 */
    136130var g_ResearchListTop = 4;
    137131
    function init(initData, hotloadData)  
    238232        g_PlayerAssignments = initData.playerAssignments;
    239233        g_MatchID = initData.attribs.matchID;
    240234        g_ReplaySelectionData = initData.replaySelectionData;
    241235        g_HasRejoined = initData.isRejoining;
    242236
    243         g_Players = getPlayerData();
    244 
    245237        if (initData.savedGUIData)
    246238            restoreSavedGameData(initData.savedGUIData);
    247239
    248240        Engine.GetGUIObjectByName("gameSpeedButton").hidden = g_IsNetworked;
    249241    }
    250242    else // Needed for autostart loading option
    251243    {
    252244        if (g_IsReplay)
    253245            g_PlayerAssignments.local.player = -1;
    254 
    255         g_Players = getPlayerData();
    256246    }
    257247
     248    g_Players = getPlayerData();
     249
    258250    g_CivData = loadCivData();
    259251    g_CivData.gaia = { "Code": "gaia", "Name": translate("Gaia") };
    260252
    261253    initializeMusic(); // before changing the perspective
    262     selectViewPlayer(g_ViewedPlayer);
    263254
    264255    let gameSpeed = Engine.GetGUIObjectByName("gameSpeed");
    265256    gameSpeed.list = g_GameSpeeds.Title;
    266257    gameSpeed.list_data = g_GameSpeeds.Speed;
    267258    let gameSpeedIdx = g_GameSpeeds.Speed.indexOf(Engine.GetSimRate());
    function init(initData, hotloadData)  
    279270    {
    280271        playerIDs.push(player);
    281272        playerNames.push(colorizePlayernameHelper("■", player) + " " + g_Players[player].name);
    282273    }
    283274
     275    // Select "observer" item when rejoining as a defeated player
     276    let viewedPlayer = g_Players[Engine.GetPlayerID()];
    284277    let viewPlayerDropdown = Engine.GetGUIObjectByName("viewPlayer");
    285278    viewPlayerDropdown.list = playerNames;
    286279    viewPlayerDropdown.list_data = playerIDs;
    287     viewPlayerDropdown.selected = Engine.GetPlayerID() + 1;
     280    viewPlayerDropdown.selected = viewedPlayer && viewedPlayer.state == "defeated" ? 0 : Engine.GetPlayerID() + 1;
    288281
    289282    // If in Atlas editor, disable the exit button
    290283    if (Engine.IsAtlasRunning())
    291284        Engine.GetGUIObjectByName("menuExitButton").enabled = false;
    292285
    function controlsPlayer(playerID)  
    433426    return playerState && (
    434427        playerState.state != "defeated" || playerState.controlsAll);
    435428}
    436429
    437430/**
     431 * Called when a player has won or was defeated.
     432 */
     433function playerFinished(player, won)
     434{
     435    reportGame();
     436    updateDiplomacy();
     437    updateChatAddressees();
     438
     439    if (player != Engine.GetPlayerID() || Engine.IsAtlasRunning())
     440        return;
     441
     442    global.music.setState(
     443        won ?
     444            global.music.states.VICTORY :
     445            global.music.states.DEFEAT
     446    );
     447
     448    // Select "observer" item
     449    if (!won)
     450        Engine.GetGUIObjectByName("viewPlayer").selected = 0;
     451
     452    g_ConfirmExit = won ? "won" : "defeated";
     453}
     454
     455/**
    438456 * Sets civ icon for the currently viewed player.
    439457 * Hides most gui objects for observers.
    440458 */
    441459function updateTopPanel()
    442460{
    function resignGame(leaveGameAfterResign  
    490508        "type": "defeat-player",
    491509        "playerId": Engine.GetPlayerID(),
    492510        "resign": true
    493511    });
    494512
    495     updateTopPanel();
    496 
    497513    global.music.setState(global.music.states.DEFEAT);
    498514
    499515    if (!leaveGameAfterResign)
    500516        resumeGame(true);
    501517}
    function resignGame(leaveGameAfterResign  
    505521 * @param willRejoin If player is going to be rejoining a networked game.
    506522 */
    507523function leaveGame(willRejoin)
    508524{
    509525    let extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState");
    510     let mapSettings = Engine.GetMapSettings();
    511     let gameResult;
    512 
    513     if (Engine.GetPlayerID() == -1)
    514     {
    515         gameResult = translate("You have left the game.");
    516         global.music.setState(global.music.states.VICTORY);
    517     }
    518     else
    519     {
    520         let playerState = extendedSimState.players[Engine.GetPlayerID()];
    521         if (g_Disconnected)
    522             gameResult = translate("You have been disconnected.");
    523         else if (playerState.state == "won")
    524             gameResult = translate("You have won the battle!");
    525         else if (playerState.state == "defeated")
    526             gameResult = translate("You have been defeated...");
    527         else // "active"
    528         {
    529             global.music.setState(global.music.states.DEFEAT);
    530             if (willRejoin)
    531                 gameResult = translate("You have left the game.");
    532             else
    533             {
    534                 gameResult = translate("You have abandoned the game.");
    535                 resignGame(true);
    536             }
    537         }
    538     }
    539 
    540     let summary = {
     526    let simData = {
    541527        "timeElapsed" : extendedSimState.timeElapsed,
    542528        "playerStates": extendedSimState.players,
    543         "players": g_Players,
    544         "mapSettings": Engine.GetMapSettings(),
     529        "mapSettings": Engine.GetMapSettings()
    545530    };
    546531
    547532    if (!g_IsReplay)
    548         Engine.SaveReplayMetadata(JSON.stringify(summary));
     533        Engine.SaveReplayMetadata(JSON.stringify(simData));
     534
     535    let guiData = {
     536        "assignedPlayer": Engine.GetPlayerID(),
     537        "disconnected": g_Disconnected,
     538        "isReplay": g_IsReplay,
     539        "replayDirectory": !g_HasRejoined && Engine.GetCurrentReplayDirectory(),
     540        "replaySelectionData": g_ReplaySelectionData,
     541    };
    549542
    550     if (!g_HasRejoined)
    551         summary.replayDirectory = Engine.GetCurrentReplayDirectory();
    552     summary.replaySelectionData = g_ReplaySelectionData;
     543    if (!willRejoin &&
     544        simData[Engine.GetPlayerID()] &&
     545        simData[Engine.GetPlayerID()].state == "active")
     546        resignGame(true);
    553547
    554548    Engine.EndGame();
    555549
    556550    if (g_IsController && Engine.HasXmppClient())
    557551        Engine.SendUnregisterGame();
    558552
    559     summary.gameResult = gameResult;
    560     summary.isReplay = g_IsReplay;
    561     Engine.SwitchGuiPage("page_summary.xml", summary);
     553    Engine.SwitchGuiPage("page_summary.xml", {
     554        "gui": guiData,
     555        "sim": simData
     556    });
    562557}
    563558
    564559// Return some data that we'll use when hotloading this file after changes
    565560function getHotloadData()
    566561{
    function onTick()  
    603598
    604599    let now = new Date();
    605600    let tickLength = new Date() - lastTickTime;
    606601    lastTickTime = now;
    607602
    608     checkPlayerState();
    609 
    610603    handleNetMessages();
    611604
    612605    updateCursorAndTooltip();
    613606
    614607    if (g_Selection.dirty)
    function onTick()  
    630623    Engine.GetGUIObjectByName("resourcePop").textcolor = g_IsTrainingBlocked && Date.now() % 1000 < 500 ? g_PopulationAlertColor : g_DefaultPopulationColor;
    631624
    632625    Engine.GuiInterfaceCall("ClearRenamedEntities");
    633626}
    634627
    635 function checkPlayerState()
    636 {
    637     if (g_GameEnded || Engine.GetPlayerID() < 1)
    638         return;
    639 
    640     // Send a game report for each player in this game.
    641     let m_simState = GetSimState();
    642     let playerState = m_simState.players[Engine.GetPlayerID()];
    643     let tempStates = "";
    644     for (let player of m_simState.players)
    645         tempStates += player.state + ",";
    646 
    647     if (g_CachedLastStates != tempStates)
    648     {
    649         g_CachedLastStates = tempStates;
    650         reportGame();
    651     }
    652 
    653     if (playerState.state == "active")
    654         return;
    655 
    656     // Make sure nothing is open to avoid stacking.
    657     closeOpenDialogs();
    658 
    659     // Make sure this doesn't run again.
    660     g_GameEnded = true;
    661 
    662     // Select observermode
    663     Engine.GetGUIObjectByName("viewPlayer").selected = playerState.state == "won" ? g_ViewedPlayer + 1 : 0;
    664 
    665     let btCaptions;
    666     let btCode;
    667     let message;
    668     let title;
    669     if (Engine.IsAtlasRunning())
    670     {
    671         // If we're in Atlas, we can't leave the game
    672         btCaptions = [translate("OK")];
    673         btCode = [null];
    674         message = translate("Press OK to continue");
    675     }
    676     else
    677     {
    678         btCaptions = [translate("No"), translate("Yes")];
    679         btCode = [null, leaveGame];
    680         message = translate("Do you want to quit?");
    681     }
    682 
    683     if (playerState.state == "defeated")
    684     {
    685         title = translate("DEFEATED!");
    686         global.music.setState(global.music.states.DEFEAT);
    687     }
    688     else if (playerState.state == "won")
    689     {
    690         title = translate("VICTORIOUS!");
    691         global.music.setState(global.music.states.VICTORY);
    692         // TODO: Reveal map directly instead of this silly proxy.
    693         if (!Engine.GetGUIObjectByName("devCommandsRevealMap").checked)
    694             Engine.GetGUIObjectByName("devCommandsRevealMap").checked = true;
    695     }
    696 
    697     messageBox(400, 200, message, title, btCaptions, btCode);
    698 }
    699 
    700628function changeGameSpeed(speed)
    701629{
    702630    if (!g_IsNetworked)
    703631        Engine.SetSimRate(speed);
    704632}
    function onSimulationUpdate()  
    735663
    736664    if (!g_SimState)
    737665        return;
    738666
    739667    handleNotifications();
    740 
    741668    updateGUIObjects();
     669
     670    if (g_ConfirmExit)
     671        confirmExit();
     672}
     673
     674/**
     675 * Don't show the message box before all playerstate changes are processed.
     676 */
     677function confirmExit()
     678{
     679    closeOpenDialogs();
     680
     681    let subject = g_ConfirmExit == "won" ?
     682        translate("You have won!") :
     683        translate("You have been defeated!");
     684
     685    subject += "\n" + translate("Do you want to quit?");
     686
     687    if (g_IsNetworked && g_IsController)
     688        subject += "\n" + translate("Leaving will disconnect all other players.");
     689
     690    messageBox(
     691        400, 200,
     692        subject,
     693        g_ConfirmExit == "won" ?
     694            translate("VICTORIOUS!") :
     695            translate("DEFEATED!"),
     696        [translate("No"), translate("Yes")],
     697        [resumeGame, leaveGame]
     698    );
     699
     700    g_ConfirmExit = false;
    742701}
    743702
    744703function updateGUIObjects()
    745704{
    746705    g_Selection.update();
    function updateGUIObjects()  
    768727        let playerState = GetSimState().players[g_ViewedPlayer];
    769728        g_DevSettings.controlAll = playerState && playerState.controlsAll;
    770729        Engine.GetGUIObjectByName("devControlAll").checked = g_DevSettings.controlAll;
    771730    }
    772731
    773     if (g_ViewedPlayer != -1 && !g_GameEnded)
     732    if (!g_IsObserver)
    774733    {
    775734        // Update music state on basis of battle state.
    776735        let battleState = Engine.GuiInterfaceCall("GetBattleState", g_ViewedPlayer);
    777736        if (battleState)
    778737            global.music.setState(global.music.states[battleState]);
    function updateGUIStatusBar(nameOfBar, p  
    826785        healthSize.rtop = value;
    827786
    828787    statusBar.size = healthSize;
    829788}
    830789
    831 
    832790function updateHeroes()
    833791{
    834792    let playerState = GetSimState().players[g_ViewedPlayer];
    835793    let heroes = playerState ? playerState.heroes : [];
    836794
    function playAmbient()  
    11401098    Engine.PlayAmbientSound(g_Ambient[Math.floor(Math.random() * g_Ambient.length)], true);
    11411099}
    11421100
    11431101function getBuildString()
    11441102{
    1145     return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), { "buildDate": Engine.GetBuildTimestamp(0), revision: Engine.GetBuildTimestamp(2) });
     1103    return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), {
     1104        "buildDate": Engine.GetBuildTimestamp(0),
     1105        "revision": Engine.GetBuildTimestamp(2)
     1106    });
    11461107}
    11471108
    11481109function showTimeWarpMessageBox()
    11491110{
    11501111    messageBox(
    function showTimeWarpMessageBox()  
    11581119 * Send a report on the gamestatus to the lobby.
    11591120 */
    11601121function reportGame()
    11611122{
    11621123    // Only 1v1 games are rated (and Gaia is part of g_Players)
    1163     if (!Engine.HasXmppClient() || !Engine.IsRankedGame() || g_Players.length != 3)
     1124    if (!Engine.HasXmppClient() || !Engine.IsRankedGame() ||
     1125        g_Players.length != 3 || Engine.GetPlayerID() == -1)
    11641126        return;
    11651127
    11661128    let extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState");
    11671129
    11681130    let unitsClasses = [
  • binaries/data/mods/public/gui/session/utility_functions.js

     
     1/**
     2 * Before removing data, consider this data is partially used in the replay menu.
     3 */
    14function getPlayerData(previousData = undefined)
    25{
    36    let players = [];
    47
    58    let simState = GetSimState();
  • binaries/data/mods/public/gui/summary/layout.js

    function updateGeneralPanelTeams()  
    315315    // If there are no players without team, hide "player name" heading
    316316    if (!g_WithoutTeam)
    317317        Engine.GetGUIObjectByName("playerNameHeading").caption = "";
    318318}
    319319
    320 function updateObjectPlayerPosition()
     320function initPlayerBoxPositions()
    321321{
    322322    for (let h = 0; h < g_MaxPlayers; ++h)
    323323    {
    324324        let playerBox = Engine.GetGUIObjectByName("playerBox[" + h + "]");
    325325        let boxSize = playerBox.size;
  • binaries/data/mods/public/gui/summary/summary.js

    function updatePanelData(panelInfo)  
    8080    updateGeneralPanelTeams();
    8181
    8282    let playerBoxesCounts = [ ];
    8383    for (let i = 0; i < g_PlayerCount; ++i)
    8484    {
    85         let playerState = g_GameData.playerStates[i+1];
     85        let playerState = g_GameData.sim.playerStates[i+1];
    8686
    8787        if (!playerBoxesCounts[playerState.team+1])
    8888            playerBoxesCounts[playerState.team+1] = 1;
    8989        else
    9090            playerBoxesCounts[playerState.team+1] += 1;
    function updatePanelData(panelInfo)  
    104104            playerCivicBoxColumn = "civIcont[" + playerState.team + "][" + positionObject + "]";
    105105            playerCounterValue = "valueDataTeam[" + playerState.team + "][" + positionObject + "]";
    106106        }
    107107
    108108        let colorString = "color: " +
    109                 Math.floor(playerState.color.r * 255) + " " +
    110                 Math.floor(playerState.color.g * 255) + " " +
    111                 Math.floor(playerState.color.b * 255);
     109            Math.floor(playerState.color.r * 255) + " " +
     110            Math.floor(playerState.color.g * 255) + " " +
     111            Math.floor(playerState.color.b * 255);
    112112
    113113        let rowPlayerObject = Engine.GetGUIObjectByName(rowPlayer);
    114114        rowPlayerObject.hidden = false;
    115115        rowPlayerObject.sprite = colorString + g_PlayerBoxAlpha;
    116116
    function updatePanelData(panelInfo)  
    119119        rowPlayerObject.size = boxSize;
    120120
    121121        let playerColorBox = Engine.GetGUIObjectByName(playerColorBoxColumn);
    122122        playerColorBox.sprite = colorString + g_PlayerColorBoxAlpha;
    123123
    124         Engine.GetGUIObjectByName(playerNameColumn).caption = g_GameData.players[i+1].name;
     124        Engine.GetGUIObjectByName(playerNameColumn).caption = g_GameData.sim.playerStates[i+1].name;
    125125
    126126        let civIcon = Engine.GetGUIObjectByName(playerCivicBoxColumn);
    127127        civIcon.sprite = "stretched:" + g_CivData[playerState.civ].Emblem;
    128128        civIcon.tooltip = g_CivData[playerState.civ].Name;
    129129
    function updatePanelData(panelInfo)  
    135135    let teamCounterFn = panelInfo.teamCounterFn;
    136136    if (g_Teams && teamCounterFn)
    137137        teamCounterFn(panelInfo.counters);
    138138}
    139139
     140function confirmStartReplay()
     141{
     142    if (Engine.HasXmppClient())
     143        messageBox(
     144            400, 200,
     145            translate("Are you sure you want to quit the lobby?"),
     146            translate("Confirmation"),
     147            [translate("No"), translate("Yes")],
     148            [null, startReplay]
     149        );
     150    else
     151        startReplay();
     152}
     153
     154function continueButton()
     155{
     156    if (g_GameData.gui.isInGame)
     157        Engine.PopGuiPageCB(0);
     158    else if (g_GameData.gui.isReplay)
     159        Engine.SwitchGuiPage("page_replaymenu.xml", {
     160            "replaySelectionData": g_GameData.gui.replaySelectionData
     161        });
     162    else if (Engine.HasXmppClient())
     163        Engine.SwitchGuiPage("page_lobby.xml");
     164    else
     165        Engine.SwitchGuiPage("page_pregame.xml");
     166}
     167
    140168function startReplay()
    141169{
    142170    if (Engine.HasXmppClient())
    143171        Engine.StopXmppClient();
    144172
    145     Engine.StartVisualReplay(g_GameData.replayDirectory);
     173    Engine.StartVisualReplay(g_GameData.gui.replayDirectory);
    146174    Engine.SwitchGuiPage("page_loading.xml", {
    147         "attribs": Engine.GetReplayAttributes(g_GameData.replayDirectory),
     175        "attribs": Engine.GetReplayAttributes(g_GameData.gui.replayDirectory),
    148176        "isNetworked": false,
    149177        "playerAssignments": {
    150178            "local": {
    151179                "name": singleplayerName(),
    152180                "player": -1
    153181            }
    154182        },
    155183        "savedGUIData": "",
    156184        "isReplay": true,
    157         "replaySelectionData": g_GameData.replaySelectionData
     185        "replaySelectionData": g_GameData.gui.replaySelectionData
    158186    });
    159187}
    160188
    161189function init(data)
    162190{
    163     updateObjectPlayerPosition();
    164191    g_GameData = data;
    165192
    166     let mapSize = data.mapSettings.Size && g_Settings.MapSizes.find(size => size.Tiles == data.mapSettings.Size);
    167     let mapType = g_Settings.MapTypes.find(mapType => mapType.Name == data.mapSettings.mapType);
     193    let assignedState = g_GameData.sim.playerStates[g_GameData.gui.assignedPlayer || -1];
    168194   
     195    Engine.GetGUIObjectByName("summaryText").caption =
     196        !assignedState ?
     197            translate("You have left the game.") :
     198        g_GameData.gui.disconnected ?
     199            translate("You have been disconnected.") :
     200        assignedState.state == "won" ?
     201            translate("You have won the battle!") :
     202        assignedState.state == "defeated" ?
     203            translate("You have been defeated...") :
     204            translate("You have abandoned the game.");
     205
     206    initPlayerBoxPositions();
     207
    169208    Engine.GetGUIObjectByName("timeElapsed").caption = sprintf(
    170209        translate("Game time elapsed: %(time)s"), {
    171             "time": timeToString(data.timeElapsed)
     210            "time": timeToString(g_GameData.sim.timeElapsed)
    172211    });
    173212
    174     Engine.GetGUIObjectByName("summaryText").caption = data.gameResult;
     213    let mapType = g_Settings.MapTypes.find(mapType => mapType.Name == g_GameData.sim.mapSettings.mapType);
     214    let mapSize = g_Settings.MapSizes.find(size => size.Tiles == g_GameData.sim.mapSettings.Size || 0);
    175215
    176216    Engine.GetGUIObjectByName("mapName").caption = sprintf(
    177217        translate("%(mapName)s - %(mapType)s"), {
    178             "mapName": translate(data.mapSettings.Name),
     218            "mapName": translate(g_GameData.sim.mapSettings.Name),
    179219            "mapType": mapSize ? mapSize.LongName : (mapType ? mapType.Title : "")
    180220        });
    181221
    182     Engine.GetGUIObjectByName("replayButton").hidden = g_GameData.isInGame || !g_GameData.replayDirectory;
     222    Engine.GetGUIObjectByName("replayButton").hidden = g_GameData.gui.isInGame || !g_GameData.gui.replayDirectory;
    183223
    184224    // Panels
    185     g_PlayerCount = data.playerStates.length - 1;
     225    g_PlayerCount = data.sim.playerStates.length - 1;
    186226
    187     if (data.mapSettings.LockTeams)
     227    if (data.sim.mapSettings.LockTeams)
    188228    {
    189229        // Count teams
    190230        for (let t = 0; t < g_PlayerCount; ++t)
    191231        {
    192             let playerTeam = data.playerStates[t+1].team;
     232            let playerTeam = g_GameData.sim.playerStates[t+1].team;
    193233            g_Teams[playerTeam] = (g_Teams[playerTeam] || 0) + 1;
    194234        }
    195235
    196236        if (g_Teams.length == g_PlayerCount)
    197237            g_Teams = false;    // Each player has his own team. Displaying teams makes no sense.
    function init(data)  
    201241
    202242    // Erase teams data if teams are not displayed
    203243    if (!g_Teams)
    204244    {
    205245        for (let p = 0; p < g_PlayerCount; ++p)
    206             data.playerStates[p+1].team = -1;
     246            g_GameData.sim.playerStates[p+1].team = -1;
    207247    }
    208248
    209249    g_WithoutTeam = g_PlayerCount;
    210250    if (g_Teams)
    211251    {
  • binaries/data/mods/public/gui/summary/summary.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22
    3 <!--
    4 ==========================================
    5 - POST-GAME SUMMARY SCREEN -
    6 ==========================================
    7 -->
    8 
    93<objects>
    104    <script file="gui/common/functions_global_object.js"/>
    115    <script file="gui/common/functions_civinfo.js"/>
    126    <script file="gui/common/functions_utility.js"/>
    137    <script file="gui/common/settings.js"/>
     
    162156            </object>
    163157        </object>
    164158
    165159        <object type="button" name="replayButton" style="ModernButtonRed" size="100%-310 100%-48 100%-170 100%-20">
    166160            <translatableAttribute id="caption">Replay</translatableAttribute>
    167             <action on="Press"><![CDATA[
    168                 if (g_GameData.isInGame)
    169                     return;
    170 
    171                 if (Engine.HasXmppClient())
    172                     messageBox(
    173                         400, 200,
    174                         translate("Are you sure you want to quit the lobby?"),
    175                         translate("Confirmation"),
    176                         [translate("No"), translate("Yes")],
    177                         [null, startReplay]
    178                     );
    179                 else
    180                     startReplay();
    181                 ]]>
    182             </action>
     161            <action on="Press">confirmStartReplay();</action>
    183162        </object>
    184163
    185164        <object type="button" style="ModernButtonRed" size="100%-160 100%-48 100%-20 100%-20">
    186165            <translatableAttribute id="caption">Continue</translatableAttribute>
    187             <action on="Press"><![CDATA[
    188                 if (g_GameData.isInGame)
    189                 {
    190                     Engine.PopGuiPageCB(0);
    191                 }
    192                 else if (g_GameData.isReplay)
    193                 {
    194                     Engine.SwitchGuiPage("page_replaymenu.xml", { "replaySelectionData": g_GameData.replaySelectionData });
    195                 }
    196                 else if (!Engine.HasXmppClient())
    197                 {
    198                     Engine.SwitchGuiPage("page_pregame.xml");
    199                 }
    200                 else
    201                 {
    202                     Engine.LobbySetPlayerPresence("available");
    203                     Engine.SwitchGuiPage("page_lobby.xml");
    204                 }
    205                 ]]>
    206             </action>
     166            <action on="Press">continueButton();</action>
    207167        </object>
    208168    </object>
    209169</objects>
  • binaries/data/mods/public/maps/scripts/ConquestCommon.js

    Trigger.prototype.ConquestHandlerOwnerSh  
    2323    let index = entities.indexOf(msg.entity);
    2424
    2525    if (index >= 0)
    2626    {
    2727        entities.splice(index, 1);
    28         if (!entities.length && Engine.QueryInterface(this.conquestEntitiesByPlayer[msg.from].player, IID_Player).GetState() == "active")
    29             Engine.PostMessage(this.conquestEntitiesByPlayer[msg.from].player, MT_PlayerDefeated, { "playerId": msg.from } );
     28        if (!entities.length)
     29        {
     30            let cmpPlayer = QueryPlayerIDInterface(msg.from);
     31            if (cmpPlayer)
     32                cmpPlayer.SetState("defeated");
     33        }
    3034    }
    3135}
    3236
    3337Trigger.prototype.ConquestAddStructure = function(msg)
    3438{
  • binaries/data/mods/public/maps/scripts/TriggerHelper.js

    TriggerHelper.GetResourceType = function  
    112112
    113113    return cmpResourceSupply.GetType();
    114114};
    115115
    116116/**
    117  * Wins the game for a player
     117 * The given player will win the game.
     118 * If it's not a last man standing game, the allies will win too.
    118119 */
    119120TriggerHelper.SetPlayerWon = function(playerID)
    120121{
    121122    let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
    122123    cmpEndGameManager.MarkPlayerAsWon(playerID);
    TriggerHelper.SetPlayerWon = function(pl  
    125126/**
    126127 * Defeats a player
    127128 */
    128129TriggerHelper.DefeatPlayer = function(playerID)
    129130{
    130     let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    131     let playerEnt = cmpPlayerManager.GetPlayerByID(playerID);
    132 
    133     Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": playerID } );
     131    let cmpPlayer = QueryPlayerIDInterface(playerID);
     132    if (cmpPlayer)
     133        cmpPlayer.SetState("defeated");
    134134};
    135135
    136136/**
    137137 * Returns the number of current players
    138138 */
  • binaries/data/mods/public/simulation/components/EndGameManager.js

     
    11/**
    2  * System component which regularly checks victory/defeat conditions
    3  * and if they are satisfied then it marks the player as victorious/defeated.
     2 * System component to store the gametype, gametype settings and
     3 * check for allied victory / last-man-standing.
    44 */
    55function EndGameManager() {}
    66
    77EndGameManager.prototype.Schema =
    88    "<a:component type='system'/><empty/>";
    EndGameManager.prototype.Init = function  
    1717
    1818    // Allied victory means allied players can win if victory conditions are met for each of them
    1919    // False for a "last man standing" game
    2020    this.alliedVictory = true;
    2121
     22    // Don't do any checks before the diplomacies were set for each player
     23    // or when marking a player as won.
     24    this.skipAlliedVictoryCheck = true;
     25
    2226    this.lastManStandingMessage = undefined;
    2327};
    2428
    2529EndGameManager.prototype.GetGameType = function()
    2630{
    EndGameManager.prototype.CheckGameType =  
    3943
    4044EndGameManager.prototype.SetGameType = function(newGameType, newSettings = {})
    4145{
    4246    this.gameType = newGameType;
    4347    this.gameTypeSettings = newSettings;
     48    this.skipAlliedVictoryCheck = false;
    4449
    4550    Engine.BroadcastMessage(MT_GameTypeChanged, {});
    4651};
    4752
    4853EndGameManager.prototype.MarkPlayerAsWon = function(playerID)
    4954{
    5055    let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    5156    let numPlayers = cmpPlayerManager.GetNumPlayers();
    5257
    53     for (let i = 1; i < numPlayers; ++i)
    54     {
    55         let playerEntityId = cmpPlayerManager.GetPlayerByID(i);
    56         let cmpPlayer = Engine.QueryInterface(playerEntityId, IID_Player);
     58    this.skipAlliedVictoryCheck = true;
    5759
    58         if (cmpPlayer.GetState() != "active")
    59             continue;
     60    // Group win/defeat messages
     61    for (let won of [false, true])
     62        for (let i = 1; i < numPlayers; ++i)
     63        {
     64            let cmpPlayer = QueryPlayerIDInterface(i);
     65            let hasWon = playerID == i || this.alliedVictory && cmpPlayer.IsMutualAlly(playerID);
    6066
    61         if (playerID == cmpPlayer.GetPlayerID() || this.alliedVictory && cmpPlayer.IsMutualAlly(playerID))
    62             cmpPlayer.SetState("won");
    63         else
    64             Engine.PostMessage(playerEntityId, MT_PlayerDefeated, {
    65                 "playerId": i,
    66                 "skipAlliedVictoryCheck": true
    67             });
    68     }
     67            if (hasWon == won)
     68                cmpPlayer.SetState(won ? "won" : "defeated");
     69        }
    6970
    70     // Reveal the map to all players
    71     let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    72     cmpRangeManager.SetLosRevealAll(-1, true);
     71    this.skipAlliedVictoryCheck = false;
    7372};
    7473
    7574EndGameManager.prototype.SetAlliedVictory = function(flag)
    7675{
    7776    this.alliedVictory = flag;
    7877};
    7978
    8079EndGameManager.prototype.AlliedVictoryCheck = function()
    8180{
     81    if (this.skipAlliedVictoryCheck)
     82        return;
     83
    8284    let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    8385    let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    8486    if (!cmpGuiInterface || !cmpPlayerManager)
    8587        return;
    8688
    EndGameManager.prototype.AlliedVictoryCh  
    99101
    100102        allies.push(playerID);
    101103    }
    102104
    103105    if (this.alliedVictory || allies.length == 1)
    104     {
    105106        for (let playerID of allies)
    106107        {
    107108            let cmpPlayer = QueryPlayerIDInterface(playerID);
    108109            if (cmpPlayer)
    109110                cmpPlayer.SetState("won");
    110111        }
    111 
    112         let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    113         if (cmpRangeManager)
    114             cmpRangeManager.SetLosRevealAll(-1, true);
    115     }
    116112    else
    117113        this.lastManStandingMessage = cmpGuiInterface.AddTimeNotification({
    118114            "message": markForTranslation("Last remaining player wins."),
    119115            "translateMessage": true,
    120116        }, 12 * 60 * 60 * 1000); // 12 hours
    EndGameManager.prototype.OnInitGame = fu  
    125121    this.AlliedVictoryCheck();
    126122};
    127123
    128124EndGameManager.prototype.OnGlobalDiplomacyChanged = function(msg)
    129125{
    130     if (!msg.skipAlliedVictoryCheck)
    131         this.AlliedVictoryCheck();
     126    this.AlliedVictoryCheck();
    132127};
    133128
    134129EndGameManager.prototype.OnGlobalPlayerDefeated = function(msg)
    135130{
    136     if (!msg.skipAlliedVictoryCheck)
    137         this.AlliedVictoryCheck();
     131    this.AlliedVictoryCheck();
    138132};
    139133
    140134Engine.RegisterSystemComponentType(IID_EndGameManager, "EndGameManager", EndGameManager);
  • binaries/data/mods/public/simulation/components/Player.js

    Player.prototype.SetTradingGoods = funct  
    357357Player.prototype.GetState = function()
    358358{
    359359    return this.state;
    360360};
    361361
    362 Player.prototype.SetState = function(newState)
     362Player.prototype.SetState = function(newState, resign)
    363363{
     364    if (this.state != "active")
     365        return;
     366
     367    if (newState != "won" && newState != "defeated")
     368    {
     369        warn("Can't change playerstate to " + this.state);
     370        return;
     371    }
     372
    364373    this.state = newState;
     374
     375    let won = newState == "won";
     376
     377    // Reveal map to all or only that player
     378    let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     379    cmpRangeManager.SetLosRevealAll(won ? -1 : this.playerID, true);
     380
     381    if (!won)
     382    {
     383        // Reassign all player's entities to Gaia
     384        let entities = cmpRangeManager.GetEntitiesByPlayer(this.playerID);
     385
     386        // The ownership change is done in two steps so that entities don't hit idle
     387        // (and thus possibly look for "enemies" to attack) before nearby allies get
     388        // converted to Gaia as well.
     389        for (let entity of entities)
     390        {
     391            let cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
     392            cmpOwnership.SetOwnerQuiet(0);
     393        }
     394
     395        // With the real ownership change complete, send OwnershipChanged messages.
     396        for (let entity of entities)
     397            Engine.PostMessage(entity, MT_OwnershipChanged, {
     398                "entity": entity,
     399                "from": this.playerID,
     400                "to": 0
     401            });
     402    }
     403
     404    Engine.BroadcastMessage(won ? MT_PlayerWon : MT_PlayerDefeated, { "playerId": this.playerID });
     405
     406    let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     407    if (won)
     408        cmpGUIInterface.PushNotification({
     409            "type": "won",
     410            "players": [this.playerID]
     411        });
     412    else
     413        cmpGUIInterface.PushNotification({
     414            "type": "defeat",
     415            "players": [this.playerID],
     416            "resign": resign
     417        });
    365418};
    366419
    367420Player.prototype.GetTeam = function()
    368421{
    369422    return this.team;
    370423};
    371424
    372 Player.prototype.SetTeam = function(team, skipAlliedVictoryCheck = false)
     425Player.prototype.SetTeam = function(team)
    373426{
    374427    if (this.teamsLocked)
    375428        return;
    376429
    377430    this.team = team;
    Player.prototype.SetTeam = function(team  
    383436        {
    384437            let cmpPlayer = QueryPlayerIDInterface(i);
    385438            if (this.team != cmpPlayer.GetTeam())
    386439                continue;
    387440
    388             this.SetAlly(i, skipAlliedVictoryCheck);
    389             cmpPlayer.SetAlly(this.playerID, skipAlliedVictoryCheck);
     441            this.SetAlly(i);
     442            cmpPlayer.SetAlly(this.playerID);
    390443        }
    391444
    392445    Engine.BroadcastMessage(MT_DiplomacyChanged, {
    393446        "player": this.playerID,
    394         "otherPlayer": null,
    395         "skipAlliedVictoryCheck": skipAlliedVictoryCheck
     447        "otherPlayer": null
    396448    });
    397449};
    398450
    399451Player.prototype.SetLockTeams = function(value)
    400452{
    Player.prototype.GetLockTeams = function  
    409461Player.prototype.GetDiplomacy = function()
    410462{
    411463    return this.diplomacy;
    412464};
    413465
    414 Player.prototype.SetDiplomacy = function(dipl, skipAlliedVictoryCheck = false)
     466Player.prototype.SetDiplomacy = function(dipl)
    415467{
    416468    this.diplomacy = dipl;
    417469
    418470    Engine.BroadcastMessage(MT_DiplomacyChanged, {
    419471        "player": this.playerID,
    420         "otherPlayer": null,
    421         "skipAlliedVictoryCheck": skipAlliedVictoryCheck
     472        "otherPlayer": null
    422473    });
    423474};
    424475
    425 Player.prototype.SetDiplomacyIndex = function(idx, value, skipAlliedVictoryCheck = false)
     476Player.prototype.SetDiplomacyIndex = function(idx, value)
    426477{
    427478    let cmpPlayer = QueryPlayerIDInterface(idx);
    428479    if (!cmpPlayer)
    429480        return;
    430481
    Player.prototype.SetDiplomacyIndex = fun  
    433484
    434485    this.diplomacy[idx] = value;
    435486
    436487    Engine.BroadcastMessage(MT_DiplomacyChanged, {
    437488        "player": this.playerID,
    438         "otherPlayer": cmpPlayer.GetPlayerID(),
    439         "skipAlliedVictoryCheck": skipAlliedVictoryCheck
     489        "otherPlayer": cmpPlayer.GetPlayerID()
    440490    });
    441491
    442492    // Mutual worsening of relations
    443493    if (cmpPlayer.diplomacy[this.playerID] > value)
    444         cmpPlayer.SetDiplomacyIndex(this.playerID, value, skipAlliedVictoryCheck);
     494        cmpPlayer.SetDiplomacyIndex(this.playerID, value);
    445495};
    446496
    447497Player.prototype.UpdateSharedLos = function()
    448498{
    449499    let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    Player.prototype.SetAI = function(flag)  
    518568Player.prototype.IsAI = function()
    519569{
    520570    return this.isAI;
    521571};
    522572
    523 Player.prototype.SetAlly = function(id, skipAlliedVictoryCheck = false)
     573Player.prototype.SetAlly = function(id)
    524574{
    525     this.SetDiplomacyIndex(id, 1, skipAlliedVictoryCheck);
     575    this.SetDiplomacyIndex(id, 1);
    526576};
    527577
    528578/**
    529579 * Check if given player is our ally
    530580 */
    Player.prototype.IsMutualAlly = function  
    556606Player.prototype.IsExclusiveMutualAlly = function(id)
    557607{
    558608    return this.playerID != id && this.IsMutualAlly(id);
    559609};
    560610
    561 Player.prototype.SetEnemy = function(id, skipAlliedVictoryCheck = false)
     611Player.prototype.SetEnemy = function(id)
    562612{
    563     this.SetDiplomacyIndex(id, -1, skipAlliedVictoryCheck);
     613    this.SetDiplomacyIndex(id, -1);
    564614};
    565615
    566616/**
    567617 * Get all enemies of a given player.
    568618 */
    Player.prototype.GetEnemies = function()  
    581631Player.prototype.IsEnemy = function(id)
    582632{
    583633    return this.diplomacy[id] < 0;
    584634};
    585635
    586 Player.prototype.SetNeutral = function(id, skipAlliedVictoryCheck = false)
     636Player.prototype.SetNeutral = function(id)
    587637{
    588     this.SetDiplomacyIndex(id, 0, skipAlliedVictoryCheck);
     638    this.SetDiplomacyIndex(id, 0);
    589639};
    590640
    591641/**
    592642 * Check if given player is neutral
    593643 */
    Player.prototype.OnGlobalOwnershipChange  
    647697        if (cmpIdentity && cmpIdentity.HasClass("Hero"))
    648698            this.heroes.push(msg.entity);
    649699    }
    650700};
    651701
    652 Player.prototype.OnPlayerDefeated = function(msg)
    653 {
    654     this.state = "defeated";
    655 
    656     // Reassign all player's entities to Gaia
    657     var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    658     var entities = cmpRangeManager.GetEntitiesByPlayer(this.playerID);
    659 
    660     // The ownership change is done in two steps so that entities don't hit idle
    661     // (and thus possibly look for "enemies" to attack) before nearby allies get
    662     // converted to Gaia as well.
    663     for (var entity of entities)
    664     {
    665         var cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
    666         cmpOwnership.SetOwnerQuiet(0);
    667     }
    668 
    669     // With the real ownership change complete, send OwnershipChanged messages.
    670     for (var entity of entities)
    671         Engine.PostMessage(entity, MT_OwnershipChanged, {
    672             "entity": entity,
    673             "from": this.playerID,
    674             "to": 0
    675         });
    676 
    677     // Reveal the map for this player.
    678     cmpRangeManager.SetLosRevealAll(this.playerID, true);
    679 
    680     // Send a chat message notifying of the player's defeat.
    681     var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    682     cmpGUIInterface.PushNotification({
    683         "type": "defeat",
    684         "players": [this.playerID],
    685         "resign": !!msg.resign
    686     });
    687 };
    688 
    689702Player.prototype.OnResearchFinished = function(msg)
    690703{
    691704    if (msg.tech == this.template.SharedLosTech)
    692705        this.UpdateSharedLos();
    693706    else if (msg.tech == this.template.SharedDropsitesTech)
  • binaries/data/mods/public/simulation/components/PlayerManager.js

    PlayerManager.prototype.AddPlayer = func  
    2121        var cmpOtherPlayer = Engine.QueryInterface(this.GetPlayerByID(i), IID_Player);
    2222        cmpOtherPlayer.diplomacy[id] = -1;
    2323        newDiplo[i] = -1;
    2424    }
    2525    newDiplo[id] = 1;
    26     cmpPlayer.SetDiplomacy(newDiplo, true);
     26    cmpPlayer.SetDiplomacy(newDiplo);
    2727   
    2828    return id;
    2929};
    3030
    3131/**
  • binaries/data/mods/public/simulation/components/interfaces/EndGameManager.js

     
    11Engine.RegisterInterface("EndGameManager");
    22Engine.RegisterMessageType("PlayerDefeated");
     3Engine.RegisterMessageType("PlayerWon");
    34Engine.RegisterMessageType("GameTypeChanged");
  • binaries/data/mods/public/simulation/helpers/Cheat.js

    function Cheat(input)  
    4646            else
    4747                Engine.DestroyEntity(ent);
    4848        }
    4949        return;
    5050    case "defeatplayer":
    51         var playerEnt = cmpPlayerManager.GetPlayerByID(input.parameter);
    52         if (playerEnt == INVALID_ENTITY)
    53             return;
    54         Engine.PostMessage(playerEnt, MT_PlayerDefeated, { "playerId": input.parameter });
     51        cmpPlayer = QueryPlayerIDInterface(input.parameter);
     52        if (cmpPlayer)
     53            cmpPlayer.SetState("defeated");
    5554        return;
    5655    case "createunits":
    5756        var cmpProductionQueue = input.selected.length && Engine.QueryInterface(input.selected[0], IID_ProductionQueue);
    5857        if (!cmpProductionQueue)
    5958        {
  • binaries/data/mods/public/simulation/helpers/Commands.js

    var g_Commands = {  
    412412        }
    413413    },
    414414
    415415    "defeat-player": function(player, cmd, data)
    416416    {
    417         // Send "OnPlayerDefeated" message to player
    418         Engine.PostMessage(data.playerEnt, MT_PlayerDefeated, { "playerId": player, "resign": !!cmd.resign });
     417        let cmpPlayer = QueryPlayerIDInterface(player);
     418        if (cmpPlayer)
     419            cmpPlayer.SetState("defeated", !!cmd.resign);
    419420    },
    420421
    421422    "garrison": function(player, cmd, data)
    422423    {
    423424        // Verify that the building can be controlled by the player or is mutualAlly
  • binaries/data/mods/public/simulation/helpers/Player.js

    function LoadPlayerSettings(settings, ne  
    121121        if (disabledTemplates.length)
    122122            cmpPlayer.SetDisabledTemplates(disabledTemplates);
    123123
    124124        // If diplomacy explicitly defined, use that; otherwise use teams
    125125        if (getSetting(playerData, playerDefaults, i, "Diplomacy") !== undefined)
    126             cmpPlayer.SetDiplomacy(getSetting(playerData, playerDefaults, i, "Diplomacy"), true);
     126            cmpPlayer.SetDiplomacy(getSetting(playerData, playerDefaults, i, "Diplomacy"));
    127127        else
    128128        {
    129129            // Init diplomacy
    130130            var myTeam = getSetting(playerData, playerDefaults, i, "Team");
    131131
    function LoadPlayerSettings(settings, ne  
    135135                if (i == j)
    136136                    cmpPlayer.SetAlly(j);
    137137                else
    138138                    cmpPlayer.SetEnemy(j);
    139139            }
    140             cmpPlayer.SetTeam(myTeam === undefined ? -1 : myTeam, true);
     140            cmpPlayer.SetTeam(myTeam === undefined ? -1 : myTeam);
    141141        }
    142142
    143143        // If formations explicitly defined, use that; otherwise use civ defaults
    144144        var formations = getSetting(playerData, playerDefaults, i, "Formations");
    145145        if (formations !== undefined)