Ticket #3383: lobby_coding_convention_v2.patch

File lobby_coding_convention_v2.patch, 33.4 KB (added by elexis, 9 years ago)

More cleanup as discussed in IRC.

  • binaries/data/mods/public/gui/lobby/lobby.js

     
     1var g_MapSizes = {};
     2const g_MapTypesText = [translateWithContext("map", "Skirmish"), translateWithContext("map", "Random"), translate("Scenario")];
     3const g_MapTypes = ["skirmish", "random", "scenario"];
     4
    15var g_ChatMessages = [];
    2 var g_Name = "unknown";
     6const g_ShowTimestamp = Engine.ConfigDB_GetValue("user", "lobby.chattimestamp") == "true";
     7
     8/** This object looks like { "name": [numMessagesSinceReset, lastReset, timeBlocked] } when in use. */
     9var g_SpamMonitor = {};
     10const g_SpamBlockLength = 30;
     11
     12/**
     13 * A key that is used to distinguish between system and user chat messages.
     14 * TODO: This should not be used, just use a boolean argument.
     15 */
     16const g_SpecialKey = Math.random();
     17
     18const g_ModPrefix = "@";
     19var g_Username = "unknown";
     20var g_UserRating = "";
     21
     22const g_FixedColors = {
     23    "system": repeatString(7, "255.0.0."),
     24    "@WFGbot": repeatString(7, "255.24.24.")
     25};
     26
    327var g_GameList = {}
     28/** Name of the column to be used for sorting. */
    429var g_GameListSortBy = "name";
     30/** Sorting order: +1 ascending, -1 descending. */
     31var g_GameListOrder = 1;
     32
    533var g_PlayerListSortBy = "name";
    6 var g_GameListOrder = 1; // 1 for ascending sort, and -1 for descending
    734var g_PlayerListOrder = 1;
    8 var g_specialKey = Math.random();
    9 // This object looks like {"name":[numMessagesSinceReset, lastReset, timeBlocked]} when in use.
    10 var g_spamMonitor = {};
    11 var g_timestamp = Engine.ConfigDB_GetValue("user", "lobby.chattimestamp") == "true";
    12 var g_mapSizes = {};
    13 const g_mapTypesText = [translateWithContext("map", "Skirmish"), translateWithContext("map", "Random"), translate("Scenario")];
    14 const g_mapTypes = ["skirmish", "random", "scenario"];
    15 var g_userRating = ""; // Rating of user, defaults to Unrated
    16 var g_modPrefix = "@";
    17 // Block spammers for 30 seconds.
    18 var SPAM_BLOCK_LENGTH = 30;
    1935
    2036////////////////////////////////////////////////////////////////////////////////////////////////
    2137
    2238function init(attribs)
    2339{
    2440    // Play menu music
    2541    initMusic();
    2642    global.music.setState(global.music.states.MENU);
    2743
    28     g_Name = Engine.LobbyGetNick();
     44    g_Username = Engine.LobbyGetNick();
    2945
    30     g_mapSizes = initMapSizes();
    31     g_mapSizes.shortNames.splice(0, 0, translateWithContext("map size", "Any"));
    32     g_mapSizes.tiles.splice(0, 0, "");
     46    g_MapSizes = initMapSizes();
     47    g_MapSizes.shortNames.splice(0, 0, translateWithContext("map size", "Any"));
     48    g_MapSizes.tiles.splice(0, 0, "");
    3349
    3450    var mapSizeFilter = Engine.GetGUIObjectByName("mapSizeFilter");
    35     mapSizeFilter.list = g_mapSizes.shortNames;
    36     mapSizeFilter.list_data = g_mapSizes.tiles;
     51    mapSizeFilter.list = g_MapSizes.shortNames;
     52    mapSizeFilter.list_data = g_MapSizes.tiles;
    3753
    3854    var playersNumberFilter = Engine.GetGUIObjectByName("playersNumberFilter");
    3955    playersNumberFilter.list = [translateWithContext("player number", "Any"),2,3,4,5,6,7,8];
    4056    playersNumberFilter.list_data = ["",2,3,4,5,6,7,8];
    4157
    4258    var mapTypeFilter = Engine.GetGUIObjectByName("mapTypeFilter");
    43     mapTypeFilter.list = [translateWithContext("map", "Any")].concat(g_mapTypesText);
    44     mapTypeFilter.list_data = [""].concat(g_mapTypes);
     59    mapTypeFilter.list = [translateWithContext("map", "Any")].concat(g_MapTypesText);
     60    mapTypeFilter.list_data = [""].concat(g_MapTypes);
    4561
    4662    Engine.LobbySetPlayerPresence("available");
    4763    Engine.SendGetGameList();
    4864    Engine.SendGetBoardList();
    4965    updatePlayerList();
     
    103119    applyFilters();
    104120}
    105121
    106122function applyFilters()
    107123{
    108     // Update the list of games
    109124    updateGameList();
    110 
    111     // Update info box about the game currently selected
    112125    updateGameSelection();
    113126}
    114127
    115128/**
    116129 * Filter a game based on the status of the filter dropdowns.
     
    174187    var presenceList = [];
    175188    var nickList = [];
    176189    var ratingList = [];
    177190    var cleanPlayerList = Engine.GetPlayerList();
    178191    // Sort the player list, ignoring case.
    179     cleanPlayerList.sort(function(a,b)
     192    cleanPlayerList.sort((a,b) =>
    180193    {
    181194        switch (g_PlayerListSortBy)
    182195        {
    183196        case 'rating':
    184197            if (a.rating < b.rating)
     
    204217            else if (aName > bName)
    205218                return g_PlayerListOrder;
    206219            return 0;
    207220        }
    208221    });
    209     for (var i = 0; i < cleanPlayerList.length; i++)
     222    for (let i = 0; i < cleanPlayerList.length; ++i)
    210223    {
    211224        // Identify current user's rating.
    212         if (cleanPlayerList[i].name == g_Name && cleanPlayerList[i].rating)
    213             g_userRating = cleanPlayerList[i].rating;
     225        if (cleanPlayerList[i].name == g_Username && cleanPlayerList[i].rating)
     226            g_UserRating = cleanPlayerList[i].rating;
    214227        // Add a "-" for unrated players.
    215228        if (!cleanPlayerList[i].rating)
    216229            cleanPlayerList[i].rating = "-";
    217230        // Colorize.
    218231        var [name, status, rating] = formatPlayerListEntry(cleanPlayerList[i].name, cleanPlayerList[i].presence, cleanPlayerList[i].rating, cleanPlayerList[i].role);
    219232        // Push to lists.
    220233        playerList.push(name);
    221234        presenceList.push(status);
    222235        nickList.push(cleanPlayerList[i].name);
    223236        var ratingSpaces = "  ";
    224         for (var index = 0; index < 4 - Math.ceil(Math.log(cleanPlayerList[i].rating) / Math.LN10); index++)
     237        for (let index = 0; index < 4 - Math.ceil(Math.log(cleanPlayerList[i].rating) / Math.LN10); ++index)
    225238            ratingSpaces += "  ";
    226239        ratingList.push(String(ratingSpaces + rating));
    227240    }
    228241    playersBox.list_name = playerList;
    229242    playersBox.list_status = presenceList;
     
    236249
    237250/**
    238251 * Display the profile of the selected player.
    239252 * Displays N/A for all stats until updateProfile is called when the stats
    240253 *  are actually received from the bot.
    241  * 
     254 *
    242255 * @param caller From which screen is the user requesting data from?
    243256 */
    244257function displayProfile(caller)
    245258{
    246259    var playerList, rating;
     
    261274        Engine.GetGUIObjectByName("profileArea").hidden = true;
    262275        return;
    263276    }
    264277    Engine.GetGUIObjectByName("profileArea").hidden = false;
    265278
    266     Engine.SendGetProfile(playerList.list[playerList.selected]);   
     279    Engine.SendGetProfile(playerList.list[playerList.selected]);
    267280
    268281    var user = playerList.list_name[playerList.selected];
    269282    var role = Engine.LobbyGetPlayerRole(playerList.list[playerList.selected]);
    270283    var userList = Engine.GetGUIObjectByName("playersBox");
    271284    if (role && caller == "lobbylist")
     
    288301    Engine.GetGUIObjectByName("ratioText").caption = translate("N/A");
    289302}
    290303
    291304/**
    292305 * Update the profile of the selected player with data from the bot.
    293  *
    294306 */
    295307function updateProfile()
    296308{
    297309    var playerList, user;
    298310    var attributes = Engine.GetProfile();
     
    306318            Engine.GetGUIObjectByName("profileErrorText").hidden = false;
    307319            return;
    308320        }
    309321        Engine.GetGUIObjectByName("profileWindowArea").hidden = false;
    310322        Engine.GetGUIObjectByName("profileErrorText").hidden = true;
    311        
     323
    312324        if (attributes[0].rating != "")
    313             user = sprintf(translate("%(nick)s (%(rating)s)"), { nick: user, rating: attributes[0].rating });
     325            user = sprintf(translate("%(nick)s (%(rating)s)"), { "nick": user, "rating": attributes[0].rating });
    314326
    315327        Engine.GetGUIObjectByName("profileUsernameText").caption = user;
    316328        Engine.GetGUIObjectByName("profileRankText").caption = attributes[0].rank;
    317329        Engine.GetGUIObjectByName("profileHighestRatingText").caption = attributes[0].highestRating;
    318330        Engine.GetGUIObjectByName("profileTotalGamesText").caption = attributes[0].totalGamesPlayed;
    319331        Engine.GetGUIObjectByName("profileWinsText").caption = attributes[0].wins;
    320332        Engine.GetGUIObjectByName("profileLossesText").caption = attributes[0].losses;
    321333
    322334        var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2);
    323335        if (attributes[0].totalGamesPlayed != 0)
    324             Engine.GetGUIObjectByName("profileRatioText").caption = sprintf(translate("%(percentage)s%%"), { percentage: winRate });
     336            Engine.GetGUIObjectByName("profileRatioText").caption = sprintf(translate("%(percentage)s%%"), { "percentage": winRate });
    325337        else
    326338            Engine.GetGUIObjectByName("profileRatioText").caption = translateWithContext("Used for an undefined winning rate", "-");
    327339        return;
    328340    }
    329341    else if (!Engine.GetGUIObjectByName("leaderboard").hidden)
    330342        playerList = Engine.GetGUIObjectByName("leaderboardBox");
    331343    else
    332344        playerList = Engine.GetGUIObjectByName("playersBox");
    333    
     345
    334346    if (attributes[0].rating == "-2")
    335347        return;
    336348    // Make sure the stats we have received coincide with the selected player.
    337349    if (attributes[0].player != playerList.list[playerList.selected])
    338350        return;
    339351    user = playerList.list_name[playerList.selected];
    340352    if (attributes[0].rating != "")
    341         user = sprintf(translate("%(nick)s (%(rating)s)"), { nick: user, rating: attributes[0].rating });
     353        user = sprintf(translate("%(nick)s (%(rating)s)"), { "nick": user, "rating": attributes[0].rating });
    342354
    343355    Engine.GetGUIObjectByName("usernameText").caption = user;
    344356    Engine.GetGUIObjectByName("rankText").caption = attributes[0].rank;
    345357    Engine.GetGUIObjectByName("highestRatingText").caption = attributes[0].highestRating;
    346358    Engine.GetGUIObjectByName("totalGamesText").caption = attributes[0].totalGamesPlayed;
    347359    Engine.GetGUIObjectByName("winsText").caption = attributes[0].wins;
    348360    Engine.GetGUIObjectByName("lossesText").caption = attributes[0].losses;
    349361
    350362    var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2);
    351363    if (attributes[0].totalGamesPlayed != 0)
    352         Engine.GetGUIObjectByName("ratioText").caption = sprintf(translate("%(percentage)s%%"), { percentage: winRate });
     364        Engine.GetGUIObjectByName("ratioText").caption = sprintf(translate("%(percentage)s%%"), { "percentage": winRate });
    353365    else
    354366        Engine.GetGUIObjectByName("ratioText").caption = translateWithContext("Used for an undefined winning rate", "-");
    355367}
    356368
    357369/**
    358370 * Update the leaderboard from data cached in C++.
    359371 */
    360372function updateLeaderboard()
    361373{
    362     // Get list from C++
    363     var boardList = Engine.GetBoardList();
    364     // Get GUI leaderboard object
    365374    var leaderboard = Engine.GetGUIObjectByName("leaderboardBox");
    366     // Sort list in acending order by rating
    367     boardList.sort(function(a, b) b.rating - a.rating);
     375    var boardList = Engine.GetBoardList();
     376    boardList.sort((a, b) => b.rating - a.rating);
    368377
    369378    var list = [];
    370379    var list_name = [];
    371380    var list_rank = [];
    372381    var list_rating = [];
    373382
    374383    // Push changes
    375     for (var i = 0; i < boardList.length; i++)
     384    for (let i = 0; i < boardList.length; ++i)
    376385    {
    377386        list_name.push(boardList[i].name);
    378387        list_rating.push(boardList[i].rating);
    379388        list_rank.push(i+1);
    380389        list.push(boardList[i].name);
     
    400409    // to update the game info panel.
    401410    g_GameList = gameList;
    402411
    403412    // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'.
    404413    var gameStatuses = ['waiting', 'init', 'running'];
    405     g_GameList.sort(function (a,b) {
     414    g_GameList.sort((a,b) => {
    406415        switch (g_GameListSortBy)
    407416        {
    408417        case 'name':
    409418        case 'mapSize':
    410419            // mapSize contains the number of tiles for random maps
     
    467476                name = '[color="255 0 0"]' + name + '[/color]';
    468477            list_name.push(name);
    469478            list_ip.push(g.ip);
    470479            list_mapName.push(translate(g.niceMapName));
    471480            list_mapSize.push(translatedMapSize(g.mapSize));
    472             let idx = g_mapTypes.indexOf(g.mapType);
    473             list_mapType.push(idx != -1 ? g_mapTypesText[idx] : "");
     481            let idx = g_MapTypes.indexOf(g.mapType);
     482            list_mapType.push(idx != -1 ? g_MapTypesText[idx] : "");
    474483            list_nPlayers.push(g.nbp + "/" +g.tnbp);
    475484            list.push(name);
    476485            list_data.push(c);
    477486        }
    478487        c++;
     
    524533    case "offline":
    525534        color = "0 0 0";
    526535        status = translate("Offline");
    527536        break;
    528537    default:
    529         warn(sprintf("Unknown presence '%(presence)s'", { presence: presence }));
     538        warn("Unknown presence '" + presence + "'");
    530539        color = "178 178 178";
    531540        status = translateWithContext("lobby presence", "Unknown");
    532541        break;
    533542    }
    534543    // Center the unrated symbol.
     
    536545        rating = "    -";
    537546    var formattedStatus = '[color="' + color + '"]' + status + "[/color]";
    538547    var formattedRating = '[color="' + color + '"]' + rating + "[/color]";
    539548    var role = Engine.LobbyGetPlayerRole(nickname);
    540549    if (role == "moderator")
    541         nickname = g_modPrefix + nickname;
     550        nickname = g_ModPrefix + nickname;
    542551    var formattedName = colorPlayerName(nickname);
    543552
    544553    // Push this player's name and status onto the list
    545554    return [formattedName, formattedStatus, formattedRating];
    546555}
     
    552561function translatedMapSize(mapSize)
    553562{
    554563    if (+mapSize !== +mapSize) // NaN
    555564        return translate(mapSize);
    556565    else
    557         return g_mapSizes.shortNames[g_mapSizes.tiles.indexOf(+mapSize)];
     566        return g_MapSizes.shortNames[g_MapSizes.tiles.indexOf(+mapSize)];
    558567}
    559568
    560569/**
    561570 * Populate the game info area with information on the current game selection.
    562571 */
     
    575584    var mapData;
    576585    var g = Engine.GetGUIObjectByName("gamesBox").list_data[selected];
    577586
    578587    // Load map data
    579588    if (g_GameList[g].mapType == "random" && g_GameList[g].mapName == "random")
    580         mapData = {"settings": {"Description": translate("A randomly selected map.")}};
     589        mapData = { "settings": { "Description": translate("A randomly selected map.") } };
    581590    else if (g_GameList[g].mapType == "random" && Engine.FileExists(g_GameList[g].mapName + ".json"))
    582591        mapData = Engine.ReadJSONFile(g_GameList[g].mapName + ".json");
    583592    else if (Engine.FileExists(g_GameList[g].mapName + ".xml"))
    584593        mapData = Engine.LoadMapSettings(g_GameList[g].mapName + ".xml");
    585594    else
    586         // Warn the player if we can't find the map.
    587         warn(sprintf("Map '%(mapName)s' not found locally.", { mapName: g_GameList[g].mapName }));
     595        warn("Map '" + g_GameList[g].mapName + "' not found locally.");
    588596
    589597    // Show the game info panel and join button.
    590598    Engine.GetGUIObjectByName("gameInfo").hidden = false;
    591599    Engine.GetGUIObjectByName("joinGameButton").hidden = false;
    592600    Engine.GetGUIObjectByName("gameInfoEmpty").hidden = true;
     
    594602    // Display the map name, number of players, the names of the players, the map size and the map type.
    595603    Engine.GetGUIObjectByName("sgMapName").caption = translate(g_GameList[g].niceMapName);
    596604    Engine.GetGUIObjectByName("sgNbPlayers").caption = g_GameList[g].nbp + "/" + g_GameList[g].tnbp;
    597605    Engine.GetGUIObjectByName("sgPlayersNames").caption = g_GameList[g].players;
    598606    Engine.GetGUIObjectByName("sgMapSize").caption = translatedMapSize(g_GameList[g].mapSize);
    599     let idx = g_mapTypes.indexOf(g_GameList[g].mapType);
    600     Engine.GetGUIObjectByName("sgMapType").caption = idx != -1 ? g_mapTypesText[idx] : "";
     607    let idx = g_MapTypes.indexOf(g_GameList[g].mapType);
     608    Engine.GetGUIObjectByName("sgMapType").caption = idx != -1 ? g_MapTypesText[idx] : "";
    601609
    602610    // Display map description if it exists, otherwise display a placeholder.
    603611    if (mapData && mapData.settings.Description)
    604612        var mapDescription = translate(mapData.settings.Description);
    605613    else
     
    622630{
    623631    var gamesBox = Engine.GetGUIObjectByName("gamesBox");
    624632    if (gamesBox.selected >= 0)
    625633    {
    626634        var g = gamesBox.list_data[gamesBox.selected];
    627         var sname = g_Name;
     635        var sname = g_Username;
    628636        var sip = g_GameList[g].ip;
    629637
    630638        // TODO: What about valid host names?
    631639        // Check if it looks like an ip address
    632640        if (sip.split('.').length != 4)
    633641        {
    634             addChatMessage({ "from": "system", "text": sprintf(translate("This game's address '%(ip)s' does not appear to be valid."), { ip: sip }) });
     642            addChatMessage({ "from": "system", "text": sprintf(translate("This game's address '%(ip)s' does not appear to be valid."), { "ip": sip }) });
    635643            return;
    636644        }
    637645
    638646        // Open Multiplayer connection window with join option.
    639         Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "join", name: sname, ip: sip, rating: g_userRating });
     647        Engine.PushGuiPage("page_gamesetup_mp.xml", { "multiplayerGameType": "join", "name": sname, "ip": sip, "rating": g_UserRating });
    640648    }
    641649}
    642650
    643651/**
    644652 * Start the hosting process.
    645653 */
    646654function hostGame()
    647655{
    648656    // Open Multiplayer connection window with host option.
    649     Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "host", name: g_Name, rating: g_userRating });
     657    Engine.PushGuiPage("page_gamesetup_mp.xml", { "multiplayerGameType": "host", "name": g_Username, "rating": g_UserRating });
    650658}
    651659
    652660////////////////////////////////////////////////////////////////////////////////////////////////
    653661// Utils
    654662////////////////////////////////////////////////////////////////////////////////////////////////
     
    700708                var [name, status, rating] = formatPlayerListEntry(nick, presence, "-");
    701709                playerList.push(name);
    702710                presenceList.push(status);
    703711                nickList.push(nick);
    704712                ratingList.push(String(rating));
    705                 addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has joined."), { nick: nick }), "key": g_specialKey });
     713                addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has joined."), { "nick": nick }), "key": g_SpecialKey });
    706714                break;
    707715            case "leave":
    708716                if (nickIndex == -1) // Left, but not present (TODO: warn about this?)
    709717                    break;
    710718                playerList.splice(nickIndex, 1);
    711719                presenceList.splice(nickIndex, 1);
    712720                nickList.splice(nickIndex, 1);
    713721                ratingList.splice(nickIndex, 1);
    714                 addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has left."), { nick: nick }), "key": g_specialKey });
     722                addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has left."), { "nick": nick }), "key": g_SpecialKey });
    715723                break;
    716724            case "nick":
    717725                if (nickIndex == -1) // Changed nick, but not present (shouldn't ever happen)
    718726                    break;
    719727                if (!isValidNick(message.data))
    720728                {
    721                     addChatMessage({ "from": "system", "text": sprintf(translate("Invalid nickname: %(nick)s"), { nick: message.data })});
     729                    addChatMessage({ "from": "system", "text": sprintf(translate("Invalid nickname: %(nick)s"), { "nick": message.data }) });
    722730                    break;
    723731                }
    724732                var [name, status, rating] = formatPlayerListEntry(message.data, presence, stripColorCodes(ratingList[nickIndex])); // TODO: actually we don't want to change the presence here, so use what was used before
    725733                playerList[nickIndex] = name;
    726734                // presence stays the same
    727735                nickList[nickIndex] = message.data;
    728                 addChatMessage({ "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { oldnick: nick, newnick: message.data }), "key": g_specialKey });
     736                addChatMessage({ "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { "oldnick": nick, "newnick": message.data }), "key": g_SpecialKey });
    729737                break;
    730738            case "presence":
    731739                if (nickIndex == -1) // Changed presence, but not online (shouldn't ever happen)
    732740                    break;
    733741                var [name, status, rating] = formatPlayerListEntry(nick, presence, stripColorCodes(ratingList[nickIndex]));
     
    737745                break;
    738746            case "subject":
    739747                updateSubject(message.text);
    740748                break;
    741749            default:
    742                 warn(sprintf("Unknown message.level '%(msglvl)s'", { msglvl: message.level }));
     750                warn("Unknown message.level '" + message.level + "'");
    743751                break;
    744752            }
    745753            // Push new data to GUI
    746754            playersBox.list_name = playerList;
    747755            playersBox.list_status = presenceList;
    748756            playersBox.list_rating = ratingList;
    749             playersBox.list = nickList;     
     757            playersBox.list = nickList;
    750758            if (playersBox.selected >= playersBox.list.length)
    751759                playersBox.selected = -1;
    752760            break;
    753761        case "system":
    754762            switch (message.level)
     
    763771                    updatePlayerList();
    764772                    // Disable the 'host' button
    765773                    Engine.GetGUIObjectByName("hostButton").enabled = false;
    766774                }
    767775                else if (message.text == "connected")
    768                 {
    769776                    Engine.GetGUIObjectByName("hostButton").enabled = true;
    770                 }
     777
    771778                break;
    772779            case "error":
    773780                addChatMessage({ "from": "system", "text": text, "color": "150 0 0" });
    774781                break;
    775782            case "internal":
     
    790797                }
    791798                break;
    792799            }
    793800            break;
    794801        default:
    795             error(sprintf("Unrecognised message type %(msgtype)s", { msgtype: message.type }));
     802            error("Unrecognised message type '" + message.type + "'");
    796803        }
    797804    }
    798805}
    799806
    800 /* Messages */
     807function returnToMainMenu()
     808{
     809    lobbyStop();
     810    Engine.SwitchGuiPage("page_pregame.xml");
     811}
     812
    801813function submitChatInput()
    802814{
    803815    var input = Engine.GetGUIObjectByName("chatInput");
    804816    var text = input.caption;
    805817    if (text.length)
    806818    {
    807         if (!handleSpecialCommand(text) && !isSpam(text, g_Name))
     819        if (!handleSpecialCommand(text) && !isSpam(text, g_Username))
    808820            Engine.LobbySendMessage(text);
    809821        input.caption = "";
    810822    }
    811823}
    812824
     
    843855        break;
    844856    case "ban": // TODO: Split reason from nick and pass it too, for now just support "/ban nick"
    845857        Engine.LobbyBan(nick, "");
    846858        break;
    847859    case "quit":
    848         lobbyStop();
    849         Engine.SwitchGuiPage("page_pregame.xml");
     860        returnToMainMenu();
    850861        break;
    851862    case "say":
    852863    case "me":
    853864        return false;
    854865    default:
    855         addChatMessage({ "from":"system", "text": sprintf(translate("We're sorry, the '%(cmd)s' command is not supported."), { cmd: cmd})});
     866        addChatMessage({ "from":"system", "text": sprintf(translate("We're sorry, the '%(cmd)s' command is not supported."), { "cmd": cmd }) });
    856867    }
    857868    return true;
    858869}
    859870
    860871/**
     
    868879    if (msg.from)
    869880    {
    870881        // Display the moderator symbol in the chatbox.
    871882        var playerRole = Engine.LobbyGetPlayerRole(msg.from);
    872883        if (playerRole == "moderator")
    873             msg.from = g_modPrefix + msg.from;
     884            msg.from = g_ModPrefix + msg.from;
    874885    }
    875886    else
    876887        msg.from = null;
    877888    if (!msg.color)
    878889        msg.color = null;
     
    880891        msg.key = null;
    881892    if (!msg.datetime)
    882893        msg.datetime = null;
    883894
    884895    // Highlight local user's nick
    885     if (msg.text.indexOf(g_Name) != -1 && g_Name != msg.from)
    886         msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), colorPlayerName(g_Name));
     896    if (msg.text.indexOf(g_Username) != -1 && g_Username != msg.from)
     897        msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Username + '\\b', "g"), colorPlayerName(g_Username));
    887898
    888899    // Run spam test if it's not a historical message
    889900    if (!msg.datetime)
    890901        updateSpamMonitor(msg.from);
    891902    if (isSpam(msg.text, msg.from))
     
    934945        var [command, message] = ircSplit(text);
    935946        switch (command)
    936947        {
    937948            case "me":
    938949                // Translation: IRC message prefix when the sender uses the /me command.
    939                 var senderString = '[font="sans-bold-13"]' + sprintf(translate("* %(sender)s"), { sender: coloredFrom }) + '[/font]';
     950                var senderString = '[font="sans-bold-13"]' + sprintf(translate("* %(sender)s"), { "sender": coloredFrom }) + '[/font]';
    940951                // Translation: IRC message issued using the ‘/me’ command.
    941                 var formattedMessage = sprintf(translate("%(sender)s %(action)s"), { sender: senderString, action: message });
     952                var formattedMessage = sprintf(translate("%(sender)s %(action)s"), { "sender": senderString, "action": message });
    942953                break;
    943954            case "say":
    944955                // Translation: IRC message prefix.
    945                 var senderString = '[font="sans-bold-13"]' + sprintf(translate("<%(sender)s>"), { sender: coloredFrom }) + '[/font]';
     956                var senderString = '[font="sans-bold-13"]' + sprintf(translate("<%(sender)s>"), { "sender": coloredFrom }) + '[/font]';
    946957                // Translation: IRC message.
    947                 var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { sender: senderString, message: message });
     958                var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { "sender": senderString, "message": message });
    948959                break
    949960            case "special":
    950                 if (key === g_specialKey)
     961                if (key === g_SpecialKey)
    951962                    // Translation: IRC system message.
    952                     var formattedMessage = '[font="sans-bold-13"]' + sprintf(translate("== %(message)s"), { message: message }) + '[/font]';
     963                    var formattedMessage = '[font="sans-bold-13"]' + sprintf(translate("== %(message)s"), { "message": message }) + '[/font]';
    953964                else
    954965                {
    955966                    // Translation: IRC message prefix.
    956                     var senderString = '[font="sans-bold-13"]' + sprintf(translate("<%(sender)s>"), { sender: coloredFrom }) + '[/font]';
     967                    var senderString = '[font="sans-bold-13"]' + sprintf(translate("<%(sender)s>"), { "sender": coloredFrom }) + '[/font]';
    957968                    // Translation: IRC message.
    958                     var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { sender: senderString, message: message });
     969                    var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { "sender": senderString, "message": message });
    959970                }
    960971                break;
    961972            default:
    962973                // This should never happen.
    963974                var formattedMessage = "";
    964975        }
    965976    }
    966977    else
    967978    {
    968979        // Translation: IRC message prefix.
    969         var senderString = '[font="sans-bold-13"]' + sprintf(translate("<%(sender)s>"), { sender: coloredFrom }) + '[/font]';
     980        var senderString = '[font="sans-bold-13"]' + sprintf(translate("<%(sender)s>"), { "sender": coloredFrom }) + '[/font]';
    970981        // Translation: IRC message.
    971         var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { sender: senderString, message: text });
     982        var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { "sender": senderString, "message": text });
    972983    }
    973984
    974985    // Build time header if enabled
    975     if (g_timestamp)
     986    if (g_ShowTimestamp)
    976987    {
    977988
    978989        var time;
    979990        if (datetime)
    980991        {
     
    9911002        // For a list of symbols that you can use, see:
    9921003        // https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table
    9931004        var timeString = Engine.FormatMillisecondsIntoDateString(time.getTime(), translate("HH:mm"));
    9941005
    9951006        // Translation: Time prefix as shown in the multiplayer lobby (when you enable it in the options page).
    996         var timePrefixString = '[font="sans-bold-13"]' + sprintf(translate("\\[%(time)s]"), { time: timeString }) + '[/font]';
     1007        var timePrefixString = '[font="sans-bold-13"]' + sprintf(translate("\\[%(time)s]"), { "time": timeString }) + '[/font]';
    9971008
    9981009        // Translation: IRC message format when there is a time prefix.
    999         return sprintf(translate("%(time)s %(message)s"), { time: timePrefixString, message: formattedMessage });
     1010        return sprintf(translate("%(time)s %(message)s"), { "time": timePrefixString, "message": formattedMessage });
    10001011    }
    10011012    else
    10021013        return formattedMessage;
    10031014}
    10041015
     
    10111022{
    10121023    // Integer time in seconds.
    10131024    var time = Math.floor(Date.now() / 1000);
    10141025
    10151026    // Update or initialize the user in the spam monitor.
    1016     if (g_spamMonitor[from])
    1017         g_spamMonitor[from][0]++;
     1027    if (g_SpamMonitor[from])
     1028        ++g_SpamMonitor[from][0];
    10181029    else
    1019         g_spamMonitor[from] = [1, time, 0];
     1030        g_SpamMonitor[from] = [1, time, 0];
    10201031}
    10211032
    10221033/**
    10231034 * Check if a message is spam.
    10241035 *
     
    10301041{
    10311042    // Integer time in seconds.
    10321043    var time = Math.floor(Date.now() / 1000);
    10331044
    10341045    // Initialize if not already in the database.
    1035     if (!g_spamMonitor[from])
    1036         g_spamMonitor[from] = [1, time, 0];
     1046    if (!g_SpamMonitor[from])
     1047        g_SpamMonitor[from] = [1, time, 0];
    10371048
    10381049    // Block blank lines.
    10391050    if (text == " ")
    10401051        return true;
    10411052    // Block users who are still within their spam block period.
    1042     else if (g_spamMonitor[from][2] + SPAM_BLOCK_LENGTH >= time)
     1053    else if (g_SpamMonitor[from][2] + g_SpamBlockLength >= time)
    10431054        return true;
    10441055    // Block users who exceed the rate of 1 message per second for five seconds and are not already blocked. TODO: Make this smarter and block profanity.
    1045     else if (g_spamMonitor[from][0] == 6)
     1056    else if (g_SpamMonitor[from][0] == 6)
    10461057    {
    1047         g_spamMonitor[from][2] = time;
    1048         if (from == g_Name)
     1058        g_SpamMonitor[from][2] = time;
     1059        if (from == g_Username)
    10491060            addChatMessage({ "from": "system", "text": translate("Please do not spam. You have been blocked for thirty seconds.") });
    10501061        return true;
    10511062    }
    10521063    // Return false if everything is clear.
    10531064    else
     
    10611072{
    10621073    // Integer time in seconds.
    10631074    var time = Math.floor(Date.now() / 1000);
    10641075
    10651076    // Clear message count every 5 seconds.
    1066     for each (var stats in g_spamMonitor)
     1077    for (let i in g_SpamMonitor)
    10671078    {
    1068         if (stats[1] + 5 <= time)
     1079        if (g_SpamMonitor[i][1] + 5 <= time)
    10691080        {
    1070             stats[1] = time;
    1071             stats[0] = 0;
     1081            g_SpamMonitor[i][1] = time;
     1082            g_SpamMonitor[i][0] = 0;
    10721083        }
    10731084    }
    1074 
    10751085}
    10761086
    10771087/* Utilities */
    1078 // Generate a (mostly) unique color for this player based on their name.
    1079 // See http://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-jquery-javascript
     1088/**
     1089 * Generate a (mostly) unique color for this player based on their name.
     1090 * See http://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-jquery-javascript
     1091 */
    10801092function getPlayerColor(playername)
    10811093{
    10821094    // Generate a probably-unique hash for the player name and use that to create a color.
    10831095    var hash = 0;
    1084     for (var i = 0; i < playername.length; i++)
     1096    for (let i = 0; i < playername.length; ++i)
    10851097        hash = playername.charCodeAt(i) + ((hash << 5) - hash);
    10861098
    10871099    // First create the color in RGB then HSL, clamp the lightness so it's not too dark to read, and then convert back to RGB to display.
    10881100    // The reason for this roundabout method is this algorithm can generate values from 0 to 255 for RGB but only 0 to 100 for HSL; this gives
    10891101    // us much more variety if we generate in RGB. Unfortunately, enforcing that RGB values are a certain lightness is very difficult, so
     
    10941106
    10951107function repeatString(times, string) {
    10961108    return Array(times + 1).join(string);
    10971109}
    10981110
    1099 // Some names are special and should always appear in certain colors.
    1100 var fixedColors = { "system": repeatString(7, "255.0.0."), "@WFGbot": repeatString(7, "255.24.24."),
    1101                     "pyrogenesis": repeatString(2, "97.0.0.") + repeatString(2, "124.0.0.") + "138.0.0." +
    1102                         repeatString(2, "174.0.0.") + repeatString(2, "229.40.0.") + repeatString(2, "243.125.15.") };
    11031111function colorPlayerName(playername)
    11041112{
    1105     var color = fixedColors[playername];
    1106     if (color) {
    1107     color = color.split(".");
    1108     return ('[color="' + playername.split("").map(function (c, i) color.slice(i * 3, i * 3 + 3).join(" ") + '"]' + c + '[/color][color="')
    1109                 .join("") + '"]').slice(0, -10);
     1113    var color = g_FixedColors[playername];
     1114    if (color)
     1115    {
     1116        color = color.split(".");
     1117        return ('[color="' + playername.split("").map((c, i) => color.slice(i * 3, i * 3 + 3).join(" ") + '"]' + c + '[/color][color="')
     1118            .join("") + '"]').slice(0, -10);
    11101119    }
    1111     return '[color="' + getPlayerColor(playername.replace(g_modPrefix, "")) + '"]' + playername + '[/color]';
     1120    return '[color="' + getPlayerColor(playername.replace(g_ModPrefix, "")) + '"]' + playername + '[/color]';
    11121121}
    11131122
    1114 // Ensure `value` is between 0 and 1.
     1123/**
     1124 * Ensure `value` is between 0 and 1.
     1125 */
    11151126function clampColorValue(value)
    11161127{
    11171128    return Math.abs(1 - Math.abs(value - 1));
    11181129}
    11191130
    1120 // See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
     1131/**
     1132 * See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
     1133 */
    11211134function rgbToHsl(r, g, b)
    11221135{
    11231136    r /= 255;
    11241137    g /= 255;
    11251138    b /= 255;
     
    11671180        r = hue2rgb(p, q, h + 1/3);
    11681181        g = hue2rgb(p, q, h);
    11691182        b = hue2rgb(p, q, h - 1/3);
    11701183    }
    11711184
    1172     return [r, g, b].map(function (n) Math.round(n * 255));
     1185    return [r, g, b].map(n => Math.round(n * 255));
    11731186}
    1174 
    1175 (function () {
    1176 function hexToRgb(hex) {
    1177     return parseInt(hex.slice(0, 2), 16) + "." + parseInt(hex.slice(2, 4), 16) + "." + parseInt(hex.slice(4, 6), 16) + ".";
    1178 }
    1179 function r(times, hex) {
    1180     return repeatString(times, hexToRgb(hex));
    1181 }
    1182 
    1183 fixedColors["Twilight_Sparkle"] = r(2, "d19fe3") + r(2, "b689c8") + r(2, "a76bc2") +
    1184     r(4, "263773") + r(2, "131f46") + r(2, "662d8a") + r(2, "ed438a");
    1185 fixedColors["Applejack"] = r(3, "ffc261") + r(3, "efb05d") + r(3, "f26f31");
    1186 fixedColors["Rarity"] = r(1, "ebeff1") + r(1, "dee3e4") + r(1, "bec2c3") +
    1187     r(1, "83509f") + r(1, "4b2568") + r(1, "4917d6");
    1188 fixedColors["Rainbow_Dash"] = r(2, "ee4144") + r(1, "f37033") + r(1, "fdf6af") +
    1189     r(1, "62bc4d") + r(1, "1e98d3") + r(2, "672f89") + r(1, "9edbf9") +
    1190     r(1, "88c4eb") + r(1, "77b0e0") + r(1, "1e98d3");
    1191 fixedColors["Pinkie_Pie"] = r(2, "f3b6cf") + r(2, "ec9dc4") + r(4, "eb81b4") +
    1192     r(1, "ed458b") + r(1, "be1d77");
    1193 fixedColors["Fluttershy"] = r(2, "fdf6af") + r(2, "fee78f") + r(2, "ead463") +
    1194     r(2, "f3b6cf") + r(2, "eb81b4");
    1195 fixedColors["Sweetie_Belle"] = r(2, "efedee") + r(3, "e2dee3") + r(3, "cfc8d1") +
    1196     r(2, "b28dc0") + r(2, "f6b8d2") + r(1, "795b8a");
    1197 fixedColors["Apple_Bloom"] = r(2, "f4f49b") + r(2, "e7e793") + r(2, "dac582") +
    1198     r(2, "f46091") + r(2, "f8415f") + r(1, "c52451");
    1199 fixedColors["Scootaloo"] = r(2, "fbba64") + r(2, "f2ab56") + r(2, "f37003") +
    1200     r(2, "bf5d95") + r(1, "bf1f79");
    1201 fixedColors["Luna"] = r(1, "7ca7fa") + r(1, "5d6fc1") + r(1, "656cb9") + r(1, "393993");
    1202 fixedColors["Celestia"] = r(1, "fdfafc") + r(1, "f7eaf2") + r(1, "d99ec5") +
    1203     r(1, "00aec5") + r(1, "f7c6dc") + r(1, "98d9ef") + r(1, "ced7ed") + r(1, "fed17b");
    1204 })();
  • binaries/data/mods/public/gui/lobby/lobby.xml

     
    161161                </action>
    162162            </object>
    163163
    164164            <object type="button" style="ModernButtonRed" size="0 100%-25 100% 100%">
    165165                <translatableAttribute id="caption">Main Menu</translatableAttribute>
    166                 <action on="Press">
    167                     lobbyStop();
    168                     Engine.SwitchGuiPage("page_pregame.xml");
    169                 </action>
     166                <action on="Press">returnToMainMenu();</action>
    170167            </object>
    171168        </object>
    172169
    173170        <!-- Middle panel: Filters, game list, chat box. -->
    174171        <object name="middlePanel" size="20%+5 5% 100%-255 97.2%">
  • binaries/data/mods/public/gui/session/messages.js

     
    366366        return;
    367367
    368368    if (Engine.GetGUIObjectByName("toggleTeamChat").checked)
    369369        text = "/team " + text;
    370370
    371     if (g_IsNetworked)
    372         Engine.SendNetworkChat(text);
    373     else
    374         addChatMessage({ "type": "message", "guid": "local", "text": text });
     371    submitChatDirectly(text);
    375372}
    376373
    377374function addChatMessage(msg)
    378375{
    379376    var playerColor, username;