/**
* This class is concerned with displaying players who are online and
* triggering handlers when selecting or doubleclicking on a player.
*/
class PlayerList
{
constructor(xmppMessages, buddyButton, gameList)
{
this.gameList = gameList;
this.selectedPlayer = undefined;
this.statusOrder = Object.keys(this.PlayerStatuses);
// Avoid repeated array construction for performance
this.buddyStatusList = [];
this.playerList = [];
this.presenceList = [];
this.nickList = [];
this.ratingList = [];
this.playersFilter = Engine.GetGUIObjectByName("playersFilter");
this.playersFilter.onPress = this.selectPlayer.bind(this);
this.playersFilter.onTab = this.autocomplete.bind(this);
this.playersFilter.tooltip = colorizeAutocompleteHotkey();
this.selectionChangeHandlers = new Set();
this.mouseLeftDoubleClickItemHandlers = new Set();
this.playersBox = Engine.GetGUIObjectByName("playersBox");
this.playersBox.onSelectionChange = this.onPlayerListSelection.bind(this);
this.playersBox.onSelectionColumnChange = this.rebuildPlayerList.bind(this);
this.playersBox.onMouseLeftClickItem = this.onMouseLeftClickItem.bind(this);
this.playersBox.onMouseLeftDoubleClickItem = this.onMouseLeftDoubleClickItem.bind(this);
buddyButton.registerBuddyChangeHandler(this.onBuddyChange.bind(this));
xmppMessages.registerPlayerListUpdateHandler(this.rebuildPlayerList.bind(this));
this.registerSelectionChangeHandler(buddyButton.onPlayerSelectionChange.bind(buddyButton));
this.registerMouseLeftDoubleClickItemHandler(buddyButton.onPress.bind(buddyButton));
this.rebuildPlayerList();
}
selectPlayer()
{
let index = this.playersBox.list.indexOf(this.playersFilter.caption);
if (index != -1)
this.playersBox.selected = index;
}
autocomplete()
{
autoCompleteText(
this.playersFilter,
Engine.GetPlayerList().map(player => player.name));
}
registerSelectionChangeHandler(handler)
{
this.selectionChangeHandlers.add(handler);
}
registerMouseLeftDoubleClickItemHandler(handler)
{
this.mouseLeftDoubleClickItemHandlers.add(handler);
}
onBuddyChange()
{
this.rebuildPlayerList();
}
onMouseLeftDoubleClickItem()
{
for (let handler of this.mouseLeftDoubleClickItemHandlers)
handler();
}
onMouseLeftClickItem()
{
// In case of clicking on the same player again
this.gameList.selectGameFromPlayername(this.selectedPlayer);
}
onPlayerListSelection()
{
if (this.playersBox.selected == this.playersBox.list.indexOf(this.selectedPlayer))
return;
this.selectedPlayer = this.playersBox.list[this.playersBox.selected];
this.gameList.selectGameFromPlayername(this.selectedPlayer);
for (let handler of this.selectionChangeHandlers)
handler(this.selectedPlayer);
}
parsePlayer(sortKey, player)
{
player.isBuddy = g_Buddies.indexOf(player.name) != -1;
switch (sortKey)
{
case 'buddy':
player.sortValue = (player.isBuddy ? 1 : 2) + this.statusOrder.indexOf(player.presence) + player.name.toLowerCase();
break;
case 'rating':
player.sortValue = +player.rating;
break;
case 'status':
player.sortValue = this.statusOrder.indexOf(player.presence) + player.name.toLowerCase();
break;
case 'name':
default:
player.sortValue = player.name.toLowerCase();
break;
}
}
/**
* Do a full update of the player listing, including ratings from cached C++ information.
* Important: This should only be performed once if
* there have been multiple messages received changing this list.
*/
rebuildPlayerList()
{
Engine.ProfileStart("rebuildPlaersList");
Engine.ProfileStart("getPlayerList");
let playerList = Engine.GetPlayerList();
Engine.ProfileStop();
Engine.ProfileStart("parsePlayers");
playerList.forEach(this.parsePlayer.bind(this, this.playersBox.selected_column));
Engine.ProfileStop();
Engine.ProfileStart("sortPlayers");
playerList.sort(this.sortPlayers.bind(this, this.playersBox.selected_column_order));
Engine.ProfileStop();
Engine.ProfileStart("prepareList");
let length = playerList.length;
this.buddyStatusList.length = length;
this.playerList.length = length;
this.presenceList.length = length;
this.nickList.length = length;
this.ratingList.length = length;
playerList.forEach((player, i) => {
// TODO: COList.cpp columns should support horizontal center align
let rating = player.rating ? (" " + player.rating).substr(-5) : " -";
let presence = this.PlayerStatuses[player.presence] ? player.presence : "unknown";
if (presence == "unknown")
warn("Unknown presence:" + player.presence);
let statusTags = this.PlayerStatuses[presence].tags;
this.buddyStatusList[i] = player.isBuddy ? setStringTags(g_BuddySymbol, statusTags) : "";
this.playerList[i] = PlayerColor.ColorPlayerName(player.name, "", player.role);
this.presenceList[i] = setStringTags(this.PlayerStatuses[presence].status, statusTags);
this.ratingList[i] = setStringTags(rating, statusTags);
this.nickList[i] = escapeText(player.name);
});
Engine.ProfileStop();
Engine.ProfileStart("copyToGUI");
this.playersBox.list_buddy = this.buddyStatusList;
this.playersBox.list_name = this.playerList;
this.playersBox.list_status = this.presenceList;
this.playersBox.list_rating = this.ratingList;
this.playersBox.list = this.nickList;
Engine.ProfileStop();
Engine.ProfileStart("selectionChange");
this.playersBox.selected = this.playersBox.list.indexOf(this.selectedPlayer);
Engine.ProfileStop();
Engine.ProfileStop();
}
sortPlayers(sortOrder, player1, player2)
{
if (player1.sortValue < player2.sortValue)
return -sortOrder;
if (player1.sortValue > player2.sortValue)
return +sortOrder;
return 0;
}
}
/**
* The playerlist will be assembled using these values.
*/
PlayerList.prototype.PlayerStatuses = {
"available": {
"status": translate("Online"),
"tags": {
"color": "0 219 0"
}
},
"away": {
"status": translate("Away"),
"tags": {
"color": "255 127 0"
}
},
"playing": {
"status": translate("Busy"),
"tags": {
"color": "200 0 0"
}
},
"offline": {
"status": translate("Offline"),
"tags": {
"color": "0 0 0"
}
},
"unknown": {
"status": translateWithContext("lobby presence", "Unknown"),
"tags": {
"color": "178 178 178"
}
}
};