Changeset View
Standalone View
binaries/data/mods/public/gui/lobby/LobbyPage/GameList.js
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
class GameListFilters | |||||
{ | |||||
} | |||||
class GameList | |||||
{ | |||||
constructor(xmppMessages, buddyButton) | |||||
{ | |||||
this.gameList = []; | |||||
this.selectionChangeHandlers = new Set(); | |||||
this.gamesBox = Engine.GetGUIObjectByName("gamesBox"); | |||||
this.gamesBox.onSelectionChange = this.onSelectionChange.bind(this); | |||||
this.gamesBox.onSelectionColumnChange = this.onFilterChange.bind(this); | |||||
let ratingColumn = Engine.ConfigDB_GetValue("user", "lobby.columns.gamerating") == "true"; | |||||
this.gamesBox.hidden_mapType = ratingColumn; | |||||
this.gamesBox.hidden_gameRating = !ratingColumn; | |||||
this.filters = Object.keys(GameListFilters).map(name => | |||||
new GameListFilters[name]( | |||||
this.onFilterChange.bind(this))); | |||||
xmppMessages.registerHandler("game", "gamelist", this.rebuildGameList.bind(this)); | |||||
xmppMessages.registerHandler("system", "disconnected", this.rebuildGameList.bind(this)); | |||||
buddyButton.registerBuddyChangeHandler(this.onBuddyChange.bind(this)); | |||||
Freagarach: +`.` | |||||
Done Inline ActionsBut this is not a sentence and not intended as one, more a half-sentence Thanks for the intent to help, but please not 20 inline comments for that, one is sufficient if I'm not convinced to add these in general. (First I would have to become convinced of ending half sentences with a period or changing all half sentences to include complete context to form a full sentence qualifying the period, then I can identify the 20+ places too. With the 31 inline comments posted I would have to click more than 31 times to cycle through all of them with this web UI, but I don't intend to repeat the answer 20+ times in 30 of the comments, so this cycle button is basically 1/3 as useful as it was before) elexis: But this is not a sentence and not intended as one, more a half-sentence
Thanks for the intent… | |||||
Not Done Inline ActionsYou're right, sorry about the inline-spam. Freagarach: You're right, sorry about the inline-spam.
(You know you can hide them and they won't be cycled… | |||||
this.rebuildGameList(); | |||||
} | |||||
registerSelectionChangeHandler(handler) | |||||
{ | |||||
this.selectionChangeHandlers.add(handler); | |||||
} | |||||
onFilterChange() | |||||
{ | |||||
this.rebuildGameList(); | |||||
} | |||||
onBuddyChange() | |||||
{ | |||||
this.rebuildGameList(); | |||||
} | |||||
onSelectionChange() | |||||
{ | |||||
let game = this.selectedGame(); | |||||
for (let handler of this.selectionChangeHandlers) | |||||
handler(game); | |||||
} | |||||
selectedGame() | |||||
{ | |||||
return this.gameList[this.gamesBox.selected] || undefined; | |||||
} | |||||
/** | |||||
* Update the game listing from data cached in C++. | |||||
*/ | |||||
rebuildGameList() | |||||
{ | |||||
this.gameList = Engine.GetGameList().map( | |||||
this.parseGame.bind(this)).filter( | |||||
this.filterGame.bind(this)).sort( | |||||
this.sortGame.bind(this)); | |||||
let list_buddy = []; | |||||
let list_name = []; | |||||
let list_mapName = []; | |||||
let list_mapSize = []; | |||||
let list_mapType = []; | |||||
let list_nPlayers = []; | |||||
let list_gameRating = []; | |||||
let list = []; | |||||
let list_data = []; | |||||
let selectedGame = this.selectedGame(); | |||||
let selectedGameIndex = -1; | |||||
this.gameList.forEach((game, i) => { | |||||
let gameName = escapeText(game.name); | |||||
let mapTypeIdx = g_MapTypes.Name.indexOf(game.mapType); | |||||
if (selectedGame && game.ip == selectedGame.ip && game.port == selectedGame.port) | |||||
selectedGameIndex = i; | |||||
list_buddy.push(game.hasBuddies ? setStringTags(g_BuddySymbol, this.GameTags[game.state]) : ""); | |||||
list_name.push(setStringTags(gameName, this.GameTags[game.state])); | |||||
list_mapName.push(translateMapTitle(game.niceMapName)); | |||||
list_mapSize.push(translateMapSize(game.mapSize)); | |||||
list_mapType.push(g_MapTypes.Title[mapTypeIdx] || ""); | |||||
list_nPlayers.push(this.getNumPlayers(game)); | |||||
list_gameRating.push(game.gameRating); | |||||
list.push(gameName); | |||||
list_data.push(i); | |||||
}); | |||||
this.gamesBox.list_buddy = list_buddy; | |||||
this.gamesBox.list_name = list_name; | |||||
this.gamesBox.list_mapName = list_mapName; | |||||
this.gamesBox.list_mapSize = list_mapSize; | |||||
this.gamesBox.list_mapType = list_mapType; | |||||
this.gamesBox.list_nPlayers = list_nPlayers; | |||||
this.gamesBox.list_gameRating = list_gameRating; | |||||
// Change these last, otherwise crash | |||||
this.gamesBox.list = list; | |||||
this.gamesBox.list_data = list_data; | |||||
this.gamesBox.auto_scroll = false; | |||||
this.gamesBox.selected = selectedGameIndex; | |||||
} | |||||
getNumPlayers(game) | |||||
{ | |||||
return sprintf( | |||||
game.observerCount ? | |||||
// Translation: The number of players and observers in this game | |||||
translate("%(current)s/%(max)s +%(observercount)s") : | |||||
// Translation: The number of players in this game | |||||
translate("%(current)s/%(max)s"), | |||||
{ | |||||
"current": setStringTags(game.nbp, this.PlayerCountTags.CurrentPlayers), | |||||
"max": setStringTags(game.maxnbp, this.PlayerCountTags.MaxPlayers), | |||||
"observercount": setStringTags(game.observerCount, this.PlayerCountTags.Observers) | |||||
}); | |||||
} | |||||
parseGame(game) | |||||
{ | |||||
game.hasBuddies = 0; | |||||
game.observerCount = 0; | |||||
// Compute average rating of participating players | |||||
let playerRatings = []; | |||||
for (let player of stringifiedTeamListToPlayerData(game.players)) | |||||
{ | |||||
let playerNickRating = splitRatingFromNick(player.Name); | |||||
if (player.Team != "observer") | |||||
playerRatings.push(playerNickRating.rating || g_DefaultLobbyRating); | |||||
else | |||||
++game.observerCount; | |||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
// Sort games with playing buddies above games with spectating buddies | |||||
if (game.hasBuddies < 2 && g_Buddies.indexOf(playerNickRating.nick) != -1) | |||||
game.hasBuddies = player.Team == "observer" ? 1 : 2; | |||||
} | |||||
game.gameRating = | |||||
playerRatings.length ? | |||||
Math.round(playerRatings.reduce((sum, current) => sum + current) / playerRatings.length) : | |||||
g_DefaultLobbyRating; | |||||
try | |||||
{ | |||||
game.mods = JSON.parse(game.mods); | |||||
} | |||||
catch (e) | |||||
{ | |||||
game.mods = []; | |||||
} | |||||
if (!hasSameMods(game.mods, Engine.GetEngineInfo().mods)) | |||||
game.state = "incompatible"; | |||||
return game; | |||||
} | |||||
filterGame(game) | |||||
{ | |||||
return this.filters.every(filter => filter.filter(game)); | |||||
} | |||||
sortGame(a, b) | |||||
{ | |||||
let sortA, sortB; | |||||
let sortBy = this.gamesBox.selected_column; | |||||
switch (sortBy) | |||||
{ | |||||
case 'name': | |||||
sortA = this.GameStatusOrder.indexOf(a.state) + a.name.toLowerCase(); | |||||
sortB = this.GameStatusOrder.indexOf(b.state) + b.name.toLowerCase(); | |||||
break; | |||||
case 'gameRating': | |||||
case 'mapSize': | |||||
case 'mapType': | |||||
sortA = a[sortBy]; | |||||
sortB = b[sortBy]; | |||||
break; | |||||
case 'buddy': | |||||
sortA = String(b.hasBuddies) + this.GameStatusOrder.indexOf(a.state) + a.name.toLowerCase(); | |||||
sortB = String(a.hasBuddies) + this.GameStatusOrder.indexOf(b.state) + b.name.toLowerCase(); | |||||
break; | |||||
case 'mapName': | |||||
sortA = translate(a.niceMapName); | |||||
sortB = translate(b.niceMapName); | |||||
break; | |||||
case 'nPlayers': | |||||
sortA = a.maxnbp; | |||||
sortB = b.maxnbp; | |||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
break; | |||||
} | |||||
let sortOrder = this.gamesBox.selected_column_order; | |||||
if (sortA < sortB) | |||||
return -sortOrder; | |||||
if (sortA > sortB) | |||||
return +sortOrder; | |||||
return 0; | |||||
} | |||||
/** | |||||
* Select the game where the selected player is currently playing, observing or offline. | |||||
* Selects in that order to account for players that occur in multiple games. | |||||
*/ | |||||
selectGameFromPlayername(playerName) | |||||
{ | |||||
if (!playerName) | |||||
return; | |||||
let foundAsObserver = false; | |||||
for (let i = 0; i < this.gameList.length; ++i) | |||||
for (let player of stringifiedTeamListToPlayerData(this.gameList[i].players)) | |||||
{ | |||||
if (playerName != splitRatingFromNick(player.Name).nick) | |||||
continue; | |||||
this.gamesBox.auto_scroll = true; | |||||
if (player.Team == "observer") | |||||
{ | |||||
foundAsObserver = true; | |||||
this.gamesBox.selected = i; | |||||
} | |||||
else if (!player.Offline) | |||||
{ | |||||
this.gamesBox.selected = i; | |||||
return; | |||||
} | |||||
else if (!foundAsObserver) | |||||
this.gamesBox.selected = i; | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Current games will be listed in these colors. | |||||
*/ | |||||
GameList.prototype.GameTags = { | |||||
"init": { "color": "0 219 0" }, | |||||
"waiting": { "color": "255 127 0" }, | |||||
"running": { "color": "219 0 0" }, | |||||
"incompatible": { "color": "gray" } | |||||
}; | |||||
/** | |||||
* Initial sorting order of the gamelist. | |||||
*/ | |||||
GameList.prototype.GameStatusOrder = [ | |||||
"init", | |||||
"waiting", | |||||
"running", | |||||
"incompatible" | |||||
]; | |||||
/** | |||||
* Color for the player count number in the games list. | |||||
*/ | |||||
GameList.prototype.PlayerCountTags = { | |||||
"CurrentPlayers": { "color": "0 160 160" }, | |||||
"MaxPlayers": { "color": "0 160 160" }, | |||||
"Observers": { "color": "0 128 128" } | |||||
}; | |||||
Not Done Inline Actions+. Freagarach: +`.` | |||||
Not Done Inline Actions+. Freagarach: +`.` |
+.