Index: binaries/data/config/default.cfg =================================================================== --- binaries/data/config/default.cfg +++ binaries/data/config/default.cfg @@ -395,6 +395,8 @@ server = "lobby.wildfiregames.com" ; Address of lobby server xpartamupp = "wfgbot23" ; Name of the server-side xmpp client that manage games buddies = "," ; Comma separated list of playernames that the current user has marked as buddies +chatsenderpresence = false ; Dynamically changing brightness of messages by presence of sent player +chatsenderrating = false ; Give ratings to players in sender from chat messages in lobby [lobby.columns] gamerating = false ; Show the average rating of the participating players in a column of the gamelist Index: binaries/data/mods/public/gui/common/color.js =================================================================== --- binaries/data/mods/public/gui/common/color.js +++ binaries/data/mods/public/gui/common/color.js @@ -141,6 +141,14 @@ return [r, g, b].map(n => Math.round(n * 255)); } +/* + * Set color to brightness in float number from zero to one [0-1]. + */ +function setColorBrightness(color, brightness) +{ + return color.split(" ").map(number => Math.round(number * brightness)).join(" "); +} + function colorizeHotkey(text, hotkey) { let key = Engine.ConfigDB_GetValue("user", "hotkey." + hotkey); 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 @@ -41,11 +41,11 @@ * The playerlist will be assembled using these values. */ var g_PlayerStatuses = { - "available": { "color": "0 219 0", "status": translate("Online") }, - "away": { "color": "229 76 13", "status": translate("Away") }, - "playing": { "color": "200 0 0", "status": translate("Busy") }, - "offline": { "color": "0 0 0", "status": translate("Offline") }, - "unknown": { "color": "178 178 178", "status": translateWithContext("lobby presence", "Unknown") } + "available": { "color": "0 219 0", "messageBrightness": 1, "status": translate("Online") }, + "away": { "color": "229 76 13", "messageBrightness": 0.8, "status": translate("Away") }, + "playing": { "color": "200 0 0", "messageBrightness": 0.6, "status": translate("Busy") }, + "offline": { "color": "0 0 0", "messageBrightness": 0.4, "status": translate("Offline") }, + "unknown": { "color": "178 178 178", "messageBrightness": 0.4, "status": translateWithContext("lobby presence", "Unknown") } }; var g_RoleNames = { @@ -111,6 +111,11 @@ var g_Kicked = false; /** + * List of currently present players in lobby + */ +var g_PlayerList = [ "uninitialized" ]; + +/** * Processing of notifications sent by XmppClient.cpp. * * @returns true if the playerlist GUI must be updated. @@ -187,7 +192,7 @@ return true; }, - "presence": msg => true, + "presence": msg => { if (g_PlayerList[0] == "uninitialized") g_PlayerList[0] = "initial"; return true; }, "role": msg => { Engine.GetGUIObjectByName("chatInput").hidden = Engine.LobbyGetPlayerRole(g_Username) == "visitor"; @@ -596,11 +601,39 @@ toggleBuddyButton.enabled = playerName && playerName != g_Username; } +/* + * Find data object in a previous list by name and restore its attributes. + * Trying firstly object at index in previous list and when not same name, search the list for object. + * @return [ object, index ] - [ found previous object, next index to look for next object in list ] + */ +function restoreObjectFromList(newObject, index, previousList, previousAttributes) +{ + let previousObject = previousList[index] && + previousList[index].name == newObject.name ? previousList[index++] : + previousList.find((previousObject, indexFound) => previousObject.name == newObject.name && (index = ++indexFound)); + + previousAttributes.forEach(att => newObject[att] = previousObject && previousObject[att] ? previousObject[att] : []); + + return [ previousObject, index ]; +} /** * Do a full update of the player listing, including ratings from cached C++ information. */ function updatePlayerList() { + let initialPlayerList = g_PlayerList[0] && g_PlayerList[0] == "initial"; + let newPlayerList = Engine.GetPlayerList(); + + if (g_PlayerList[0] && g_PlayerList[0] == "uninitialized") + // If a playerlist is available before initial presences update, go initial + if (newPlayerList.length > 0) + initialPlayerList = true; + else + return; + + if (initialPlayerList) + g_PlayerList = []; + let playersBox = Engine.GetGUIObjectByName("playersBox"); let sortBy = playersBox.selected_column || "name"; let sortOrder = playersBox.selected_column_order || 1; @@ -611,10 +644,30 @@ let nickList = []; let ratingList = []; - let cleanPlayerList = Engine.GetPlayerList().map(player => { + let updatedChat = false; + let indexPrevious = 0; + + g_PlayerList = newPlayerList.map(player => { + let playerPrevious = {}; + + [ playerPrevious, indexPrevious ] = restoreObjectFromList(player, indexPrevious, g_PlayerList, ["messages"]); + + if (playerPrevious && (playerPrevious.rating != player.rating || playerPrevious.presence != player.presence)) + player.messages.forEach(message => { + message.playerRating = player.rating; + message.playerPresence = player.presence; + updatedChat = ircFormat(message) || updatedChat; + }); + player.isBuddy = g_Buddies.indexOf(player.name) != -1; return player; - }).sort((a, b) => { + }); + + // Check if unassigned messages to players lie in chat + if (initialPlayerList) + g_ChatMessages.forEach(msg => addChatMessageToPlayer(msg, true) && (updatedChat = ircFormat(msg) || updatedChat)); + + let cleanPlayerList = g_PlayerList.sort((a, b) => { let sortA, sortB; let statusOrder = Object.keys(g_PlayerStatuses); let statusA = statusOrder.indexOf(a.presence) + a.name.toLowerCase(); @@ -671,6 +724,9 @@ playersBox.list = nickList; playersBox.selected = playersBox.list.indexOf(g_SelectedPlayer); + + if (updatedChat) + updateChatWindow(); } /** @@ -1257,6 +1313,19 @@ return g_ChatCommands[cmd].handler(args); } +function addChatMessageToPlayer(message, checkAlreadyAdded) +{ + let playerFrom = g_PlayerList.find(player => player.name && player.name == message.from); + + if (playerFrom && (!checkAlreadyAdded || playerFrom.messages.indexOf(message) == -1)) + { + playerFrom.messages.push(message); + message.playerRating = playerFrom.rating; + message.playerPresence = playerFrom.presence; + return true; + } +} + /** * Process and if appropriate, display a formatted message. * @@ -1266,6 +1335,8 @@ { if (msg.from) { + addChatMessageToPlayer(msg); + if (Engine.LobbyGetPlayerRole(msg.from) == "moderator") msg.from = g_ModeratorPrefix + msg.from; @@ -1279,12 +1350,16 @@ } } - let formatted = ircFormat(msg); - if (!formatted) + if (!ircFormat(msg)) return; - g_ChatMessages.push(formatted); - Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n"); + g_ChatMessages.push(msg); + updateChatWindow(); +} + +function updateChatWindow() +{ + Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.map(msg => msg.formatted).join("\n"); } /** @@ -1309,7 +1384,18 @@ function ircFormat(msg) { let formattedMessage = ""; - let coloredFrom = msg.from && colorPlayerName(msg.from); + + let messageBrightness = g_PlayerStatuses[ + Engine.ConfigDB_GetValue("user", "lobby.chatsenderpresence") == "false" || + !msg.from || + msg.from == "system" ? "available" : msg.playerPresence || "offline" + ].messageBrightness; + + let coloredFrom = msg.from ? coloredText( + msg.from + (Engine.ConfigDB_GetValue("user", "lobby.chatsenderrating") == "true" && msg.playerRating ? + " (" + msg.playerRating + ")" : ""), + setColorBrightness(getPlayerColor(msg.from), messageBrightness) + ) : ""; // Handle commands allowed past handleChatCommand. if (msg.text[0] == '/') @@ -1368,7 +1454,7 @@ break; } default: - return ""; + return false; } } else @@ -1394,24 +1480,27 @@ } // Add chat message timestamp - if (Engine.ConfigDB_GetValue("user", "chat.timestamp") != "true") - return formattedMessage; + if (Engine.ConfigDB_GetValue("user", "chat.timestamp") == "true") + { + // Translation: Time as shown in the multiplayer lobby (when you enable it in the options page). + // For a list of symbols that you can use, see: + // https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table + let timeString = Engine.FormatMillisecondsIntoDateStringLocal(msg.time ? msg.time * 1000 : Date.now(), translate("HH:mm")); + + // Translation: Time prefix as shown in the multiplayer lobby (when you enable it in the options page). + let timePrefixString = sprintf(translate("\\[%(time)s]"), { + "time": timeString + }); - // Translation: Time as shown in the multiplayer lobby (when you enable it in the options page). - // For a list of symbols that you can use, see: - // https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table - let timeString = Engine.FormatMillisecondsIntoDateStringLocal(msg.time ? msg.time * 1000 : Date.now(), translate("HH:mm")); - - // Translation: Time prefix as shown in the multiplayer lobby (when you enable it in the options page). - let timePrefixString = sprintf(translate("\\[%(time)s]"), { - "time": timeString - }); + // Translation: IRC message format when there is a time prefix. + formattedMessage = sprintf(translate("%(time)s %(message)s"), { + "time": senderFont(timePrefixString), + "message": formattedMessage + }); + } - // Translation: IRC message format when there is a time prefix. - return sprintf(translate("%(time)s %(message)s"), { - "time": senderFont(timePrefixString), - "message": formattedMessage - }); + msg.formatted = coloredText(formattedMessage, setColorBrightness("255 255 255", messageBrightness)); + return true; } /** 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 @@ -411,6 +411,18 @@ "label": "Game Rating Column", "tooltip": "Show the average rating of the participating players in a column of the gamelist.", "config": "lobby.columns.gamerating" + }, + { + "type": "boolean", + "label": "Chat Ratings", + "tooltip": "Show player ratings in chat.", + "config": "lobby.chatsenderrating" + }, + { + "type": "boolean", + "label": "Chat Sender Presence", + "tooltip": "Show brightness of messages in chat according to the presence of the sender.", + "config": "lobby.chatsenderpresence" } ] },