Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -377,6 +377,9 @@ 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 +[lobby.columns] +gamerating = false ; Show the average rating of the participating players in a column of the gamelist + [mod] enabledmods = "mod public" Index: binaries/data/mods/public/gui/common/functions_utility.js =================================================================== --- binaries/data/mods/public/gui/common/functions_utility.js +++ binaries/data/mods/public/gui/common/functions_utility.js @@ -158,15 +158,6 @@ 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) Index: binaries/data/mods/public/gui/common/gamedescription.js =================================================================== --- binaries/data/mods/public/gui/common/gamedescription.js +++ binaries/data/mods/public/gui/common/gamedescription.js @@ -4,11 +4,30 @@ var g_DescriptionHighlight = "orange"; /** + * The rating assigned to lobby players who didn't complete a ranked 1v1 yet. + */ +var g_DefaultLobbyRating = 1200; + +/** * 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 = ","; + +/** + * Returns the nickname without the lobby rating. + */ +function splitRatingFromNick(playerName) +{ + let result = /^(\S+)\ \((\d+)\)$/g.exec(playerName); + + if (!result) + return [playerName, g_DefaultLobbyRating]; + + return [result[1], +result[2]]; +} + /** * Array of playernames that the current user has marked as buddies. */ @@ -140,7 +159,7 @@ (isAI ? "white" : getPlayerColor(playerData.Name)) : rgbToGuiColor(playerData.Color || g_Settings.PlayerDefaults[playerIdx].Color)) + '"]' + - (g_Buddies.indexOf(removeRatingFromNick(playerData.Name)) != -1 ? g_BuddySymbol + " " : "") + + (g_Buddies.indexOf(splitRatingFromNick(playerData.Name)[0]) != -1 ? g_BuddySymbol + " " : "") + escapeText(playerData.Name) + "[/color]", Index: binaries/data/mods/public/gui/lobby/lobby.js =================================================================== --- binaries/data/mods/public/gui/lobby/lobby.js +++ binaries/data/mods/public/gui/lobby/lobby.js @@ -360,10 +360,34 @@ Engine.LobbyClearPresenceUpdates(); updatePlayerList(); updateSubject(Engine.LobbyGetRoomSubject()); + updateLobbyColumns(); Engine.GetGUIObjectByName("chatInput").tooltip = colorizeAutocompleteHotkey(); } +function updateLobbyColumns() +{ + let gameRating = Engine.ConfigDB_GetValue("user", "lobby.columns.gamerating") == "true"; + + // Only show the selected columns + let gamesBox = Engine.GetGUIObjectByName("gamesBox"); + gamesBox.hidden_mapType = gameRating; + gamesBox.hidden_gameRating = !gameRating; + + // Only show the filters of selected columns + let mapTypeFilter = Engine.GetGUIObjectByName("mapTypeFilter"); + mapTypeFilter.hidden = gameRating; + let gameRatingFilter = Engine.GetGUIObjectByName("gameRatingFilter"); + gameRatingFilter.hidden = !gameRating; + + // Keep filters right above the according column + let playersNumberFilter = Engine.GetGUIObjectByName("playersNumberFilter"); + let size = playersNumberFilter.size; + size.rleft = gameRating ? 74: 90; + size.rright = gameRating ? 84: 100; + playersNumberFilter.size = size; +} + function returnToMainMenu() { Engine.StopXmppClient(); @@ -385,6 +409,16 @@ mapTypeFilter.list = [translateWithContext("map", "Any")].concat(g_MapTypes.Title); mapTypeFilter.list_data = [""].concat(g_MapTypes.Name); + let gameRatingOptions = ["<1000", "<1100","<1200",">1200",">1300",">1400",">1500"].reverse(); + gameRatingOptions = prepareForDropdown(gameRatingOptions.map(r => ({ + "value": r, + "label": sprintf(r[0] == ">" ? translate("> %(rating)s") : translate("< %(rating)s"), { "rating": r.substr(1) }) + }))) + + let gameRatingFilter = Engine.GetGUIObjectByName("gameRatingFilter"); + gameRatingFilter.list = [translateWithContext("map", "Any")].concat(gameRatingOptions.label); + gameRatingFilter.list_data = [""].concat(gameRatingOptions.value); + resetFilters(); } @@ -393,6 +427,7 @@ Engine.GetGUIObjectByName("mapSizeFilter").selected = 0; Engine.GetGUIObjectByName("playersNumberFilter").selected = 0; Engine.GetGUIObjectByName("mapTypeFilter").selected = g_MapTypes.Default; + Engine.GetGUIObjectByName("gameRatingFilter").selected = 0; Engine.GetGUIObjectByName("showFullFilter").checked = false; applyFilters(); @@ -415,6 +450,7 @@ let mapSizeFilter = Engine.GetGUIObjectByName("mapSizeFilter"); let playersNumberFilter = Engine.GetGUIObjectByName("playersNumberFilter"); let mapTypeFilter = Engine.GetGUIObjectByName("mapTypeFilter"); + let gameRatingFilter = Engine.GetGUIObjectByName("gameRatingFilter"); let showFullFilter = Engine.GetGUIObjectByName("showFullFilter"); // We assume index 0 means display all for any given filter. @@ -433,6 +469,14 @@ if (!showFullFilter.checked && game.maxnbp <= game.nbp) return true; + if (gameRatingFilter.selected > 0) + { + let selected = gameRatingFilter.list_data[gameRatingFilter.selected]; + if (selected.startsWith(">") && +selected.substr(1) >= game.gameRating || + selected.startsWith("<") && +selected.substr(1) <= game.gameRating) + return true; + } + return false; } @@ -630,7 +674,7 @@ for (let i = 0; i < g_GameList.length; ++i) for (let player of stringifiedTeamListToPlayerData(g_GameList[i].players)) { - let nick = removeRatingFromNick(player.Name); + let [nick, rating] = splitRatingFromNick(player.Name); if (playerName != nick) continue; @@ -809,16 +853,30 @@ g_SelectedGamePort = g_GameList[gamesBox.selected].port; } - g_GameList = Engine.GetGameList().map(game => { + let testList = [{name:"Partida de pablek2",ip:"bla",port:"20595",state:"running",nbp:"2",maxnbp:"2",players:'{"0":\\[{"Name":"pablek2 (1269)","Team":0}],"1":\\[{"Name":"Raulyo (875)","Team":1}]}',mapName:"maps/random/marmara",niceMapName:"Marmara",mapSize:"320",mapType:"random",victoryCondition:"Conquista",startTime:"1487082783"},{name:"Reaperisonline's game",ip:"bla",port:"20595",state:"waiting",nbp:"5",maxnbp:"6",players:'{"0":\\[{"Name":"Reaperisonline (1259)","Team":0},{"Name":"Sh4d0w3rPL (1387)","Team":0}],"1":\\[{"Name":"jtggamer (1103)","Team":1},{"Name":"temik5 (1033)","Team":1},{"Name":"WGold (1180)","Team":1},{"Name":"siliboss (1143)","Team":1,"Offline":true,"State":"defeated"}]}',mapName:"maps/random/fortress",niceMapName:"Fortress",mapSize:"320",mapType:"random",victoryCondition:"Conquest",startTime:"1487081792"},{name:"siole's game",ip:"blabla",port:"20595",state:"running",nbp:"8",maxnbp:"8",players:'{"-1":\\[{"Name":"siole","Team":-1},{"Name":"Grugnas (1417)","Team":-1},{"Name":"merlin1 (1564)","Team":-1},{"Name":"snelius (1365)","Team":-1},{"Name":"imrobbyg (1622)","Team":-1},{"Name":"PrincipalityOfZeon (1652)","Team":-1},{"Name":"jaxtrx2 (1030)","Team":-1},{"Name":"nigel87 (1530)","Team":-1}],"observer":\\[{"Name":"fpre (1545)","Team":"observer"},{"Name":"Achelao (1418)","Team":"observer"},{"Name":"elexis3 (1156)","Team":"observer"}]}',mapName:"maps/random/survivalofthefittest",niceMapName:"Survival of the Fittest",mapSize:"384",mapType:"random",victoryCondition:"Regicide",startTime:"1486938980"},{name:"1v1",ip:"blabla",port:"20595",state:"running",nbp:"2",maxnbp:"2",players:'{"-1":\\[{"Name":"borg- (2114)","Team":-1},{"Name":"Feldfeld (1638)","Team":-1}]}',mapName:"maps/random/mainland",niceMapName:"Mainland",mapSize:"384",mapType:"random",victoryCondition:"Conquest",startTime:"1486532180"},{name:"Partida de JeanClaude",ip:"blabla",port:"20595",state:"running",nbp:"2",maxnbp:"2",players:'{"-1":\\[{"Name":"JeanClaude (1653)","Team":-1},{"Name":"Liberty (1569)","Team":-1}]}',mapName:"maps/random/mainland",niceMapName:"Mainland",mapSize:"384",mapType:"random",victoryCondition:"Conquest",startTime:"1486523148"},{name:"Komenoss spel",ip:"blabla",port:"20595",state:"running",nbp:"4",maxnbp:"4",players:'{"-1":\\[{"Name":"Komenos","Team":-1},{"Name":"Jordi_Iranzo93 (1278)","Team":-1},{"Name":"META-BARONS (1393)","Team":-1},{"Name":"King_Jerusalem (1265)","Team":-1}]}',mapName:"maps/random/bahrain",niceMapName:"Bahrain",mapSize:"384",mapType:"random",victoryCondition:"Erövring",startTime:"1487084910"},{name:"Partida de freedomm",ip:"blabla",port:"20595",state:"running",nbp:"2",maxnbp:"2",players:'{"0":\\[{"Name":"freedomm (1298)","Team":0}],"1":\\[{"Name":"bill13986 (1028)","Team":1}]}',mapName:"maps/skirmishes/Lorraine Plain (2)",niceMapName:"Lorraine Plain (2)",mapSize:"Default",mapType:"skirmish",victoryCondition:"Conquista",startTime:"1487085137"},{name:"Partida de comandantelobo",ip:"blabla",port:"20595",state:"running",nbp:"4",maxnbp:"4",players:'{"0":\\[{"Name":"comandantelobo (1047)","Team":0},{"Name":"perro (1153)","Team":0}],"1":\\[{"Name":"akshayhalasangi","Team":1},{"Name":"sckt (1263)","Team":1}]}',mapName:"maps/random/mainland",niceMapName:"Mainland",mapSize:"256",mapType:"random",victoryCondition:"Conquista",startTime:"1487083419"},{name:"Partie de mazert",ip:"blabla",port:"20595",state:"init",nbp:"4",maxnbp:"4",players:'{"-1":\\[{"Name":"mazert (1224)"},{"Name":"HirnWolf (1149)"},{"Name":"siliboss (1143)"},{"Name":"Ivaylo_Uzunov (1085)"}]}',mapName:"maps/random/frontier",niceMapName:"Frontier",mapSize:"256",mapType:"random",victoryCondition:"Conquête",startTime:""},{name:"mord's game",ip:"blabla",port:"20595",state:"running",nbp:"2",maxnbp:"2",players:'{"0":\\[{"Name":"mord (1165)","Team":0}],"1":\\[{"Name":"Chrisf1 (1222)","Team":1}]}',mapName:"maps/random/unknown",niceMapName:"Unknown",mapSize:"256",mapType:"random",victoryCondition:"Conquest",startTime:"1487082144"},{name:"craqqy's game",ip:"blabla",port:"20595",state:"running",nbp:"2",maxnbp:"2",players:'{"-1":\\[{"Name":"craqqy","Team":-1},{"Name":"KeaneY","Team":-1}]}',mapName:"maps/skirmishes/Acropolis Bay (2)",niceMapName:"Acropolis Bay (2)",mapSize:"Default",mapType:"skirmish",victoryCondition:"Conquest",startTime:"1487084573"},{name:"GummionkelchenIsBack's Spiel",ip:"blabla",port:"20595",state:"waiting",nbp:"5",maxnbp:"6",players:'{"0":\\[{"Name":"GummionkelchenIsBack (1396)","Team":0},{"Name":"Jordi_Iranzo93 (1278)","Team":0,"Offline":true},{"Name":"zDiegoAC","Team":0}],"1":\\[{"Name":"yashbanka123 (1141)","Team":1},{"Name":"TheRolle","Team":1},{"Name":"Rexosts (1342)","Team":1}]}',mapName:"maps/random/unknown_land",niceMapName:"Unknown Land",mapSize:"256",mapType:"random",victoryCondition:"Eroberung",startTime:"1487084209"},{name:"noobiel's game",ip:"blabla",port:"20595",state:"running",nbp:"1",maxnbp:"2",players:'{"-1":\\[{"Name":"noobiel","Team":-1},{"Name":"Ptolemy Epigone","Team":-1,"AI":"petra","AIDiff":3}]}',mapName:"maps/skirmishes/Greek Acropolis (2)",niceMapName:"Greek Acropolis (2)",mapSize:"Default",mapType:"skirmish",victoryCondition:"Conquest",startTime:"1486821890"}]; + g_GameList = testList/*Engine.GetGameList()*/.map(game => { + game.hasBuddies = 0; + + // Compute average rating of participating players + let playerRatings = []; for (let player of stringifiedTeamListToPlayerData(game.players)) { - let nick = removeRatingFromNick(player.Name); + let [nick, rating] = splitRatingFromNick(player.Name); + + if (player.Team != "observer") + playerRatings.push(rating); // 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; } + + game.gameRating = + playerRatings.length ? + Math.round(playerRatings.reduce((sum, current) => sum + current) / playerRatings.length) : + g_DefaultLobbyRating; + return game; }).filter(game => !filterGame(game)).sort((a, b) => { let sortA, sortB; @@ -827,6 +885,7 @@ case 'name': case 'mapSize': case 'mapType': + case 'gameRating': sortA = a[sortBy]; sortB = b[sortBy]; break; @@ -859,6 +918,7 @@ let list_mapSize = []; let list_mapType = []; let list_nPlayers = []; + let list_gameRating = []; let list = []; let list_data = []; let selectedGameIndex = -1; @@ -878,6 +938,7 @@ list_mapSize.push(translateMapSize(game.mapSize)); list_mapType.push(g_MapTypes.Title[mapTypeIdx] || ""); list_nPlayers.push(game.nbp + "/" + game.maxnbp); + list_gameRating.push(game.gameRating); list.push(gameName); list_data.push(i); } @@ -888,6 +949,8 @@ gamesBox.list_mapSize = list_mapSize; gamesBox.list_mapType = list_mapType; gamesBox.list_nPlayers = list_nPlayers; + gamesBox.list_gameRating = list_gameRating; + // Change these last, otherwise crash gamesBox.list = list; gamesBox.list_data = list_data; Index: binaries/data/mods/public/gui/lobby/lobby.xml =================================================================== --- binaries/data/mods/public/gui/lobby/lobby.xml +++ binaries/data/mods/public/gui/lobby/lobby.xml @@ -204,7 +204,7 @@ applyFilters(); joinButton(); - + Name @@ -216,16 +216,20 @@ Type - + Players + + Rating + + applyFilters(); @@ -233,7 +237,7 @@ applyFilters(); @@ -241,7 +245,15 @@ + applyFilters(); + + + applyFilters(); @@ -260,6 +272,7 @@ selectGameFromSelectedPlayername(); + Index: binaries/data/mods/public/gui/options/options.json =================================================================== --- binaries/data/mods/public/gui/options/options.json +++ binaries/data/mods/public/gui/options/options.json @@ -269,6 +269,12 @@ "label": "Chat Backlog", "tooltip": "Number of backlogged messages to load when joining the lobby", "parameters": { "config": "lobby.history", "min": "0" } + }, + { + "type": "boolean", + "label": "Game Rating Column", + "tooltip": "Show the average rating of the participating players in a column of the gamelist.", + "parameters": { "config": "lobby.columns.gamerating" } } ], "notificationSetting":