Ticket #2640: 2640_3.patch

File 2640_3.patch, 37.2 KB (added by Matt Doerksen, 9 years ago)

Includes content from 2640_1 and 2640_2 patches. This moves all chat functionality (gamesetup, lobby, ingame) to functions_chat.js.

  • new file inaries/data/mods/public/gui/common/functions_chat.js

    From 2d4f86f1d0ea48e6a47c862cd88731a274804d99 Mon Sep 17 00:00:00 2001
    From: matt <matthewdoerksen@hotmail.com>
    Date: Sat, 20 Jun 2015 19:01:11 -0700
    Subject: [PATCH] Move lobby chat functionality.
    
    ---
     .../data/mods/public/gui/common/functions_chat.js  | 306 +++++++++++++++++++++
     .../data/mods/public/gui/gamesetup/gamesetup.js    |  79 +-----
     .../data/mods/public/gui/gamesetup/gamesetup.xml   |   1 +
     binaries/data/mods/public/gui/lobby/lobby.js       | 141 ++++------
     binaries/data/mods/public/gui/lobby/lobby.xml      |   1 +
     binaries/data/mods/public/gui/session/messages.js  | 223 +--------------
     binaries/data/mods/public/gui/session/session.xml  |   1 +
     7 files changed, 375 insertions(+), 377 deletions(-)
     create mode 100644 binaries/data/mods/public/gui/common/functions_chat.js
    
    diff --git a/binaries/data/mods/public/gui/common/functions_chat.js b/binaries/data/mods/public/gui/common/functions_chat.js
    new file mode 100644
    index 0000000..ca29459
    - +  
     1function addChatMessageGameSetup(msg)
     2{
     3    var username = "";
     4    if (msg.username)
     5        username = escapeText(msg.username);
     6    else if (msg.guid && g_PlayerAssignments[msg.guid])
     7        username = escapeText(g_PlayerAssignments[msg.guid].name);
     8
     9    var message = "";
     10    if ("text" in msg && msg.text)
     11        message = escapeText(msg.text);
     12
     13    // TODO: Maybe host should have distinct font/color?
     14    var color = "white";
     15
     16    if (msg.guid && g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != -1)
     17    {
     18        // Valid player who has been assigned - get player color
     19        var player = g_PlayerAssignments[msg.guid].player - 1;
     20        var mapName = g_GameAttributes.map;
     21        var mapData = loadMapData(mapName);
     22        var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
     23        var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
     24        var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
     25
     26        color = rgbToGuiColor(getSetting(pData, pDefs, "Color"));
     27    }
     28
     29    var formatted;
     30    switch (msg.type)
     31    {
     32    case "connect":
     33        var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
     34        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has joined"), { username: formattedUsername }) }) + '[/font]';
     35        break;
     36
     37    case "disconnect":
     38        var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
     39        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]';
     40        break;
     41
     42    case "message":
     43        var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
     44        var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
     45        formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message });
     46        break;
     47
     48    case "ready":
     49        var formattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]'
     50        if (msg.ready)
     51            formatted = ' ' + sprintf(translate("* %(username)s is ready!"), { username: formattedUsername });
     52        else
     53            formatted = ' ' + sprintf(translate("* %(username)s is not ready."), { username: formattedUsername });
     54        break;
     55
     56    case "settings":
     57        formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: translate('Game settings have been changed') }) + '[/font]';
     58        break;
     59
     60    default:
     61        error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
     62        return;
     63    }
     64
     65    g_ChatMessages.push(formatted);
     66
     67    Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
     68}
     69
     70function addChatMessageSession(msg, playerAssignments)
     71{
     72    // Default to global assignments, but allow overriding for when reporting
     73    // new players joining
     74    if (!playerAssignments)
     75        playerAssignments = g_PlayerAssignments;
     76
     77    var playerColor, username;
     78
     79    // No context by default. May be set by parseChatCommands().
     80    msg.context = "";
     81
     82    if ("guid" in msg && playerAssignments[msg.guid])
     83    {
     84        var n = playerAssignments[msg.guid].player;
     85        // Observers have an ID of -1 which is not a valid index.
     86        if (n < 0)
     87            n = 0;
     88        playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b;
     89        username = escapeText(playerAssignments[msg.guid].name);
     90
     91        // Parse in-line commands in regular messages.
     92        if (msg.type == "message")
     93            parseChatCommands(msg, playerAssignments);
     94    }
     95    else if (msg.type == "defeat" && msg.player)
     96    {
     97        [username, playerColor] = getUsernameAndColor(msg.player);
     98    }
     99    else if (msg.type == "message")
     100    {
     101        [username, playerColor] = getUsernameAndColor(msg.player);
     102        parseChatCommands(msg, playerAssignments);
     103    }
     104    else
     105    {
     106        playerColor = "255 255 255";
     107        username = translate("Unknown player");
     108    }
     109
     110    var formatted;
     111
     112    switch (msg.type)
     113    {
     114    case "connect":
     115        formatted = sprintf(translate("%(player)s is starting to rejoin the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
     116        break;
     117    case "disconnect":
     118        formatted = sprintf(translate("%(player)s has left the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
     119        break;
     120    case "rejoined":
     121        formatted = sprintf(translate("%(player)s has rejoined the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
     122        break;
     123    case "defeat":
     124        // In singleplayer, the local player is "You". "You has" is incorrect.
     125        if (!g_IsNetworked && msg.player == Engine.GetPlayerID())
     126            formatted = translate("You have been defeated.");
     127        else
     128            formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
     129        break;
     130    case "diplomacy":
     131        var message;
     132        if (msg.player == Engine.GetPlayerID())
     133        {
     134            [username, playerColor] = getUsernameAndColor(msg.player1);
     135            if (msg.status == "ally")
     136                message = translate("You are now allied with %(player)s.");
     137            else if (msg.status == "enemy")
     138                message = translate("You are now at war with %(player)s.");
     139            else // (msg.status == "neutral")
     140                message = translate("You are now neutral with %(player)s.");
     141        }
     142        else if (msg.player1 == Engine.GetPlayerID())
     143        {
     144            [username, playerColor] = getUsernameAndColor(msg.player);
     145            if (msg.status == "ally")
     146                message = translate("%(player)s is now allied with you.");
     147            else if (msg.status == "enemy")
     148                message = translate("%(player)s is now at war with you.");
     149            else // (msg.status == "neutral")
     150                message = translate("%(player)s is now neutral with you.");
     151        }
     152        else // No need for other players to know of this.
     153            return;
     154
     155        formatted = sprintf(message, { "player": '[color="'+ playerColor + '"]' + username + '[/color]' });
     156        break;
     157    case "tribute":
     158        if (msg.player != Engine.GetPlayerID())
     159            return;
     160
     161        [username, playerColor] = getUsernameAndColor(msg.player1);
     162
     163        // Format the amounts to proper English: 200 food, 100 wood, and 300 metal; 100 food; 400 wood and 200 stone
     164        let amounts = Object.keys(msg.amounts)
     165            .filter(function (type) { return msg.amounts[type] > 0; })
     166            .map(function (type) { return sprintf(translate("%(amount)s %(resourceType)s"), {
     167                "amount": msg.amounts[type],
     168                "resourceType": getLocalizedResourceName(type, "withinSentence")});
     169            });
     170
     171        if (amounts.length > 1)
     172        {
     173            let lastAmount = amounts.pop();
     174            amounts = sprintf(translate("%(previousAmounts)s and %(lastAmount)s"), {
     175                previousAmounts: amounts.join(translate(", ")),
     176                lastAmount: lastAmount
     177            });
     178        }
     179
     180        formatted = sprintf(translate("%(player)s has sent you %(amounts)s."), {
     181            player: "[color=\"" + playerColor + "\"]" + username + "[/color]",
     182            amounts: amounts
     183        });
     184        break;
     185    case "attack":
     186        if (msg.player != Engine.GetPlayerID())
     187            return;
     188
     189        [username, playerColor] = getUsernameAndColor(msg.attacker);
     190        // Since livestock can be attacked/gathered by other players,
     191        // we display a more specific notification in this case to not confuse the player
     192        if (msg.targetIsDomesticAnimal)
     193            var message = translate("Your livestock have been attacked by %(attacker)s!");
     194        else
     195            var message = translate("You have been attacked by %(attacker)s!");
     196        formatted = sprintf(message, { attacker: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
     197        break;
     198    case "message":
     199        // May have been hidden by the 'team' command.
     200        if (msg.hide)
     201            return;
     202
     203        var message;
     204        if ("translate" in msg && msg.translate)
     205        {
     206            message = translate(msg.text); // No need to escape, not a user message.
     207            if ("translateParameters" in msg && msg.translateParameters)
     208            {
     209                var parameters = msg.parameters || {};
     210                translateObjectKeys(parameters, msg.translateParameters);
     211                message = sprintf(message, parameters);
     212            }
     213        }
     214        else
     215            message = escapeText(msg.text);
     216
     217        if (msg.action)
     218        {
     219            if (msg.context !== "")
     220            {
     221                formatted = sprintf(translate("(%(context)s) * %(user)s %(message)s"), {
     222                    context: msg.context,
     223                    user: "[color=\"" + playerColor + "\"]" + username + "[/color]",
     224                    message: message
     225                });
     226            }
     227            else
     228            {
     229                formatted = sprintf(translate("* %(user)s %(message)s"), {
     230                    user: "[color=\"" + playerColor + "\"]" + username + "[/color]",
     231                    message: message
     232                });
     233            }
     234        }
     235        else
     236        {
     237            var userTag = sprintf(translate("<%(user)s>"), { user: username })
     238            var formattedUserTag = sprintf(translate("<%(user)s>"), { user: "[color=\"" + playerColor + "\"]" + username + "[/color]" })
     239            if (msg.context !== "")
     240            {
     241                formatted = sprintf(translate("(%(context)s) %(userTag)s %(message)s"), {
     242                    context: msg.context,
     243                    userTag: formattedUserTag,
     244                    message: message
     245                });
     246            }
     247            else
     248            {
     249                formatted = sprintf(translate("%(userTag)s %(message)s"), { userTag: formattedUserTag, message: message});
     250            }
     251        }
     252        break;
     253    default:
     254        error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
     255        return;
     256    }
     257
     258    chatMessages.push(formatted);
     259    chatTimers.push(setTimeout(removeOldChatMessages, CHAT_TIMEOUT));
     260
     261    if (chatMessages.length > MAX_NUM_CHAT_LINES)
     262        removeOldChatMessages();
     263    else
     264        Engine.GetGUIObjectByName("chatText").caption = chatMessages.join("\n");
     265}
     266
     267function addChatMessageLobby(msg)
     268{
     269    // Some calls of this function will leave some msg parameters empty. Text is required though.
     270    if (msg.from)
     271    {
     272        // Display the moderator symbol in the chatbox.
     273        var playerRole = Engine.LobbyGetPlayerRole(msg.from);
     274        if (playerRole == "moderator")
     275            msg.from = g_modPrefix + msg.from;
     276    }
     277    else
     278        msg.from = null;
     279    if (!msg.color)
     280        msg.color = null;
     281    if (!msg.key)
     282        msg.key = null;
     283    if (!msg.datetime)
     284        msg.datetime = null;
     285
     286    // Highlight local user's nick
     287    if (msg.text.indexOf(g_Name) != -1 && g_Name != msg.from)
     288        msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), colorPlayerName(g_Name));
     289
     290    // Run spam test if it's not a historical message
     291    if (!msg.datetime)
     292        updateSpamMonitor(msg.from);
     293    if (isSpam(msg.text, msg.from))
     294        return;
     295
     296    // Format Text
     297    var formatted = ircFormat(msg.text, msg.from, msg.color, msg.key, msg.datetime);
     298
     299    // If there is text, add it to the chat box.
     300    if (formatted)
     301    {
     302        g_ChatMessages.push(formatted);
     303        Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
     304    }
     305}
     306
  • binaries/data/mods/public/gui/gamesetup/gamesetup.js

    diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js
    index 856b79d..960393a 100644
    a b function handleNetMessage(message)  
    451451                // If we have extra player slots and we are the controller, give the player an ID.
    452452                if (g_IsController && message.hosts[host].player === -1 && g_AssignedCount < g_GameAttributes.settings.PlayerData.length)
    453453                    Engine.AssignNetworkPlayer(g_AssignedCount + 1, host);
    454                 addChatMessage({ "type": "connect", "username": message.hosts[host].name });
     454                addChatMessageGameSetup({ "type": "connect", "username": message.hosts[host].name });
    455455                newPlayer = host;
    456456            }
    457457        }
    function handleNetMessage(message)  
    460460        {
    461461            if (!message.hosts[host])
    462462            {
    463                 addChatMessage({ "type": "disconnect", "guid": host });
     463                addChatMessageGameSetup({ "type": "disconnect", "guid": host });
    464464                if (g_PlayerAssignments[host].player != -1)
    465465                    resetReady = true; // Observers shouldn't reset ready.
    466466            }
    function handleNetMessage(message)  
    494494        break;
    495495
    496496    case "chat":
    497         addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
     497        addChatMessageGameSetup({ "type": "message", "guid": message.guid, "text": message.text });
    498498        break;
    499499
    500500    // Singular client to host message
    501501    case "ready":
    502502        g_ReadyChanged -= 1;
    503503        if (g_ReadyChanged < 1 && g_PlayerAssignments[message.guid].player != -1)
    504             addChatMessage({ "type": "ready", "guid": message.guid, "ready": +message.status == 1 });
     504            addChatMessageGameSetup({ "type": "ready", "guid": message.guid, "ready": +message.status == 1 });
    505505        if (!g_IsController)
    506506            break;
    507507        g_PlayerAssignments[message.guid].status = +message.status == 1;
    function submitChatInput()  
    16881688    }
    16891689}
    16901690
    1691 function addChatMessage(msg)
    1692 {
    1693     var username = "";
    1694     if (msg.username)
    1695         username = escapeText(msg.username);
    1696     else if (msg.guid && g_PlayerAssignments[msg.guid])
    1697         username = escapeText(g_PlayerAssignments[msg.guid].name);
    1698 
    1699     var message = "";
    1700     if ("text" in msg && msg.text)
    1701         message = escapeText(msg.text);
    1702 
    1703     // TODO: Maybe host should have distinct font/color?
    1704     var color = "white";
    1705 
    1706     if (msg.guid && g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != -1)
    1707     {
    1708         // Valid player who has been assigned - get player color
    1709         var player = g_PlayerAssignments[msg.guid].player - 1;
    1710         var mapName = g_GameAttributes.map;
    1711         var mapData = loadMapData(mapName);
    1712         var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
    1713         var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
    1714         var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
    1715 
    1716         color = rgbToGuiColor(getSetting(pData, pDefs, "Color"));
    1717     }
    1718 
    1719     var formatted;
    1720     switch (msg.type)
    1721     {
    1722     case "connect":
    1723         var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    1724         formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has joined"), { username: formattedUsername }) }) + '[/font]';
    1725         break;
    1726 
    1727     case "disconnect":
    1728         var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    1729         formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]';
    1730         break;
    1731 
    1732     case "message":
    1733         var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
    1734         var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
    1735         formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message });
    1736         break;
    1737 
    1738     case "ready":
    1739         var formattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]'
    1740         if (msg.ready)
    1741             formatted = ' ' + sprintf(translate("* %(username)s is ready!"), { username: formattedUsername });
    1742         else
    1743             formatted = ' ' + sprintf(translate("* %(username)s is not ready."), { username: formattedUsername });
    1744         break;
    1745 
    1746     case "settings":
    1747         formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: translate('Game settings have been changed') }) + '[/font]';
    1748         break;
    1749 
    1750     default:
    1751         error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
    1752         return;
    1753     }
    1754 
    1755     g_ChatMessages.push(formatted);
    1756 
    1757     Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
    1758 }
    1759 
    17601691function toggleMoreOptions()
    17611692{
    17621693    Engine.GetGUIObjectByName("moreOptionsFade").hidden = !Engine.GetGUIObjectByName("moreOptionsFade").hidden;
    function resetReadyData()  
    18351766        return;
    18361767
    18371768    if (g_ReadyChanged < 1)
    1838         addChatMessage({ "type": "settings"});
     1769        addChatMessageGameSetup({ "type": "settings"});
    18391770    else if (g_ReadyChanged == 2 && !g_ReadyInit)
    18401771        return; // duplicate calls on init
    18411772    else
  • binaries/data/mods/public/gui/gamesetup/gamesetup.xml

    diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.xml b/binaries/data/mods/public/gui/gamesetup/gamesetup.xml
    index 4788977..d575793 100644
    a b  
    33<objects>
    44
    55    <script file="gui/common/network.js"/>
     6    <script file="gui/common/functions_chat.js"/>
    67    <script file="gui/common/functions_civinfo.js"/>
    78    <script file="gui/common/functions_global_object.js"/>
    89    <script file="gui/common/functions_utility.js"/>
  • binaries/data/mods/public/gui/lobby/lobby.js

    diff --git a/binaries/data/mods/public/gui/lobby/lobby.js b/binaries/data/mods/public/gui/lobby/lobby.js
    index 0777bfd..64ee983 100644
    a b function lobbyDisconnect()  
    7575}
    7676
    7777////////////////////////////////////////////////////////////////////////////////////////////////
    78 // Update functions
    79 ////////////////////////////////////////////////////////////////////////////////////////////////
    80 
    81 function updateGameListOrderSelection()
    82 {
    83     g_GameListSortBy = Engine.GetGUIObjectByName("gamesBox").selected_column;
    84     g_GameListOrder = Engine.GetGUIObjectByName("gamesBox").selected_column_order;
    85 
    86     applyFilters();
    87 }
    88 
    89 function updatePlayerListOrderSelection()
    90 {
    91     g_PlayerListSortBy = Engine.GetGUIObjectByName("playersBox").selected_column;
    92     g_PlayerListOrder = Engine.GetGUIObjectByName("playersBox").selected_column_order;
    93 
    94     updatePlayerList();
    95 }
    96 
    97 function resetFilters()
    98 {
    99     // Reset states of gui objects
     78// Update functions
     79////////////////////////////////////////////////////////////////////////////////////////////////
     80
     81function updateGameListOrderSelection()
     82{
     83    g_GameListSortBy = Engine.GetGUIObjectByName("gamesBox").selected_column;
     84    g_GameListOrder = Engine.GetGUIObjectByName("gamesBox").selected_column_order;
     85
     86    applyFilters();
     87}
     88
     89function updatePlayerListOrderSelection()
     90{
     91    g_PlayerListSortBy = Engine.GetGUIObjectByName("playersBox").selected_column;
     92    g_PlayerListOrder = Engine.GetGUIObjectByName("playersBox").selected_column_order;
     93
     94    updatePlayerList();
     95}
     96
     97function resetFilters()
     98{
     99    // Reset states of gui objects
    100100    Engine.GetGUIObjectByName("mapSizeFilter").selected = 0
    101101    Engine.GetGUIObjectByName("playersNumberFilter").selected = 0;
    102     Engine.GetGUIObjectByName("mapTypeFilter").selected = 0;
    103     Engine.GetGUIObjectByName("showFullFilter").checked = false;
    104 
    105     applyFilters();
    106 }
    107 
    108 function applyFilters()
     102    Engine.GetGUIObjectByName("mapTypeFilter").selected = 0;
     103    Engine.GetGUIObjectByName("showFullFilter").checked = false;
     104
     105    applyFilters();
     106}
     107
     108function applyFilters()
    109109{
    110110    // Update the list of games
    111111    updateGameList();
    function updateSubject(newSubject)  
    162162        subjectBox.hidden = false;
    163163        logo.size = "50%-110 40 50%+110 140";
    164164    }
    165 }
    166 
    167 /**
    168  * Do a full update of the player listing, including ratings from cached C++ information.
    169  *
    170  * @return Array containing the player, presence, nickname, and rating listings.
    171  */
     165}
     166
     167/**
     168 * Do a full update of the player listing, including ratings from cached C++ information.
     169 *
     170 * @return Array containing the player, presence, nickname, and rating listings.
     171 */
    172172function updatePlayerList()
    173173{
    174174    var playersBox = Engine.GetGUIObjectByName("playersBox");
    function updateGameList()  
    402402    // to update the game info panel.
    403403    g_GameList = gameList;
    404404
    405     // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'.
     405    // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'.
    406406    var gameStatuses = ['waiting', 'init', 'running'];
    407407    g_GameList.sort(function (a,b) {
    408408        switch (g_GameListSortBy)
    function joinSelectedGame()  
    633633        // Check if it looks like an ip address
    634634        if (sip.split('.').length != 4)
    635635        {
    636             addChatMessage({ "from": "system", "text": sprintf(translate("This game's address '%(ip)s' does not appear to be valid."), { ip: sip }) });
     636            addChatMessageLobby({ "from": "system", "text": sprintf(translate("This game's address '%(ip)s' does not appear to be valid."), { ip: sip }) });
    637637            return;
    638638        }
    639639
    function onTick()  
    681681        {
    682682        case "mucmessage": // For room messages
    683683            var from = escapeText(message.from);
    684             addChatMessage({ "from": from, "text": text, "datetime": message.datetime});
     684            addChatMessageLobby({ "from": from, "text": text, "datetime": message.datetime});
    685685            break;
    686686        case "message": // For private messages
    687687            var from = escapeText(message.from);
    688             addChatMessage({ "from": from, "text": text, "datetime": message.datetime});
     688            addChatMessageLobby({ "from": from, "text": text, "datetime": message.datetime});
    689689            break;
    690690        case "muc":
    691691            var nick = message.text;
    function onTick()  
    716716                    nickList.push(nick);
    717717                    ratingList.push(String(rating));
    718718                    Engine.SendGetRatingList();
    719                     addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has joined."), { nick: nick }), "key": g_specialKey });
     719                    addChatMessageLobby({ "text": "/special " + sprintf(translate("%(nick)s has joined."), { nick: nick }), "key": g_specialKey });
    720720                }
    721721                break;
    722722            case "leave":
    function onTick()  
    726726                presenceList.splice(nickIndex, 1);
    727727                nickList.splice(nickIndex, 1);
    728728                ratingList.splice(nickIndex, 1);
    729                 addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has left."), { nick: nick }), "key": g_specialKey });
     729                addChatMessageLobby({ "text": "/special " + sprintf(translate("%(nick)s has left."), { nick: nick }), "key": g_specialKey });
    730730                break;
    731731            case "nick":
    732732                if (nickIndex == -1) // This shouldn't ever happen
    733733                    break;
    734734                if (!isValidNick(message.data))
    735735                {
    736                     addChatMessage({ "from": "system", "text": sprintf(translate("Invalid nickname: %(nick)s"), { nick: message.data })});
     736                    addChatMessageLobby({ "from": "system", "text": sprintf(translate("Invalid nickname: %(nick)s"), { nick: message.data })});
    737737                    break;
    738738                }
    739739                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
    740740                playerList[nickIndex] = name;
    741741                // presence stays the same
    742742                nickList[nickIndex] = message.data;
    743                 addChatMessage({ "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { oldnick: nick, newnick: message.data }), "key": g_specialKey });
     743                addChatMessageLobby({ "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { oldnick: nick, newnick: message.data }), "key": g_specialKey });
    744744                Engine.SendGetRatingList();
    745745                break;
    746746            case "presence":
    function onTick()  
    770770            switch (message.level)
    771771            {
    772772            case "standard":
    773                 addChatMessage({ "from": "system", "text": text, "color": "150 0 0" });
     773                addChatMessageLobby({ "from": "system", "text": text, "color": "150 0 0" });
    774774                if (message.text == "disconnected")
    775775                {
    776776                    // Clear the list of games and the list of players
    function onTick()  
    786786                }
    787787                break;
    788788            case "error":
    789                 addChatMessage({ "from": "system", "text": text, "color": "150 0 0" });
     789                addChatMessageLobby({ "from": "system", "text": text, "color": "150 0 0" });
    790790                break;
    791791            case "internal":
    792792                switch (message.text)
    function handleSpecialCommand(text)  
    868868    case "me":
    869869        return false;
    870870    default:
    871         addChatMessage({ "from":"system", "text": sprintf(translate("We're sorry, the '%(cmd)s' command is not supported."), { cmd: cmd})});
     871        addChatMessageLobby({ "from":"system", "text": sprintf(translate("We're sorry, the '%(cmd)s' command is not supported."), { cmd: cmd})});
    872872    }
    873873    return true;
    874874}
    875875
    876 /**
    877  * Process and, if appropriate, display a formatted message.
    878  *
    879  * @param msg The message to be processed.
    880  */
    881 function addChatMessage(msg)
    882 {
    883     // Some calls of this function will leave some msg parameters empty. Text is required though.
    884     if (msg.from)
    885     {
    886         // Display the moderator symbol in the chatbox.
    887         var playerRole = Engine.LobbyGetPlayerRole(msg.from);
    888         if (playerRole == "moderator")
    889             msg.from = g_modPrefix + msg.from;
    890     }
    891     else
    892         msg.from = null;
    893     if (!msg.color)
    894         msg.color = null;
    895     if (!msg.key)
    896         msg.key = null;
    897     if (!msg.datetime)
    898         msg.datetime = null;
    899 
    900     // Highlight local user's nick
    901     if (msg.text.indexOf(g_Name) != -1 && g_Name != msg.from)
    902         msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), colorPlayerName(g_Name));
    903 
    904     // Run spam test if it's not a historical message
    905     if (!msg.datetime)
    906         updateSpamMonitor(msg.from);
    907     if (isSpam(msg.text, msg.from))
    908         return;
    909 
    910     // Format Text
    911     var formatted = ircFormat(msg.text, msg.from, msg.color, msg.key, msg.datetime);
    912 
    913     // If there is text, add it to the chat box.
    914     if (formatted)
    915     {
    916         g_ChatMessages.push(formatted);
    917         Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
    918     }
    919 }
    920 
    921876function ircSplit(string)
    922877{
    923878    var idx = string.indexOf(' ');
    function isSpam(text, from)  
    10621017    {
    10631018        g_spamMonitor[from][2] = time;
    10641019        if (from == g_Name)
    1065             addChatMessage({ "from": "system", "text": translate("Please do not spam. You have been blocked for thirty seconds.") });
     1020            addChatMessageLobby({ "from": "system", "text": translate("Please do not spam. You have been blocked for thirty seconds.") });
    10661021        return true;
    10671022    }
    10681023    // Return false if everything is clear.
  • binaries/data/mods/public/gui/lobby/lobby.xml

    diff --git a/binaries/data/mods/public/gui/lobby/lobby.xml b/binaries/data/mods/public/gui/lobby/lobby.xml
    index 879b49d..6574518 100644
    a b  
    11<?xml version="1.0" encoding="utf-8"?>
    22
    33<objects>
     4    <script file="gui/common/functions_chat.js"/>
    45    <script file="gui/common/functions_global_object.js"/>
    56    <script file="gui/common/functions_utility.js"/>
    67    <script file="gui/common/timer.js"/>
  • binaries/data/mods/public/gui/session/messages.js

    diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js
    index 0aef597..5c504c3 100644
    a b var g_NotificationsTypes =  
    4444        } else {
    4545            message["guid"] = guid;
    4646        }
    47         addChatMessage(message);
     47        addChatMessageSession(message);
    4848    },
    4949    "aichat": function(notification, player)
    5050    {
    var g_NotificationsTypes =  
    7474        } else {
    7575            message["guid"] = guid;
    7676        }
    77         addChatMessage(message);
     77        addChatMessageSession(message);
    7878    },
    7979    "defeat": function(notification, player)
    8080    {
    81         addChatMessage({
     81        addChatMessageSession({
    8282            "type": "defeat",
    8383            "guid": findGuidForPlayerID(g_PlayerAssignments, player),
    8484            "player": player
    var g_NotificationsTypes =  
    8888    },
    8989    "diplomacy": function(notification, player)
    9090    {
    91         addChatMessage({
     91        addChatMessageSession({
    9292            "type": "diplomacy",
    9393            "player": player,
    9494            "player1": notification.player1,
    var g_NotificationsTypes =  
    103103    },
    104104    "tribute": function(notification, player)
    105105    {
    106         addChatMessage({
     106        addChatMessageSession({
    107107            "type": "tribute",
    108108            "player": player,
    109109            "player1": notification.donator,
    var g_NotificationsTypes =  
    116116            return;
    117117        if (Engine.ConfigDB_GetValue("user", "gui.session.attacknotificationmessage") !== "true")
    118118            return;
    119         addChatMessage({
     119        addChatMessageSession({
    120120            "type": "attack",
    121121            "player": player,
    122122            "attacker": notification.attacker,
    function handleNetMessage(message)  
    259259            if (!message.hosts[host])
    260260            {
    261261                // Tell the user about the disconnection
    262                 addChatMessage({ "type": "disconnect", "guid": host });
     262                addChatMessageSession({ "type": "disconnect", "guid": host });
    263263
    264264                // Update the cached player data, so we can display the disconnection status
    265265                updatePlayerDataRemove(g_Players, host);
    function handleNetMessage(message)  
    275275                updatePlayerDataAdd(g_Players, host, message.hosts[host]);
    276276
    277277                // Tell the user about the connection
    278                 addChatMessage({ "type": "connect", "guid": host }, message.hosts);
     278                addChatMessageSession({ "type": "connect", "guid": host }, message.hosts);
    279279            }
    280280        }
    281281
    function handleNetMessage(message)  
    290290        break;
    291291
    292292    case "chat":
    293         addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
     293        addChatMessageSession({ "type": "message", "guid": message.guid, "text": message.text });
    294294        break;
    295295
    296296    case "aichat":
    297         addChatMessage({ "type": "message", "guid": message.guid, "text": message.text, "translate": true });
     297        addChatMessageSession({ "type": "message", "guid": message.guid, "text": message.text, "translate": true });
    298298        break;
    299299
    300300    case "rejoined":
    301         addChatMessage({ "type": "rejoined", "guid": message.guid});
     301        addChatMessageSession({ "type": "rejoined", "guid": message.guid});
    302302        break;
    303303       
    304304    // To prevent errors, ignore these message types that occur during autostart
    function submitChatDirectly(text)  
    318318        if (g_IsNetworked)
    319319            Engine.SendNetworkChat(text);
    320320        else
    321             addChatMessage({ "type": "message", "guid": "local", "text": text });
     321            addChatMessageSession({ "type": "message", "guid": "local", "text": text });
    322322    }
    323323}
    324324
    function submitChatInput()  
    385385            if (g_IsNetworked)
    386386                Engine.SendNetworkChat(text);
    387387            else
    388                 addChatMessage({ "type": "message", "guid": "local", "text": text });
     388                addChatMessageSession({ "type": "message", "guid": "local", "text": text });
    389389        }
    390390        input.caption = ""; // Clear chat input
    391391    }
    function submitChatInput()  
    395395    toggleChatWindow();
    396396}
    397397
    398 function addChatMessage(msg, playerAssignments)
    399 {
    400     // Default to global assignments, but allow overriding for when reporting
    401     // new players joining
    402     if (!playerAssignments)
    403         playerAssignments = g_PlayerAssignments;
    404 
    405     var playerColor, username;
    406 
    407     // No context by default. May be set by parseChatCommands().
    408     msg.context = "";
    409 
    410     if ("guid" in msg && playerAssignments[msg.guid])
    411     {
    412         var n = playerAssignments[msg.guid].player;
    413         // Observers have an ID of -1 which is not a valid index.
    414         if (n < 0)
    415             n = 0;
    416         playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b;
    417         username = escapeText(playerAssignments[msg.guid].name);
    418 
    419         // Parse in-line commands in regular messages.
    420         if (msg.type == "message")
    421             parseChatCommands(msg, playerAssignments);
    422     }
    423     else if (msg.type == "defeat" && msg.player)
    424     {
    425         [username, playerColor] = getUsernameAndColor(msg.player);
    426     }
    427     else if (msg.type == "message")
    428     {
    429         [username, playerColor] = getUsernameAndColor(msg.player);
    430         parseChatCommands(msg, playerAssignments);
    431     }
    432     else
    433     {
    434         playerColor = "255 255 255";
    435         username = translate("Unknown player");
    436     }
    437 
    438     var formatted;
    439 
    440     switch (msg.type)
    441     {
    442     case "connect":
    443         formatted = sprintf(translate("%(player)s is starting to rejoin the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    444         break;
    445     case "disconnect":
    446         formatted = sprintf(translate("%(player)s has left the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    447         break;
    448     case "rejoined":
    449         formatted = sprintf(translate("%(player)s has rejoined the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    450         break;
    451     case "defeat":
    452         // In singleplayer, the local player is "You". "You has" is incorrect.
    453         if (!g_IsNetworked && msg.player == Engine.GetPlayerID())
    454             formatted = translate("You have been defeated.");
    455         else
    456             formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    457         break;
    458     case "diplomacy":
    459         var message;
    460         if (msg.player == Engine.GetPlayerID())
    461         {
    462             [username, playerColor] = getUsernameAndColor(msg.player1);
    463             if (msg.status == "ally")
    464                 message = translate("You are now allied with %(player)s.");
    465             else if (msg.status == "enemy")
    466                 message = translate("You are now at war with %(player)s.");
    467             else // (msg.status == "neutral")
    468                 message = translate("You are now neutral with %(player)s.");
    469         }
    470         else if (msg.player1 == Engine.GetPlayerID())
    471         {
    472             [username, playerColor] = getUsernameAndColor(msg.player);
    473             if (msg.status == "ally")
    474                 message = translate("%(player)s is now allied with you.");
    475             else if (msg.status == "enemy")
    476                 message = translate("%(player)s is now at war with you.");
    477             else // (msg.status == "neutral")
    478                 message = translate("%(player)s is now neutral with you.");
    479         }
    480         else // No need for other players to know of this.
    481             return;
    482 
    483         formatted = sprintf(message, { "player": '[color="'+ playerColor + '"]' + username + '[/color]' });
    484         break;
    485     case "tribute":
    486         if (msg.player != Engine.GetPlayerID())
    487             return;
    488 
    489         [username, playerColor] = getUsernameAndColor(msg.player1);
    490 
    491         // Format the amounts to proper English: 200 food, 100 wood, and 300 metal; 100 food; 400 wood and 200 stone
    492         let amounts = Object.keys(msg.amounts)
    493             .filter(function (type) { return msg.amounts[type] > 0; })
    494             .map(function (type) { return sprintf(translate("%(amount)s %(resourceType)s"), {
    495                 "amount": msg.amounts[type],
    496                 "resourceType": getLocalizedResourceName(type, "withinSentence")});
    497             });
    498 
    499         if (amounts.length > 1)
    500         {
    501             let lastAmount = amounts.pop();
    502             amounts = sprintf(translate("%(previousAmounts)s and %(lastAmount)s"), {
    503                 previousAmounts: amounts.join(translate(", ")),
    504                 lastAmount: lastAmount
    505             });
    506         }
    507 
    508         formatted = sprintf(translate("%(player)s has sent you %(amounts)s."), {
    509             player: "[color=\"" + playerColor + "\"]" + username + "[/color]",
    510             amounts: amounts
    511         });
    512         break;
    513     case "attack":
    514         if (msg.player != Engine.GetPlayerID())
    515             return;
    516 
    517         [username, playerColor] = getUsernameAndColor(msg.attacker);
    518         // Since livestock can be attacked/gathered by other players,
    519         // we display a more specific notification in this case to not confuse the player
    520         if (msg.targetIsDomesticAnimal)
    521             var message = translate("Your livestock have been attacked by %(attacker)s!");
    522         else
    523             var message = translate("You have been attacked by %(attacker)s!");
    524         formatted = sprintf(message, { attacker: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
    525         break;
    526     case "message":
    527         // May have been hidden by the 'team' command.
    528         if (msg.hide)
    529             return;
    530 
    531         var message;
    532         if ("translate" in msg && msg.translate)
    533         {
    534             message = translate(msg.text); // No need to escape, not a user message.
    535             if ("translateParameters" in msg && msg.translateParameters)
    536             {
    537                 var parameters = msg.parameters || {};
    538                 translateObjectKeys(parameters, msg.translateParameters);
    539                 message = sprintf(message, parameters);
    540             }
    541         }
    542         else
    543             message = escapeText(msg.text);
    544 
    545         if (msg.action)
    546         {
    547             if (msg.context !== "")
    548             {
    549                 formatted = sprintf(translate("(%(context)s) * %(user)s %(message)s"), {
    550                     context: msg.context,
    551                     user: "[color=\"" + playerColor + "\"]" + username + "[/color]",
    552                     message: message
    553                 });
    554             }
    555             else
    556             {
    557                 formatted = sprintf(translate("* %(user)s %(message)s"), {
    558                     user: "[color=\"" + playerColor + "\"]" + username + "[/color]",
    559                     message: message
    560                 });
    561             }
    562         }
    563         else
    564         {
    565             var userTag = sprintf(translate("<%(user)s>"), { user: username })
    566             var formattedUserTag = sprintf(translate("<%(user)s>"), { user: "[color=\"" + playerColor + "\"]" + username + "[/color]" })
    567             if (msg.context !== "")
    568             {
    569                 formatted = sprintf(translate("(%(context)s) %(userTag)s %(message)s"), {
    570                     context: msg.context,
    571                     userTag: formattedUserTag,
    572                     message: message
    573                 });
    574             }
    575             else
    576             {
    577                 formatted = sprintf(translate("%(userTag)s %(message)s"), { userTag: formattedUserTag, message: message});
    578             }
    579         }
    580         break;
    581     default:
    582         error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
    583         return;
    584     }
    585 
    586     chatMessages.push(formatted);
    587     chatTimers.push(setTimeout(removeOldChatMessages, CHAT_TIMEOUT));
    588 
    589     if (chatMessages.length > MAX_NUM_CHAT_LINES)
    590         removeOldChatMessages();
    591     else
    592         Engine.GetGUIObjectByName("chatText").caption = chatMessages.join("\n");
    593 }
    594 
    595398function removeOldChatMessages()
    596399{
    597400    clearTimeout(chatTimers[0]); // The timer only needs to be cleared when new messages bump old messages off
  • binaries/data/mods/public/gui/session/session.xml

    diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml
    index d9ee7da..988d0db 100644
    a b  
    33<objects>
    44
    55<script file="gui/common/colorFades.js"/>
     6<script file="gui/common/functions_chat.js"/>
    67<script file="gui/common/functions_civinfo.js"/>
    78<script file="gui/common/functions_global_object.js"/>
    89<script file="gui/common/functions_utility.js"/>