0ad/binaries/data/mods/public/simulation/components/EndGameManager.js
Angen 25e8b38ba8 Fix message on allied victory with no active players
When starting a game with exactly one player that instantly wins the
game (consequest victory) and after that ceasefire running out will
trigger first the correct victory chat message, then a broken and
undefined have won the game chat message, initiated from the
ceasefiremanager.

Check for allies emptyness and do not send it if no players are active,
what means they all have been defeated or won.

Differential Revision: https://code.wildfiregames.com/D2891
Fixes: #5675

This was SVN commit r23857.
2020-07-20 17:10:08 +00:00

202 lines
5.7 KiB
JavaScript

/**
* System component to store the victory conditions and their settings and
* check for allied victory / last-man-standing.
*/
function EndGameManager() {}
EndGameManager.prototype.Schema =
"<a:component type='system'/><empty/>";
EndGameManager.prototype.Init = function()
{
// Contains settings specific to the victory condition,
// for example wonder victory duration.
this.gameSettings = {};
// Allied victory means allied players can win if victory conditions are met for each of them
// False for a "last man standing" game
this.alliedVictory = true;
// Don't do any checks before the diplomacies were set for each player
// or when marking a player as won.
this.skipAlliedVictoryCheck = true;
this.lastManStandingMessage = undefined;
this.endlessGame = false;
};
EndGameManager.prototype.GetGameSettings = function()
{
return this.gameSettings;
};
EndGameManager.prototype.GetVictoryConditions = function()
{
return this.gameSettings.victoryConditions;
};
EndGameManager.prototype.SetGameSettings = function(newSettings = {})
{
this.gameSettings = newSettings;
this.skipAlliedVictoryCheck = false;
this.endlessGame = !this.gameSettings.victoryConditions.length;
Engine.BroadcastMessage(MT_VictoryConditionsChanged, {});
};
/**
* Sets the given player (and the allies if allied victory is enabled) as a winner.
*
* @param {number} playerID - The player that should win.
* @param {function} victoryReason - Function that maps from number to plural string, for example
* n => markForPluralTranslation(
* "%(lastPlayer)s has won (game mode).",
* "%(players)s and %(lastPlayer)s have won (game mode).",
* n));
*/
EndGameManager.prototype.MarkPlayerAndAlliesAsWon = function(playerID, victoryString, defeatString)
{
let state = QueryPlayerIDInterface(playerID).GetState();
if (state != "active")
{
warn("Can't mark player " + playerID + " as won, since the state is " + state);
return;
}
let winningPlayers = [playerID];
if (this.alliedVictory)
winningPlayers = QueryPlayerIDInterface(playerID).GetMutualAllies(playerID).filter(
player => QueryPlayerIDInterface(player).GetState() == "active");
this.MarkPlayersAsWon(winningPlayers, victoryString, defeatString);
};
/**
* Sets the given players as won and others as defeated.
*
* @param {array} winningPlayers - The players that should win.
* @param {function} victoryReason - Function that maps from number to plural string, for example
* n => markForPluralTranslation(
* "%(lastPlayer)s has won (game mode).",
* "%(players)s and %(lastPlayer)s have won (game mode).",
* n));
*/
EndGameManager.prototype.MarkPlayersAsWon = function(winningPlayers, victoryString, defeatString)
{
this.skipAlliedVictoryCheck = true;
for (let playerID of winningPlayers)
{
let cmpPlayer = QueryPlayerIDInterface(playerID);
let state = cmpPlayer.GetState();
if (state != "active")
{
warn("Can't mark player " + playerID + " as won, since the state is " + state);
continue;
}
cmpPlayer.SetState("won", undefined);
}
let defeatedPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetActivePlayers().filter(
playerID => winningPlayers.indexOf(playerID) == -1);
for (let playerID of defeatedPlayers)
QueryPlayerIDInterface(playerID).SetState("defeated", undefined);
let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification({
"type": "won",
"players": [winningPlayers[0]],
"allies" : winningPlayers,
"message": victoryString(winningPlayers.length)
});
if (defeatedPlayers.length)
cmpGUIInterface.PushNotification({
"type": "defeat",
"players": [defeatedPlayers[0]],
"allies" : defeatedPlayers,
"message": defeatString(defeatedPlayers.length)
});
this.skipAlliedVictoryCheck = false;
};
EndGameManager.prototype.SetAlliedVictory = function(flag)
{
this.alliedVictory = flag;
};
EndGameManager.prototype.GetAlliedVictory = function()
{
return this.alliedVictory;
};
EndGameManager.prototype.AlliedVictoryCheck = function()
{
if (this.skipAlliedVictoryCheck || this.endlessGame)
return;
let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGuiInterface.DeleteTimeNotification(this.lastManStandingMessage);
// Proceed if only allies are remaining
let allies = [];
let numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
for (let playerID = 1; playerID < numPlayers; ++playerID)
{
let cmpPlayer = QueryPlayerIDInterface(playerID);
if (cmpPlayer.GetState() != "active")
continue;
if (allies.length && !cmpPlayer.IsMutualAlly(allies[0]))
return;
allies.push(playerID);
}
if (!allies.length)
return;
if (this.alliedVictory || allies.length == 1)
{
for (let playerID of allies)
{
let cmpPlayer = QueryPlayerIDInterface(playerID);
if (cmpPlayer)
cmpPlayer.SetState("won", undefined);
}
cmpGuiInterface.PushNotification({
"type": "won",
"players": [allies[0]],
"allies" : allies,
"message": markForPluralTranslation(
"%(lastPlayer)s has won (last player alive).",
"%(players)s and %(lastPlayer)s have won (last players alive).",
allies.length)
});
}
else
this.lastManStandingMessage = cmpGuiInterface.AddTimeNotification({
"message": markForTranslation("Last remaining player wins."),
"translateMessage": true,
}, 12 * 60 * 60 * 1000); // 12 hours
};
EndGameManager.prototype.OnInitGame = function(msg)
{
this.AlliedVictoryCheck();
};
EndGameManager.prototype.OnGlobalDiplomacyChanged = function(msg)
{
this.AlliedVictoryCheck();
};
EndGameManager.prototype.OnGlobalPlayerDefeated = function(msg)
{
this.AlliedVictoryCheck();
};
Engine.RegisterSystemComponentType(IID_EndGameManager, "EndGameManager", EndGameManager);