1
0
forked from 0ad/0ad

Mark players as buddies in the lobby and add a symbol to identify them in the lobby playerlist, gamelist and in the game description panel of the lobby, replay and load/save screen.

Differential Revision: https://code.wildfiregames.com/D209
Patch By: fpre aka. ffffffff
Reviewed By: causative
This was SVN commit r19433.
This commit is contained in:
elexis 2017-04-19 11:51:29 +00:00
parent 89c413031f
commit dcf12abe8c
5 changed files with 108 additions and 16 deletions

View File

@ -372,6 +372,7 @@ history = 0 ; Number of past messages to display on join
room = "arena22" ; Default MUC room to join
server = "lobby.wildfiregames.com" ; Address of lobby server
xpartamupp = "wfgbot22" ; Name of the server-side xmpp client that manage games
buddies = "," ; Comma separated list of playernames that the current user has marked as buddies
[mod]
enabledmods = "mod public"

View File

@ -158,6 +158,15 @@ function multiplayerName()
return Engine.ConfigDB_GetValue("user", "playername.multiplayer") || Engine.GetSystemUsername();
}
/**
* Returns the nickname without the lobby rating.
*/
function removeRatingFromNick(playerName)
{
let result = /^(\S+)\ \(\d+\)$/g.exec(playerName);
return result ? result[1] : playerName;
}
function tryAutoComplete(text, autoCompleteList)
{
if (!text.length)

View File

@ -3,6 +3,22 @@
*/
var g_DescriptionHighlight = "orange";
/**
* XEP-0172 doesn't restrict nicknames, but our lobby policy does.
* So use this human readable delimiter to separate buddy names in the config file.
*/
var g_BuddyListDelimiter = ",";
/**
* Array of playernames that the current user has marked as buddies.
*/
var g_Buddies = Engine.ConfigDB_GetValue("user", "lobby.buddies").split(g_BuddyListDelimiter);
/**
* Denotes which players are a lobby buddy of the current user.
*/
var g_BuddySymbol = '•';
/**
* Returns map description and preview image or placeholder.
*/
@ -123,7 +139,10 @@ function formatPlayerInfo(playerDataArray, playerStates)
(typeof getPlayerColor == 'function' ?
(isAI ? "white" : getPlayerColor(playerData.Name)) :
rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color)) +
'"]' + escapeText(playerData.Name) + "[/color]",
'"]' +
(g_Buddies.indexOf(removeRatingFromNick(playerData.Name)) != -1 ? g_BuddySymbol + " " : "") +
escapeText(playerData.Name) +
"[/color]",
"civ":
!playerData.Civ ?

View File

