Ticket #3383: lobby_coding_convention_v2.patch
File lobby_coding_convention_v2.patch, 33.4 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/public/gui/lobby/lobby.js
1 var g_MapSizes = {}; 2 const g_MapTypesText = [translateWithContext("map", "Skirmish"), translateWithContext("map", "Random"), translate("Scenario")]; 3 const g_MapTypes = ["skirmish", "random", "scenario"]; 4 1 5 var g_ChatMessages = []; 2 var g_Name = "unknown"; 6 const g_ShowTimestamp = Engine.ConfigDB_GetValue("user", "lobby.chattimestamp") == "true"; 7 8 /** This object looks like { "name": [numMessagesSinceReset, lastReset, timeBlocked] } when in use. */ 9 var g_SpamMonitor = {}; 10 const 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 */ 16 const g_SpecialKey = Math.random(); 17 18 const g_ModPrefix = "@"; 19 var g_Username = "unknown"; 20 var g_UserRating = ""; 21 22 const g_FixedColors = { 23 "system": repeatString(7, "255.0.0."), 24 "@WFGbot": repeatString(7, "255.24.24.") 25 }; 26 3 27 var g_GameList = {} 28 /** Name of the column to be used for sorting. */ 4 29 var g_GameListSortBy = "name"; 30 /** Sorting order: +1 ascending, -1 descending. */ 31 var g_GameListOrder = 1; 32 5 33 var g_PlayerListSortBy = "name"; 6 var g_GameListOrder = 1; // 1 for ascending sort, and -1 for descending7 34 var 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 Unrated16 var g_modPrefix = "@";17 // Block spammers for 30 seconds.18 var SPAM_BLOCK_LENGTH = 30;19 35 20 36 //////////////////////////////////////////////////////////////////////////////////////////////// 21 37 22 38 function init(attribs) 23 39 { 24 40 // Play menu music 25 41 initMusic(); 26 42 global.music.setState(global.music.states.MENU); 27 43 28 g_ Name = Engine.LobbyGetNick();44 g_Username = Engine.LobbyGetNick(); 29 45 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, ""); 33 49 34 50 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; 37 53 38 54 var playersNumberFilter = Engine.GetGUIObjectByName("playersNumberFilter"); 39 55 playersNumberFilter.list = [translateWithContext("player number", "Any"),2,3,4,5,6,7,8]; 40 56 playersNumberFilter.list_data = ["",2,3,4,5,6,7,8]; 41 57 42 58 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); 45 61 46 62 Engine.LobbySetPlayerPresence("available"); 47 63 Engine.SendGetGameList(); 48 64 Engine.SendGetBoardList(); 49 65 updatePlayerList(); … … 103 119 applyFilters(); 104 120 } 105 121 106 122 function applyFilters() 107 123 { 108 // Update the list of games109 124 updateGameList(); 110 111 // Update info box about the game currently selected112 125 updateGameSelection(); 113 126 } 114 127 115 128 /** 116 129 * Filter a game based on the status of the filter dropdowns. … … 174 187 var presenceList = []; 175 188 var nickList = []; 176 189 var ratingList = []; 177 190 var cleanPlayerList = Engine.GetPlayerList(); 178 191 // Sort the player list, ignoring case. 179 cleanPlayerList.sort( function(a,b)192 cleanPlayerList.sort((a,b) => 180 193 { 181 194 switch (g_PlayerListSortBy) 182 195 { 183 196 case 'rating': 184 197 if (a.rating < b.rating) … … 204 217 else if (aName > bName) 205 218 return g_PlayerListOrder; 206 219 return 0; 207 220 } 208 221 }); 209 for ( var i = 0; i < cleanPlayerList.length; i++)222 for (let i = 0; i < cleanPlayerList.length; ++i) 210 223 { 211 224 // 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; 214 227 // Add a "-" for unrated players. 215 228 if (!cleanPlayerList[i].rating) 216 229 cleanPlayerList[i].rating = "-"; 217 230 // Colorize. 218 231 var [name, status, rating] = formatPlayerListEntry(cleanPlayerList[i].name, cleanPlayerList[i].presence, cleanPlayerList[i].rating, cleanPlayerList[i].role); 219 232 // Push to lists. 220 233 playerList.push(name); 221 234 presenceList.push(status); 222 235 nickList.push(cleanPlayerList[i].name); 223 236 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) 225 238 ratingSpaces += " "; 226 239 ratingList.push(String(ratingSpaces + rating)); 227 240 } 228 241 playersBox.list_name = playerList; 229 242 playersBox.list_status = presenceList; … … 236 249 237 250 /** 238 251 * Display the profile of the selected player. 239 252 * Displays N/A for all stats until updateProfile is called when the stats 240 253 * are actually received from the bot. 241 * 254 * 242 255 * @param caller From which screen is the user requesting data from? 243 256 */ 244 257 function displayProfile(caller) 245 258 { 246 259 var playerList, rating; … … 261 274 Engine.GetGUIObjectByName("profileArea").hidden = true; 262 275 return; 263 276 } 264 277 Engine.GetGUIObjectByName("profileArea").hidden = false; 265 278 266 Engine.SendGetProfile(playerList.list[playerList.selected]); 279 Engine.SendGetProfile(playerList.list[playerList.selected]); 267 280 268 281 var user = playerList.list_name[playerList.selected]; 269 282 var role = Engine.LobbyGetPlayerRole(playerList.list[playerList.selected]); 270 283 var userList = Engine.GetGUIObjectByName("playersBox"); 271 284 if (role && caller == "lobbylist") … … 288 301 Engine.GetGUIObjectByName("ratioText").caption = translate("N/A"); 289 302 } 290 303 291 304 /** 292 305 * Update the profile of the selected player with data from the bot. 293 *294 306 */ 295 307 function updateProfile() 296 308 { 297 309 var playerList, user; 298 310 var attributes = Engine.GetProfile(); … … 306 318 Engine.GetGUIObjectByName("profileErrorText").hidden = false; 307 319 return; 308 320 } 309 321 Engine.GetGUIObjectByName("profileWindowArea").hidden = false; 310 322 Engine.GetGUIObjectByName("profileErrorText").hidden = true; 311 323 312 324 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 }); 314 326 315 327 Engine.GetGUIObjectByName("profileUsernameText").caption = user; 316 328 Engine.GetGUIObjectByName("profileRankText").caption = attributes[0].rank; 317 329 Engine.GetGUIObjectByName("profileHighestRatingText").caption = attributes[0].highestRating; 318 330 Engine.GetGUIObjectByName("profileTotalGamesText").caption = attributes[0].totalGamesPlayed; 319 331 Engine.GetGUIObjectByName("profileWinsText").caption = attributes[0].wins; 320 332 Engine.GetGUIObjectByName("profileLossesText").caption = attributes[0].losses; 321 333 322 334 var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2); 323 335 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 }); 325 337 else 326 338 Engine.GetGUIObjectByName("profileRatioText").caption = translateWithContext("Used for an undefined winning rate", "-"); 327 339 return; 328 340 } 329 341 else if (!Engine.GetGUIObjectByName("leaderboard").hidden) 330 342 playerList = Engine.GetGUIObjectByName("leaderboardBox"); 331 343 else 332 344 playerList = Engine.GetGUIObjectByName("playersBox"); 333 345 334 346 if (attributes[0].rating == "-2") 335 347 return; 336 348 // Make sure the stats we have received coincide with the selected player. 337 349 if (attributes[0].player != playerList.list[playerList.selected]) 338 350 return; 339 351 user = playerList.list_name[playerList.selected]; 340 352 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 }); 342 354 343 355 Engine.GetGUIObjectByName("usernameText").caption = user; 344 356 Engine.GetGUIObjectByName("rankText").caption = attributes[0].rank; 345 357 Engine.GetGUIObjectByName("highestRatingText").caption = attributes[0].highestRating; 346 358 Engine.GetGUIObjectByName("totalGamesText").caption = attributes[0].totalGamesPlayed; 347 359 Engine.GetGUIObjectByName("winsText").caption = attributes[0].wins; 348 360 Engine.GetGUIObjectByName("lossesText").caption = attributes[0].losses; 349 361 350 362 var winRate = (attributes[0].wins / attributes[0].totalGamesPlayed * 100).toFixed(2); 351 363 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 }); 353 365 else 354 366 Engine.GetGUIObjectByName("ratioText").caption = translateWithContext("Used for an undefined winning rate", "-"); 355 367 } 356 368 357 369 /** 358 370 * Update the leaderboard from data cached in C++. 359 371 */ 360 372 function updateLeaderboard() 361 373 { 362 // Get list from C++363 var boardList = Engine.GetBoardList();364 // Get GUI leaderboard object365 374 var leaderboard = Engine.GetGUIObjectByName("leaderboardBox"); 366 // Sort list in acending order by rating367 boardList.sort( function(a, b)b.rating - a.rating);375 var boardList = Engine.GetBoardList(); 376 boardList.sort((a, b) => b.rating - a.rating); 368 377 369 378 var list = []; 370 379 var list_name = []; 371 380 var list_rank = []; 372 381 var list_rating = []; 373 382 374 383 // Push changes 375 for ( var i = 0; i < boardList.length; i++)384 for (let i = 0; i < boardList.length; ++i) 376 385 { 377 386 list_name.push(boardList[i].name); 378 387 list_rating.push(boardList[i].rating); 379 388 list_rank.push(i+1); 380 389 list.push(boardList[i].name); … … 400 409 // to update the game info panel. 401 410 g_GameList = gameList; 402 411 403 412 // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'. 404 413 var gameStatuses = ['waiting', 'init', 'running']; 405 g_GameList.sort( function (a,b){414 g_GameList.sort((a,b) => { 406 415 switch (g_GameListSortBy) 407 416 { 408 417 case 'name': 409 418 case 'mapSize': 410 419 // mapSize contains the number of tiles for random maps … … 467 476 name = '[color="255 0 0"]' + name + '[/color]'; 468 477 list_name.push(name); 469 478 list_ip.push(g.ip); 470 479 list_mapName.push(translate(g.niceMapName)); 471 480 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] : ""); 474 483 list_nPlayers.push(g.nbp + "/" +g.tnbp); 475 484 list.push(name); 476 485 list_data.push(c); 477 486 } 478 487 c++; … … 524 533 case "offline": 525 534 color = "0 0 0"; 526 535 status = translate("Offline"); 527 536 break; 528 537 default: 529 warn( sprintf("Unknown presence '%(presence)s'", { presence: presence }));538 warn("Unknown presence '" + presence + "'"); 530 539 color = "178 178 178"; 531 540 status = translateWithContext("lobby presence", "Unknown"); 532 541 break; 533 542 } 534 543 // Center the unrated symbol. … … 536 545 rating = " -"; 537 546 var formattedStatus = '[color="' + color + '"]' + status + "[/color]"; 538 547 var formattedRating = '[color="' + color + '"]' + rating + "[/color]"; 539 548 var role = Engine.LobbyGetPlayerRole(nickname); 540 549 if (role == "moderator") 541 nickname = g_ modPrefix + nickname;550 nickname = g_ModPrefix + nickname; 542 551 var formattedName = colorPlayerName(nickname); 543 552 544 553 // Push this player's name and status onto the list 545 554 return [formattedName, formattedStatus, formattedRating]; 546 555 } … … 552 561 function translatedMapSize(mapSize) 553 562 { 554 563 if (+mapSize !== +mapSize) // NaN 555 564 return translate(mapSize); 556 565 else 557 return g_ mapSizes.shortNames[g_mapSizes.tiles.indexOf(+mapSize)];566 return g_MapSizes.shortNames[g_MapSizes.tiles.indexOf(+mapSize)]; 558 567 } 559 568 560 569 /** 561 570 * Populate the game info area with information on the current game selection. 562 571 */ … … 575 584 var mapData; 576 585 var g = Engine.GetGUIObjectByName("gamesBox").list_data[selected]; 577 586 578 587 // Load map data 579 588 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.") } }; 581 590 else if (g_GameList[g].mapType == "random" && Engine.FileExists(g_GameList[g].mapName + ".json")) 582 591 mapData = Engine.ReadJSONFile(g_GameList[g].mapName + ".json"); 583 592 else if (Engine.FileExists(g_GameList[g].mapName + ".xml")) 584 593 mapData = Engine.LoadMapSettings(g_GameList[g].mapName + ".xml"); 585 594 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."); 588 596 589 597 // Show the game info panel and join button. 590 598 Engine.GetGUIObjectByName("gameInfo").hidden = false; 591 599 Engine.GetGUIObjectByName("joinGameButton").hidden = false; 592 600 Engine.GetGUIObjectByName("gameInfoEmpty").hidden = true; … … 594 602 // Display the map name, number of players, the names of the players, the map size and the map type. 595 603 Engine.GetGUIObjectByName("sgMapName").caption = translate(g_GameList[g].niceMapName); 596 604 Engine.GetGUIObjectByName("sgNbPlayers").caption = g_GameList[g].nbp + "/" + g_GameList[g].tnbp; 597 605 Engine.GetGUIObjectByName("sgPlayersNames").caption = g_GameList[g].players; 598 606 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] : ""; 601 609 602 610 // Display map description if it exists, otherwise display a placeholder. 603 611 if (mapData && mapData.settings.Description) 604 612 var mapDescription = translate(mapData.settings.Description); 605 613 else … … 622 630 { 623 631 var gamesBox = Engine.GetGUIObjectByName("gamesBox"); 624 632 if (gamesBox.selected >= 0) 625 633 { 626 634 var g = gamesBox.list_data[gamesBox.selected]; 627 var sname = g_ Name;635 var sname = g_Username; 628 636 var sip = g_GameList[g].ip; 629 637 630 638 // TODO: What about valid host names? 631 639 // Check if it looks like an ip address 632 640 if (sip.split('.').length != 4) 633 641 { 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 }) }); 635 643 return; 636 644 } 637 645 638 646 // 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 }); 640 648 } 641 649 } 642 650 643 651 /** 644 652 * Start the hosting process. 645 653 */ 646 654 function hostGame() 647 655 { 648 656 // 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 }); 650 658 } 651 659 652 660 //////////////////////////////////////////////////////////////////////////////////////////////// 653 661 // Utils 654 662 //////////////////////////////////////////////////////////////////////////////////////////////// … … 700 708 var [name, status, rating] = formatPlayerListEntry(nick, presence, "-"); 701 709 playerList.push(name); 702 710 presenceList.push(status); 703 711 nickList.push(nick); 704 712 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 }); 706 714 break; 707 715 case "leave": 708 716 if (nickIndex == -1) // Left, but not present (TODO: warn about this?) 709 717 break; 710 718 playerList.splice(nickIndex, 1); 711 719 presenceList.splice(nickIndex, 1); 712 720 nickList.splice(nickIndex, 1); 713 721 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 }); 715 723 break; 716 724 case "nick": 717 725 if (nickIndex == -1) // Changed nick, but not present (shouldn't ever happen) 718 726 break; 719 727 if (!isValidNick(message.data)) 720 728 { 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 }) }); 722 730 break; 723 731 } 724 732 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 725 733 playerList[nickIndex] = name; 726 734 // presence stays the same 727 735 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 }); 729 737 break; 730 738 case "presence": 731 739 if (nickIndex == -1) // Changed presence, but not online (shouldn't ever happen) 732 740 break; 733 741 var [name, status, rating] = formatPlayerListEntry(nick, presence, stripColorCodes(ratingList[nickIndex])); … … 737 745 break; 738 746 case "subject": 739 747 updateSubject(message.text); 740 748 break; 741 749 default: 742 warn( sprintf("Unknown message.level '%(msglvl)s'", { msglvl: message.level }));750 warn("Unknown message.level '" + message.level + "'"); 743 751 break; 744 752 } 745 753 // Push new data to GUI 746 754 playersBox.list_name = playerList; 747 755 playersBox.list_status = presenceList; 748 756 playersBox.list_rating = ratingList; 749 playersBox.list = nickList; 757 playersBox.list = nickList; 750 758 if (playersBox.selected >= playersBox.list.length) 751 759 playersBox.selected = -1; 752 760 break; 753 761 case "system": 754 762 switch (message.level) … … 763 771 updatePlayerList(); 764 772 // Disable the 'host' button 765 773 Engine.GetGUIObjectByName("hostButton").enabled = false; 766 774 } 767 775 else if (message.text == "connected") 768 {769 776 Engine.GetGUIObjectByName("hostButton").enabled = true; 770 } 777 771 778 break; 772 779 case "error": 773 780 addChatMessage({ "from": "system", "text": text, "color": "150 0 0" }); 774 781 break; 775 782 case "internal": … … 790 797 } 791 798 break; 792 799 } 793 800 break; 794 801 default: 795 error( sprintf("Unrecognised message type %(msgtype)s", { msgtype: message.type }));802 error("Unrecognised message type '" + message.type + "'"); 796 803 } 797 804 } 798 805 } 799 806 800 /* Messages */ 807 function returnToMainMenu() 808 { 809 lobbyStop(); 810 Engine.SwitchGuiPage("page_pregame.xml"); 811 } 812 801 813 function submitChatInput() 802 814 { 803 815 var input = Engine.GetGUIObjectByName("chatInput"); 804 816 var text = input.caption; 805 817 if (text.length) 806 818 { 807 if (!handleSpecialCommand(text) && !isSpam(text, g_ Name))819 if (!handleSpecialCommand(text) && !isSpam(text, g_Username)) 808 820 Engine.LobbySendMessage(text); 809 821 input.caption = ""; 810 822 } 811 823 } 812 824 … … 843 855 break; 844 856 case "ban": // TODO: Split reason from nick and pass it too, for now just support "/ban nick" 845 857 Engine.LobbyBan(nick, ""); 846 858 break; 847 859 case "quit": 848 lobbyStop(); 849 Engine.SwitchGuiPage("page_pregame.xml"); 860 returnToMainMenu(); 850 861 break; 851 862 case "say": 852 863 case "me": 853 864 return false; 854 865 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 }) }); 856 867 } 857 868 return true; 858 869 } 859 870 860 871 /** … … 868 879 if (msg.from) 869 880 { 870 881 // Display the moderator symbol in the chatbox. 871 882 var playerRole = Engine.LobbyGetPlayerRole(msg.from); 872 883 if (playerRole == "moderator") 873 msg.from = g_ modPrefix + msg.from;884 msg.from = g_ModPrefix + msg.from; 874 885 } 875 886 else 876 887 msg.from = null; 877 888 if (!msg.color) 878 889 msg.color = null; … … 880 891 msg.key = null; 881 892 if (!msg.datetime) 882 893 msg.datetime = null; 883 894 884 895 // 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)); 887 898 888 899 // Run spam test if it's not a historical message 889 900 if (!msg.datetime) 890 901 updateSpamMonitor(msg.from); 891 902 if (isSpam(msg.text, msg.from)) … … 934 945 var [command, message] = ircSplit(text); 935 946 switch (command) 936 947 { 937 948 case "me": 938 949 // 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]'; 940 951 // 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 }); 942 953 break; 943 954 case "say": 944 955 // 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]'; 946 957 // 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 }); 948 959 break 949 960 case "special": 950 if (key === g_ specialKey)961 if (key === g_SpecialKey) 951 962 // 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]'; 953 964 else 954 965 { 955 966 // 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]'; 957 968 // 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 }); 959 970 } 960 971 break; 961 972 default: 962 973 // This should never happen. 963 974 var formattedMessage = ""; 964 975 } 965 976 } 966 977 else 967 978 { 968 979 // 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]'; 970 981 // 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 }); 972 983 } 973 984 974 985 // Build time header if enabled 975 if (g_ timestamp)986 if (g_ShowTimestamp) 976 987 { 977 988 978 989 var time; 979 990 if (datetime) 980 991 { … … 991 1002 // For a list of symbols that you can use, see: 992 1003 // https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table 993 1004 var timeString = Engine.FormatMillisecondsIntoDateString(time.getTime(), translate("HH:mm")); 994 1005 995 1006 // 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]'; 997 1008 998 1009 // 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 }); 1000 1011 } 1001 1012 else 1002 1013 return formattedMessage; 1003 1014 } 1004 1015 … … 1011 1022 { 1012 1023 // Integer time in seconds. 1013 1024 var time = Math.floor(Date.now() / 1000); 1014 1025 1015 1026 // 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]; 1018 1029 else 1019 g_ spamMonitor[from] = [1, time, 0];1030 g_SpamMonitor[from] = [1, time, 0]; 1020 1031 } 1021 1032 1022 1033 /** 1023 1034 * Check if a message is spam. 1024 1035 * … … 1030 1041 { 1031 1042 // Integer time in seconds. 1032 1043 var time = Math.floor(Date.now() / 1000); 1033 1044 1034 1045 // 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]; 1037 1048 1038 1049 // Block blank lines. 1039 1050 if (text == " ") 1040 1051 return true; 1041 1052 // 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) 1043 1054 return true; 1044 1055 // 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) 1046 1057 { 1047 g_ spamMonitor[from][2] = time;1048 if (from == g_ Name)1058 g_SpamMonitor[from][2] = time; 1059 if (from == g_Username) 1049 1060 addChatMessage({ "from": "system", "text": translate("Please do not spam. You have been blocked for thirty seconds.") }); 1050 1061 return true; 1051 1062 } 1052 1063 // Return false if everything is clear. 1053 1064 else … … 1061 1072 { 1062 1073 // Integer time in seconds. 1063 1074 var time = Math.floor(Date.now() / 1000); 1064 1075 1065 1076 // Clear message count every 5 seconds. 1066 for each (var stats in g_spamMonitor)1077 for (let i in g_SpamMonitor) 1067 1078 { 1068 if ( stats[1] + 5 <= time)1079 if (g_SpamMonitor[i][1] + 5 <= time) 1069 1080 { 1070 stats[1] = time;1071 stats[0] = 0;1081 g_SpamMonitor[i][1] = time; 1082 g_SpamMonitor[i][0] = 0; 1072 1083 } 1073 1084 } 1074 1075 1085 } 1076 1086 1077 1087 /* 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 */ 1080 1092 function getPlayerColor(playername) 1081 1093 { 1082 1094 // Generate a probably-unique hash for the player name and use that to create a color. 1083 1095 var hash = 0; 1084 for ( var i = 0; i < playername.length; i++)1096 for (let i = 0; i < playername.length; ++i) 1085 1097 hash = playername.charCodeAt(i) + ((hash << 5) - hash); 1086 1098 1087 1099 // 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. 1088 1100 // 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 1089 1101 // us much more variety if we generate in RGB. Unfortunately, enforcing that RGB values are a certain lightness is very difficult, so … … 1094 1106 1095 1107 function repeatString(times, string) { 1096 1108 return Array(times + 1).join(string); 1097 1109 } 1098 1110 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.") };1103 1111 function colorPlayerName(playername) 1104 1112 { 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); 1110 1119 } 1111 return '[color="' + getPlayerColor(playername.replace(g_ modPrefix, "")) + '"]' + playername + '[/color]';1120 return '[color="' + getPlayerColor(playername.replace(g_ModPrefix, "")) + '"]' + playername + '[/color]'; 1112 1121 } 1113 1122 1114 // Ensure `value` is between 0 and 1. 1123 /** 1124 * Ensure `value` is between 0 and 1. 1125 */ 1115 1126 function clampColorValue(value) 1116 1127 { 1117 1128 return Math.abs(1 - Math.abs(value - 1)); 1118 1129 } 1119 1130 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 */ 1121 1134 function rgbToHsl(r, g, b) 1122 1135 { 1123 1136 r /= 255; 1124 1137 g /= 255; 1125 1138 b /= 255; … … 1167 1180 r = hue2rgb(p, q, h + 1/3); 1168 1181 g = hue2rgb(p, q, h); 1169 1182 b = hue2rgb(p, q, h - 1/3); 1170 1183 } 1171 1184 1172 return [r, g, b].map( function (n)Math.round(n * 255));1185 return [r, g, b].map(n => Math.round(n * 255)); 1173 1186 } 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
161 161 </action> 162 162 </object> 163 163 164 164 <object type="button" style="ModernButtonRed" size="0 100%-25 100% 100%"> 165 165 <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> 170 167 </object> 171 168 </object> 172 169 173 170 <!-- Middle panel: Filters, game list, chat box. --> 174 171 <object name="middlePanel" size="20%+5 5% 100%-255 97.2%"> -
binaries/data/mods/public/gui/session/messages.js
366 366 return; 367 367 368 368 if (Engine.GetGUIObjectByName("toggleTeamChat").checked) 369 369 text = "/team " + text; 370 370 371 if (g_IsNetworked) 372 Engine.SendNetworkChat(text); 373 else 374 addChatMessage({ "type": "message", "guid": "local", "text": text }); 371 submitChatDirectly(text); 375 372 } 376 373 377 374 function addChatMessage(msg) 378 375 { 379 376 var playerColor, username;