mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-06-21 13:04:10 +00:00
Victory and defeat reason strings.
Clarify the victory / defeat reason by using a custom string for each defeat and victory reason. Group chat notifications instead of posting one for each player. This also slightly improves lobby performance upon win. Differential Revision: https://code.wildfiregames.com/D762 Fixes #4382 Based on patch by: Angen This was SVN commit r19955.
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
{"nick": "aBothe", "name": "Alexander Bothe"},
|
||||
{"nick": "alpha123", "name": "Peter P. Cannici"},
|
||||
{"nick": "andy5995", "name": "Andy Alt"},
|
||||
{"nick": "Angen"},
|
||||
{"nick": "ArnH", "name": "Arno Hemelhof"},
|
||||
{"nick": "Aurium", "name": "Aurélio Heckert"},
|
||||
{"nick": "badmadblacksad", "name": "Martin F"},
|
||||
|
||||
@@ -126,8 +126,7 @@ var g_FormatChatMessage = {
|
||||
),
|
||||
"clientlist": msg => getUsernameList(),
|
||||
"message": msg => formatChatCommand(msg),
|
||||
"defeat": msg => formatDefeatMessage(msg),
|
||||
"won": msg => formatWinMessage(msg),
|
||||
"defeat-victory": msg => formatDefeatVictoryMessage(msg.message, msg.players),
|
||||
"diplomacy": msg => formatDiplomacyMessage(msg),
|
||||
"tribute": msg => formatTributeMessage(msg),
|
||||
"barter": msg => formatBarterMessage(msg),
|
||||
@@ -315,24 +314,11 @@ var g_NotificationsTypes =
|
||||
},
|
||||
"defeat": function(notification, player)
|
||||
{
|
||||
addChatMessage({
|
||||
"type": "defeat",
|
||||
"guid": findGuidForPlayerID(player),
|
||||
"player": player,
|
||||
"resign": !!notification.resign
|
||||
});
|
||||
playerFinished(player, false);
|
||||
sendLobbyPlayerlistUpdate();
|
||||
playersFinished(notification.allies, notification.message, false);
|
||||
},
|
||||
"won": function(notification, player)
|
||||
{
|
||||
addChatMessage({
|
||||
"type": "won",
|
||||
"guid": findGuidForPlayerID(player),
|
||||
"player": player
|
||||
});
|
||||
playerFinished(player, true);
|
||||
sendLobbyPlayerlistUpdate();
|
||||
playersFinished(notification.allies, notification.message, true);
|
||||
},
|
||||
"diplomacy": function(notification, player)
|
||||
{
|
||||
@@ -966,20 +952,20 @@ function colorizePlayernameParameters(parameters)
|
||||
parameters[param] = colorizePlayernameByID(parameters[param]);
|
||||
}
|
||||
|
||||
function formatDefeatMessage(msg)
|
||||
function formatDefeatVictoryMessage(message, players)
|
||||
{
|
||||
return sprintf(
|
||||
msg.resign ?
|
||||
translate("%(player)s has resigned.") :
|
||||
translate("%(player)s has been defeated."),
|
||||
{ "player": colorizePlayernameByID(msg.player) }
|
||||
);
|
||||
}
|
||||
if (!message.pluralMessage)
|
||||
return sprintf(translate(message), {
|
||||
"player": colorizePlayernameByID(players[0])
|
||||
});
|
||||
|
||||
function formatWinMessage(msg)
|
||||
{
|
||||
return sprintf(translate("%(player)s has won."), {
|
||||
"player": colorizePlayernameByID(msg.player)
|
||||
let mPlayers = players.map(playerID => colorizePlayernameByID(playerID));
|
||||
let lastPlayer = mPlayers.pop();
|
||||
|
||||
return sprintf(translatePlural(message.message, message.pluralMessage, message.pluralCount), {
|
||||
// Translation: This comma is used for separating first to penultimate elements in an enumeration.
|
||||
"players": mPlayers.join(translate(", ")),
|
||||
"lastPlayer": lastPlayer
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -546,23 +546,35 @@ function controlsPlayer(playerID)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a player has won or was defeated.
|
||||
* Called when one or more players have won or were defeated.
|
||||
*
|
||||
* @param {array} - IDs of the players who have won or were defeated.
|
||||
* @param {object} - a plural string stating the victory reason.
|
||||
* @param {boolean} - whether these players have won or lost.
|
||||
*/
|
||||
function playerFinished(player, won)
|
||||
function playersFinished(players, victoryString, won)
|
||||
{
|
||||
if (player == Engine.GetPlayerID())
|
||||
addChatMessage({
|
||||
"type": "defeat-victory",
|
||||
"message": victoryString,
|
||||
"players": players
|
||||
});
|
||||
|
||||
if (players.indexOf(Engine.GetPlayerID()) != -1)
|
||||
reportGame();
|
||||
|
||||
sendLobbyPlayerlistUpdate();
|
||||
|
||||
updatePlayerData();
|
||||
updateChatAddressees();
|
||||
|
||||
if (player != g_ViewedPlayer)
|
||||
if (players.indexOf(g_ViewedPlayer) == -1)
|
||||
return;
|
||||
|
||||
// Select "observer" item on loss. On win enable observermode without changing perspective
|
||||
Engine.GetGUIObjectByName("viewPlayer").selected = won ? g_ViewedPlayer + 1 : 0;
|
||||
|
||||
if (player != Engine.GetPlayerID() || Engine.IsAtlasRunning())
|
||||
if (players.indexOf(Engine.GetPlayerID()) == -1 || Engine.IsAtlasRunning())
|
||||
return;
|
||||
|
||||
global.music.setState(
|
||||
@@ -657,9 +669,7 @@ function resignGame(leaveGameAfterResign)
|
||||
return;
|
||||
|
||||
Engine.PostNetworkCommand({
|
||||
"type": "defeat-player",
|
||||
"playerId": Engine.GetPlayerID(),
|
||||
"resign": true
|
||||
"type": "resign"
|
||||
});
|
||||
|
||||
if (!leaveGameAfterResign)
|
||||
|
||||
@@ -345,7 +345,10 @@ Trigger.prototype.OnOwnershipChanged = function(data)
|
||||
if (data.entity == this.playerCivicCenter[data.from])
|
||||
{
|
||||
this.playerCivicCenter[data.from] = undefined;
|
||||
TriggerHelper.DefeatPlayer(data.from);
|
||||
|
||||
TriggerHelper.DefeatPlayer(
|
||||
data.from,
|
||||
markForTranslation("%(player)s has been defeated (lost civic center)."));
|
||||
}
|
||||
else if (data.entity == this.treasureFemale[data.from])
|
||||
{
|
||||
|
||||
@@ -96,7 +96,16 @@ Trigger.prototype.BattleMessage = function()
|
||||
|
||||
Trigger.prototype.Victory = function(playerID)
|
||||
{
|
||||
TriggerHelper.SetPlayerWon(playerID);
|
||||
TriggerHelper.SetPlayerWon(
|
||||
playerID,
|
||||
n => markForPluralTranslation(
|
||||
"%(lastPlayer)s has won (treasure collected).",
|
||||
"%(players)s and %(lastPlayer)s have won (treasure collected).",
|
||||
n),
|
||||
n => markForPluralTranslation(
|
||||
"%(lastPlayer)s has been defeated (treasure collected).",
|
||||
"%(players)s and %(lastPlayer)s have been defeated (treasure collected).",
|
||||
n));
|
||||
};
|
||||
|
||||
var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
|
||||
@@ -145,8 +145,23 @@ Trigger.prototype.StartCaptureTheRelicCountdown = function(playerAndAllies)
|
||||
"translateMessage": true
|
||||
}, captureTheRelicDuration);
|
||||
|
||||
this.relicsVictoryTimer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager,
|
||||
"MarkPlayerAsWon", captureTheRelicDuration, playerAndAllies[0]);
|
||||
this.relicsVictoryTimer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger,
|
||||
"CaptureTheRelicVictorySetWinner", captureTheRelicDuration, playerAndAllies[0]);
|
||||
};
|
||||
|
||||
Trigger.prototype.CaptureTheRelicVictorySetWinner = function(playerID)
|
||||
{
|
||||
let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
|
||||
cmpEndGameManager.MarkPlayerAsWon(
|
||||
playerID,
|
||||
n => markForPluralTranslation(
|
||||
"%(lastPlayer)s has won (Capture the Relic).",
|
||||
"%(players)s and %(lastPlayer)s have won (Capture the Relic).",
|
||||
n),
|
||||
n => markForPluralTranslation(
|
||||
"%(lastPlayer)s has been defeated (Capture the Relic).",
|
||||
"%(players)s and %(lastPlayer)s have been defeated (Capture the Relic).",
|
||||
n));
|
||||
};
|
||||
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.conquestClassFilter = "ConquestCritical";
|
||||
cmpTrigger.conquestDefeatReason = markForTranslation("%(player)s has been defeated (lost all workers and structures).");
|
||||
|
||||
let data = { "enabled": true };
|
||||
cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data);
|
||||
|
||||
@@ -29,7 +29,7 @@ Trigger.prototype.ConquestHandlerOwnerShipChanged = function(msg)
|
||||
{
|
||||
let cmpPlayer = QueryPlayerIDInterface(msg.from);
|
||||
if (cmpPlayer)
|
||||
cmpPlayer.SetState("defeated");
|
||||
cmpPlayer.SetState("defeated", this.conquestDefeatReason);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.conquestClassFilter = "Structure";
|
||||
cmpTrigger.conquestDefeatReason = markForTranslation("%(player)s has been defeated (lost all structures).");
|
||||
|
||||
let data = { "enabled": true };
|
||||
cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.conquestClassFilter = "Unit";
|
||||
cmpTrigger.conquestDefeatReason = markForTranslation("%(player)s has been defeated (lost all workers).");
|
||||
|
||||
let data = { "enabled": true };
|
||||
cmpTrigger.RegisterTrigger("OnOwnershipChanged", "ConquestHandlerOwnerShipChanged", data);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
Trigger.prototype.CheckRegicideDefeat = function(data)
|
||||
{
|
||||
if (data.entity == this.regicideHeroes[data.from])
|
||||
TriggerHelper.DefeatPlayer(data.from);
|
||||
TriggerHelper.DefeatPlayer(
|
||||
data.from,
|
||||
markForTranslation("%(player)s has been defeated (lost hero)."));
|
||||
};
|
||||
|
||||
Trigger.prototype.InitRegicideGame = function(msg)
|
||||
|
||||
@@ -116,22 +116,34 @@ TriggerHelper.GetResourceType = function(entity)
|
||||
|
||||
/**
|
||||
* The given player will win the game.
|
||||
* If it's not a last man standing game, then allies will win too.
|
||||
* If it's not a last man standing game, then allies will win too and others will be defeated.
|
||||
*
|
||||
* @param {number} playerID - The player who will 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));
|
||||
* It's a function since we don't know in advance how many players will have won.
|
||||
*/
|
||||
TriggerHelper.SetPlayerWon = function(playerID)
|
||||
TriggerHelper.SetPlayerWon = function(playerID, victoryReason, defeatReason)
|
||||
{
|
||||
let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
|
||||
cmpEndGameManager.MarkPlayerAsWon(playerID);
|
||||
cmpEndGameManager.MarkPlayerAsWon(playerID, victoryReason, defeatReason);
|
||||
};
|
||||
|
||||
/**
|
||||
* Defeats a player
|
||||
* Defeats a single player.
|
||||
*
|
||||
* @param {number} - ID of that player.
|
||||
* @param {string} - String to be shown in chat, for example
|
||||
* markForTranslation("%(player)s has been defeated (objective).")
|
||||
*/
|
||||
TriggerHelper.DefeatPlayer = function(playerID)
|
||||
TriggerHelper.DefeatPlayer = function(playerID, defeatReason)
|
||||
{
|
||||
let cmpPlayer = QueryPlayerIDInterface(playerID);
|
||||
if (cmpPlayer)
|
||||
cmpPlayer.SetState("defeated");
|
||||
cmpPlayer.SetState("defeated", defeatReason);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,8 +56,8 @@ Trigger.prototype.CheckWonderVictory = function(data)
|
||||
"translateMessage": true,
|
||||
}, wonderDuration);
|
||||
|
||||
timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager,
|
||||
"MarkPlayerAsWon", wonderDuration, data.to);
|
||||
timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger,
|
||||
"WonderVictorySetWinner", wonderDuration, data.to);
|
||||
|
||||
this.wonderVictoryTimers[ent] = timer;
|
||||
this.wonderVictoryMessages[ent] = messages;
|
||||
@@ -76,6 +76,21 @@ Trigger.prototype.DeleteWonderVictoryMessages = function(data)
|
||||
}
|
||||
};
|
||||
|
||||
Trigger.prototype.WonderVictorySetWinner = function(playerID)
|
||||
{
|
||||
let cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
|
||||
cmpEndGameManager.MarkPlayerAsWon(
|
||||
playerID,
|
||||
n => markForPluralTranslation(
|
||||
"%(lastPlayer)s has won (wonder victory).",
|
||||
"%(players)s and %(lastPlayer)s have won (wonder victory).",
|
||||
n),
|
||||
n => markForPluralTranslation(
|
||||
"%(lastPlayer)s has been defeated (wonder victory).",
|
||||
"%(players)s and %(lastPlayer)s have been defeated (wonder victory).",
|
||||
n));
|
||||
};
|
||||
|
||||
{
|
||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckWonderVictory", { "enabled": true });
|
||||
|
||||
@@ -48,13 +48,28 @@ EndGameManager.prototype.SetGameType = function(newGameType, newSettings = {})
|
||||
Engine.BroadcastMessage(MT_GameTypeChanged, {});
|
||||
};
|
||||
|
||||
EndGameManager.prototype.MarkPlayerAsWon = function(playerID)
|
||||
/**
|
||||
* 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.MarkPlayerAsWon = function(playerID, victoryString, defeatString)
|
||||
{
|
||||
let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
|
||||
let numPlayers = cmpPlayerManager.GetNumPlayers();
|
||||
|
||||
this.skipAlliedVictoryCheck = true;
|
||||
|
||||
let winningPlayers = [];
|
||||
let defeatedPlayers = [];
|
||||
|
||||
let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
|
||||
// Group win/defeat messages
|
||||
for (let won of [false, true])
|
||||
for (let i = 1; i < numPlayers; ++i)
|
||||
@@ -63,9 +78,36 @@ EndGameManager.prototype.MarkPlayerAsWon = function(playerID)
|
||||
let hasWon = playerID == i || this.alliedVictory && cmpPlayer.IsMutualAlly(playerID);
|
||||
|
||||
if (hasWon == won)
|
||||
cmpPlayer.SetState(won ? "won" : "defeated");
|
||||
{
|
||||
if (won)
|
||||
{
|
||||
cmpPlayer.SetState("won", undefined);
|
||||
winningPlayers.push(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmpPlayer.SetState("defeated", undefined);
|
||||
defeatedPlayers.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (winningPlayers.length)
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -106,12 +148,24 @@ EndGameManager.prototype.AlliedVictoryCheck = function()
|
||||
}
|
||||
|
||||
if (this.alliedVictory || allies.length == 1)
|
||||
{
|
||||
for (let playerID of allies)
|
||||
{
|
||||
let cmpPlayer = QueryPlayerIDInterface(playerID);
|
||||
if (cmpPlayer)
|
||||
cmpPlayer.SetState("won");
|
||||
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."),
|
||||
|
||||
@@ -395,7 +395,13 @@ Player.prototype.GetState = function()
|
||||
return this.state;
|
||||
};
|
||||
|
||||
Player.prototype.SetState = function(newState, resign)
|
||||
/**
|
||||
* @param {string} newState - Either "defeated" or "won".
|
||||
* @param {string|undefined} message - A string to be shown in chat, for example
|
||||
* markForTranslation("%(player)s has been defeated (failed objective).").
|
||||
* If it is undefined, the caller MUST send that GUI notification manually.
|
||||
*/
|
||||
Player.prototype.SetState = function(newState, message)
|
||||
{
|
||||
if (this.state != "active")
|
||||
return;
|
||||
@@ -438,17 +444,21 @@ Player.prototype.SetState = function(newState, resign)
|
||||
Engine.BroadcastMessage(won ? MT_PlayerWon : MT_PlayerDefeated, { "playerId": this.playerID });
|
||||
|
||||
let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
|
||||
if (won)
|
||||
cmpGUIInterface.PushNotification({
|
||||
"type": "won",
|
||||
"players": [this.playerID]
|
||||
});
|
||||
else
|
||||
cmpGUIInterface.PushNotification({
|
||||
"type": "defeat",
|
||||
"players": [this.playerID],
|
||||
"resign": resign
|
||||
});
|
||||
if (message)
|
||||
if (won)
|
||||
cmpGUIInterface.PushNotification({
|
||||
"type": "won",
|
||||
"players": [this.playerID],
|
||||
"allies": [this.playerID],
|
||||
"message": message
|
||||
});
|
||||
else
|
||||
cmpGUIInterface.PushNotification({
|
||||
"type": "defeat",
|
||||
"players": [this.playerID],
|
||||
"allies": [this.playerID],
|
||||
"message": message
|
||||
});
|
||||
};
|
||||
|
||||
Player.prototype.GetTeam = function()
|
||||
|
||||
@@ -50,7 +50,7 @@ function Cheat(input)
|
||||
case "defeatplayer":
|
||||
cmpPlayer = QueryPlayerIDInterface(input.parameter);
|
||||
if (cmpPlayer)
|
||||
cmpPlayer.SetState("defeated");
|
||||
cmpPlayer.SetState("defeated", markForTranslation("%(player)s has been defeated (cheat)."));
|
||||
return;
|
||||
case "createunits":
|
||||
var cmpProductionQueue = input.selected.length && Engine.QueryInterface(input.selected[0], IID_ProductionQueue);
|
||||
|
||||
@@ -437,11 +437,11 @@ var g_Commands = {
|
||||
}
|
||||
},
|
||||
|
||||
"defeat-player": function(player, cmd, data)
|
||||
"resign": function(player, cmd, data)
|
||||
{
|
||||
let cmpPlayer = QueryPlayerIDInterface(player);
|
||||
if (cmpPlayer)
|
||||
cmpPlayer.SetState("defeated", !!cmd.resign);
|
||||
cmpPlayer.SetState("defeated", markForTranslation("%(player)s has resigned."));
|
||||
},
|
||||
|
||||
"garrison": function(player, cmd, data)
|
||||
|
||||
Reference in New Issue
Block a user