@ -382,23 +382,34 @@ function updatePlayerList()
if (playersBox.selected > -1)
g_SelectedPlayer = playersBox.list[playersBox.selected];
let buddyStatusList = [];
let playerList = [];
let presenceList = [];
let nickList = [];
let ratingList = [];
let cleanPlayerList = Engine.GetPlayerList().sort((a, b) => {
let cleanPlayerList = Engine.GetPlayerList().map(player => {
player.isBuddy = g_Buddies.indexOf(player.name) != -1;
return player;
}).sort((a, b) => {
let sortA, sortB;
let statusOrder = Object.keys(g_PlayerStatuses);
let statusA = statusOrder.indexOf(a.presence) + a.name.toLowerCase();
let statusB = statusOrder.indexOf(b.presence) + b.name.toLowerCase();
switch (sortBy)
{
case 'buddy':
sortA = (a.isBuddy ? 1 : 2) + statusA;
sortB = (b.isBuddy ? 1 : 2) + statusB;
break;
case 'rating':
sortA = +a.rating;
sortB = +b.rating;
break;
case 'status':
let statusOrder = Object.keys(g_PlayerStatuses);
sortA = statusOrder.indexOf(a.presence);
sortB = statusOrder.indexOf(b.presence);
sortA = statusA;
sortB = statusB;
break;
case 'name':
default:
@ -427,12 +438,14 @@ function updatePlayerList()
let coloredPresence = '[color="' + statusColor + '"]' + g_PlayerStatuses[presence].status + "[/color]";
let coloredRating = '[color="' + statusColor + '"]' + rating + "[/color]";
buddyStatusList.push(player.isBuddy ? '[color="' + statusColor + '"]' + g_BuddySymbol + '[/color]' : "");
playerList.push(coloredName);
presenceList.push(coloredPresence);
ratingList.push(coloredRating);
nickList.push(player.name);
}
playersBox.list_buddy = buddyStatusList;
playersBox.list_name = playerList;
playersBox.list_status = presenceList;
playersBox.list_rating = ratingList;
@ -443,6 +456,31 @@ function updatePlayerList()
playersBox.selected = playersBox.list.indexOf(g_SelectedPlayer);
}
/**
* Toggle buddy state for a player in playerlist within the user config
*/
function toggleBuddy()
{
let playerList = Engine.GetGUIObjectByName("playersBox");
let name = playerList.list[playerList.selected];
if (!name || name == g_Username || name.indexOf(g_BuddyListDelimiter) != -1)
return;
let index = g_Buddies.indexOf(name);
if (index != -1)
g_Buddies.splice(index, 1);
else
g_Buddies.push(name);
let buddies = g_Buddies.filter(nick => nick).join(g_BuddyListDelimiter);
Engine.ConfigDB_CreateValue("user", "lobby.buddies", buddies);
Engine.ConfigDB_WriteValueToFile("user", "lobby.buddies", buddies, "config/user.cfg");
updatePlayerList();
updateGameList();
}
/**
* Select the game listing the selected player when toggling the full games filter.
*/
@ -465,9 +503,7 @@ function selectGameFromPlayername(playerName)
for (let i = 0; i < g_GameList.length; ++i)
for (let player of stringifiedTeamListToPlayerData(g_GameList[i].players))
{
let result = /^(\S+)\ \(\d+\)$/g.exec(player.Name);
let nick = result ? result[1] : player.Name;
let nick = removeRatingFromNick(player.Name);
if (playerName != nick)
continue;
@ -632,7 +668,18 @@ function updateGameList()
g_SelectedGamePort = g_GameList[gamesBox.selected].port;
}
g_GameList = Engine.GetGameList().filter(game => !filterGame(game)).sort((a, b) => {
g_GameList = Engine.GetGameList().map(game => {
game.hasBuddies = 0;
for (let player of stringifiedTeamListToPlayerData(game.players))
{
let nick = removeRatingFromNick(player.Name);
// Sort games with playing buddies above games with spectating buddies
if (game.hasBuddies < 2 && g_Buddies.indexOf(nick) != -1)
game.hasBuddies = player.Team == "observer" ? 1 : 2;
}
return game;
}).filter(game => !filterGame(game)).sort((a, b) => {
let sortA, sortB;
switch (sortBy)
{
@ -642,6 +689,10 @@ function updateGameList()
sortA = a[sortBy];
sortB = b[sortBy];
break;
case 'buddy':
sortA = String(b.hasBuddies) + g_GameStatusOrder.indexOf(a.state) + a.name.toLowerCase();
sortB = String(a.hasBuddies) + g_GameStatusOrder.indexOf(b.state) + b.name.toLowerCase();
break;
case 'mapName':
sortA = translate(a.niceMapName);
sortB = translate(b.niceMapName);
@ -661,6 +712,7 @@ function updateGameList()
return 0;
});
let list_buddy = [];
let list_name = [];
let list_mapName = [];
let list_mapSize = [];
@ -679,6 +731,7 @@ function updateGameList()
if (game.ip == g_SelectedGameIP && game.port == g_SelectedGamePort)
selectedGameIndex = +i;
list_buddy.push(game.hasBuddies ? '[color="' + g_GameColors[game.state] + '"]' + g_BuddySymbol + '[/color]' : "");
list_name.push('[color="' + g_GameColors[game.state] + '"]' + gameName);
list_mapName.push(translateMapTitle(game.niceMapName));
list_mapSize.push(translateMapSize(game.mapSize));
@ -688,6 +741,7 @@ function updateGameList()
list_data.push(i);
}
gamesBox.list_buddy = list_buddy;
gamesBox.list_name = list_name;
gamesBox.list_mapName = list_mapName;
gamesBox.list_mapSize = list_mapSize;

View File

@ -23,7 +23,7 @@
</action>
<!-- Left panel: Player list. -->
<object name="leftPanel" size="20 30 20% 100%-280">
<object name="leftPanel" size="20 30 20% 100%-310">
<object name="playersBox"
style="ModernList"
sprite_asc="ModernArrowDown"
@ -36,10 +36,11 @@
size="0 0 100% 100%"
font="sans-bold-stroke-13"
>
<column id="buddy" width="12"/>
<column id="status" width="26%">
<translatableAttribute id="heading">Status</translatableAttribute>
</column>
<column id="name" width="48%">
<column id="name" width="45%-12">
<translatableAttribute id="heading">Name</translatableAttribute>
</column>
<column id="rating" width="26%">
@ -51,10 +52,11 @@
<action on="SelectionColumnChange">
updatePlayerList();
</action>
<action on="mouseleftdoubleclickitem">toggleBuddy();</action>
</object>
</object>
<object name="profilePanel" size="20 100%-275 20% 100%-80">
<object name="profilePanel" size="20 100%-305 20% 100%-110">
<object name="profileBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%">
<object name="profileArea" size="0 0 100% 100%" hidden="true">
<object name="usernameText" size="0 0 100% 45" type="text" style="ModernLabelText" text_align="center" font="sans-bold-16" />
@ -87,8 +89,14 @@
</object>
</object>
<object name="leftButtonPanel" size="20 100%-75 20% 100%-20">
<object name="leaderboardButton" type="button" style="ModernButtonRed" size="0 0 100% 25">
<object name="leftButtonPanel" size="20 100%-105 20% 100%-20">
<object name="toggleBuddy" type="button" style="ModernButtonRed" size="0 100%-85 100% 100%-60">
<translatableAttribute id="caption" comment="Toggle Buddy">Toggle Buddy</translatableAttribute>
<action on="Press">
toggleBuddy();
</action>
</object>
<object name="leaderboardButton" type="button" style="ModernButtonRed" size="0 100%-55 100% 100%-30">
<translatableAttribute id="caption">Leaderboard</translatableAttribute>
<action on="Press">
Engine.SendGetBoardList();
@ -97,7 +105,7 @@
displayProfile("leaderboard");
</action>
</object>
<object name="userprofileButton" type="button" style="ModernButtonRed" size="0 30 100% 100%">
<object name="userprofileButton" type="button" style="ModernButtonRed" size="0 100%-25 100% 100%">
<translatableAttribute id="caption">User Profile Lookup</translatableAttribute>
<action on="Press">
Engine.GetGUIObjectByName("profileFetch").hidden = false;
@ -199,7 +207,8 @@
<action on="SelectionChange">updateGameSelection();</action>
<action on="SelectionColumnChange">applyFilters();</action>
<action on="mouseleftdoubleclickitem">joinButton();</action>
<column id="name" color="0 60 0" width="27%">
<column id="buddy" width="12"/>
<column id="name" color="0 60 0" width="27%-12">
<translatableAttribute id="heading">Name</translatableAttribute>
</column>
<column id="mapName" color="128 128 128" width="25%">