Ticket #3702: lockedTeams3.patch

File lockedTeams3.patch, 15.1 KB (added by elexis, 8 years ago)
  • binaries/data/mods/public/gui/session/menu.js

    const g_IdleTraderTextColor = "orange";  
    3434var g_IsMenuOpen = false;
    3535
    3636var g_IsDiplomacyOpen = false;
    3737var g_IsTradeOpen = false;
    3838
    39 // Redefined every time someone makes a tribute (so we can save some data in a closure). Called in input.js handleInputBeforeGui.
     39/**
     40 * Redefined every time someone makes a tribute (so we can save some data in a closure).
     41 * Called in input.js handleInputBeforeGui.
     42 */
    4043var g_FlushTributing = function() {};
    4144
    4245// Ignore size defined in XML and set the actual menu size here
    4346function initMenuPosition()
    4447{
    function toggleChatWindow(teamChat)  
    252255
    253256    updateTeamCheckbox(teamChat);
    254257    chatWindow.hidden = !hidden;
    255258}
    256259
    257 function setDiplomacy(data)
    258 {
    259     Engine.PostNetworkCommand({ "type": "diplomacy", "to": data.to, "player": data.player });
    260 }
    261 
    262 function tributeResource(data)
    263 {
    264     Engine.PostNetworkCommand({ "type": "tribute", "player": data.player, "amounts":  data.amounts });
    265 }
    266 
    267260function openDiplomacy()
    268261{
    269262    closeOpenDialogs();
    270263
    271264    if (g_ViewedPlayer < 1)
    272265        return;
    273266
    274267    g_IsDiplomacyOpen = true;
    275268
    276269    let isCeasefireActive = GetSimState().ceasefireActive;
     270    for (let i = 1; i < g_Players.length; ++i)
     271    {
     272        diplomacySetupTexts(i);
     273
     274        let playerInactive = i == g_ViewedPlayer || isPlayerObserver(g_ViewedPlayer) || isPlayerObserver(i);
     275        let hasAllies = g_Players.filter(player => player.isMutualAlly[g_ViewedPlayer]).length > 1;
     276
     277        diplomacyFormatTributeButtons(i, playerInactive);
     278        diplomacyFormatStanceButtons(i, playerInactive || isCeasefireActive || g_Players[g_ViewedPlayer].teamsLocked);
     279        diplomacyFormatAttackRequestButton(i, playerInactive || isCeasefireActive || !hasAllies || !g_Players[i].isEnemy[g_ViewedPlayer]);
     280    }
     281
     282    Engine.GetGUIObjectByName("diplomacyDialogPanel").hidden = false;
     283}
    277284
     285function diplomacySetupTexts(i)
     286{
    278287    // Get offset for one line
    279288    let onesize = Engine.GetGUIObjectByName("diplomacyPlayer[0]").size;
    280289    let rowsize = onesize.bottom - onesize.top;
    281290
    282     // We don't include gaia
    283     for (let i = 1; i < g_Players.length; ++i)
    284     {
    285         // Apply offset
    286         let row = Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]");
    287         let size = row.size;
    288         size.top = rowsize*(i-1);
    289         size.bottom = rowsize*i;
    290         row.size = size;
    291 
    292         // Set background color
    293         let playerColor = rgbToGuiColor(g_Players[i].color);
    294         row.sprite = "color: "+playerColor + " 32";
    295 
    296         Engine.GetGUIObjectByName("diplomacyPlayerName["+(i-1)+"]").caption = "[color=\"" + playerColor + "\"]" + g_Players[i].name + "[/color]";
    297         Engine.GetGUIObjectByName("diplomacyPlayerCiv["+(i-1)+"]").caption = g_CivData[g_Players[i].civ].Name;
    298         Engine.GetGUIObjectByName("diplomacyPlayerTeam["+(i-1)+"]").caption = (g_Players[i].team < 0) ? translateWithContext("team", "None") : g_Players[i].team+1;
    299         Engine.GetGUIObjectByName("diplomacyPlayerTheirs["+(i-1)+"]").caption = (i == g_ViewedPlayer) ? "" : (g_Players[i].isAlly[g_ViewedPlayer] ? translate("Ally") : (g_Players[i].isNeutral[g_ViewedPlayer] ? translate("Neutral") : translate("Enemy")));
     291    let row = Engine.GetGUIObjectByName("diplomacyPlayer["+(i-1)+"]");
     292    let size = row.size;
     293    size.top = rowsize*(i-1);
     294    size.bottom = rowsize*i;
     295    row.size = size;
     296    row.sprite = "color: " + rgbToGuiColor(g_Players[i].color) + " 32";
    300297
    301         // Don't display the options for ourself, or if we or the other player aren't active anymore
    302         if (i == g_ViewedPlayer || g_Players[i].state != "active")
    303         {
    304             // Hide the unused/unselectable options
    305             for (let a of ["TributeFood", "TributeWood", "TributeStone", "TributeMetal", "Ally", "Neutral", "Enemy"])
    306                 Engine.GetGUIObjectByName("diplomacyPlayer"+a+"["+(i-1)+"]").hidden = true;
    307             Engine.GetGUIObjectByName("diplomacyAttackRequest["+(i-1)+"]").hidden = true;
    308             continue;
    309         }
     298    Engine.GetGUIObjectByName("diplomacyPlayerName["+(i-1)+"]").caption = colorizePlayernameByID(i);
     299    Engine.GetGUIObjectByName("diplomacyPlayerCiv["+(i-1)+"]").caption = g_CivData[g_Players[i].civ].Name;
    310300
    311         // Tribute
    312         for (let resource of RESOURCES)
    313         {
    314             let button = Engine.GetGUIObjectByName("diplomacyPlayerTribute"+resource[0].toUpperCase()+resource.substring(1)+"["+(i-1)+"]");
    315             button.onpress = (function(player, resource, button){
    316                 // Implement something like how unit batch training works. Shift+click to send 500, shift+click+click to send 1000, etc.
    317                 // Also see input.js (searching for "INPUT_MASSTRIBUTING" should get all the relevant parts).
    318                 let multiplier = 1;
    319                 return function() {
    320                     let isBatchTrainPressed = Engine.HotkeyIsPressed("session.masstribute");
    321                     if (isBatchTrainPressed)
    322                     {
    323                         inputState = INPUT_MASSTRIBUTING;
    324                         multiplier += multiplier == 1 ? 4 : 5;
    325                     }
    326                     let amounts = {
    327                         "food": (resource == "food" ? 100 : 0) * multiplier,
    328                         "wood": (resource == "wood" ? 100 : 0) * multiplier,
    329                         "stone": (resource == "stone" ? 100 : 0) * multiplier,
    330                         "metal": (resource == "metal" ? 100 : 0) * multiplier
    331                     };
    332                     button.tooltip = formatTributeTooltip(g_Players[player], resource, amounts[resource]);
    333                     // This is in a closure so that we have access to `player`, `amounts`, and `multiplier` without some
    334                     // evil global variable hackery.
    335                     g_FlushTributing = function() {
    336                         tributeResource({ "player": player, "amounts": amounts });
    337                         multiplier = 1;
    338                         button.tooltip = formatTributeTooltip(g_Players[player], resource, 100);
    339                     };
    340                     if (!isBatchTrainPressed)
    341                         g_FlushTributing();
    342                 };
    343             })(i, resource, button);
    344             button.enabled = controlsPlayer(g_ViewedPlayer);
    345             button.hidden = false;
    346             button.tooltip = formatTributeTooltip(g_Players[i], resource, 100);
    347         }
     301    Engine.GetGUIObjectByName("diplomacyPlayerTeam["+(i-1)+"]").caption =
     302        g_Players[i].team < 0 ? translateWithContext("team", "None") : g_Players[i].team+1;
    348303
    349         // Attack Request
    350         let button = Engine.GetGUIObjectByName("diplomacyAttackRequest["+(i-1)+"]");
    351         button.hidden = isCeasefireActive || !g_Players[i].isEnemy[g_ViewedPlayer];
    352         button.enabled = controlsPlayer(g_ViewedPlayer);
    353         button.tooltip = translate("Request your allies to attack this enemy");
    354         button.onpress = (function(i) { return function() {
    355             Engine.PostNetworkCommand({ "type": "attack-request", "source": g_ViewedPlayer, "target": i });
    356         }; })(i);
     304    Engine.GetGUIObjectByName("diplomacyPlayerTheirs["+(i-1)+"]").caption =
     305        i == g_ViewedPlayer ? "" :
     306        g_Players[i].isAlly[g_ViewedPlayer] ? translate("Ally") :
     307        g_Players[i].isNeutral[g_ViewedPlayer] ? translate("Neutral") : translate("Enemy");
     308}
    357309
    358         // Skip our own teams on teams locked
    359         if (g_Players[g_ViewedPlayer].teamsLocked && g_Players[g_ViewedPlayer].team != -1 && g_Players[g_ViewedPlayer].team == g_Players[i].team)
     310function diplomacyFormatTributeButtons(i, hidden)
     311{
     312    for (let resource of RESOURCES)
     313    {
     314        let button = Engine.GetGUIObjectByName("diplomacyPlayerTribute"+resource[0].toUpperCase()+resource.substring(1)+"["+(i-1)+"]");
     315        button.hidden = hidden;
     316        if (hidden)
    360317            continue;
     318        button.enabled = controlsPlayer(g_ViewedPlayer);
     319        button.tooltip = formatTributeTooltip(g_Players[i], resource, 100);
     320        button.onpress = (function(i, resource, button) {
     321            // Shift+click to send 500, shift+click+click to send 1000, etc.
     322            // Also see INPUT_MASSTRIBUTING in input.js
     323            let multiplier = 1;
     324            return function() {
     325                let isBatchTrainPressed = Engine.HotkeyIsPressed("session.masstribute");
     326                if (isBatchTrainPressed)
     327                {
     328                    inputState = INPUT_MASSTRIBUTING;
     329                    multiplier += multiplier == 1 ? 4 : 5;
     330                }
     331                let amounts = {
     332                    "food": (resource == "food" ? 100 : 0) * multiplier,
     333                    "wood": (resource == "wood" ? 100 : 0) * multiplier,
     334                    "stone": (resource == "stone" ? 100 : 0) * multiplier,
     335                    "metal": (resource == "metal" ? 100 : 0) * multiplier
     336                };
     337                button.tooltip = formatTributeTooltip(g_Players[i], resource, amounts[resource]);
     338                // This is in a closure so that we have access to `player`, `amounts`, and `multiplier` without some
     339                // evil global variable hackery.
     340                g_FlushTributing = function() {
     341                    Engine.PostNetworkCommand({ "type": "tribute", "player": i, "amounts":  amounts });
     342                    multiplier = 1;
     343                    button.tooltip = formatTributeTooltip(g_Players[i], resource, 100);
     344                };
     345                if (!isBatchTrainPressed)
     346                    g_FlushTributing();
     347            };
     348        })(i, resource, button);
     349    }
     350}
    361351
    362         // Diplomacy settings
    363         // Set up the buttons
    364         for (let setting of ["Ally", "Neutral", "Enemy"])
    365         {
    366             let button = Engine.GetGUIObjectByName("diplomacyPlayer"+setting+"["+(i-1)+"]");
     352function diplomacyFormatAttackRequestButton(i, hidden)
     353{
     354    let button = Engine.GetGUIObjectByName("diplomacyAttackRequest["+(i-1)+"]");
     355    button.hidden = hidden;
     356    if (hidden)
     357        return;
    367358
    368             button.caption = g_Players[g_ViewedPlayer]["is" + setting][i] ? translate("x") : "";
    369             button.onpress = (function(e){ return function() { setDiplomacy(e); }; })({ "player": i, "to": setting.toLowerCase() });
    370             button.enabled = controlsPlayer(g_ViewedPlayer);
    371             button.hidden = isCeasefireActive;
    372         }
    373     }
     359    button.enabled = controlsPlayer(g_ViewedPlayer);
     360    button.tooltip = translate("Request your allies to attack this enemy");
     361    button.onpress = (function(i) { return function() {
     362        Engine.PostNetworkCommand({ "type": "attack-request", "source": g_ViewedPlayer, "target": i });
     363    }; })(i);
     364}
    374365
    375     Engine.GetGUIObjectByName("diplomacyDialogPanel").hidden = false;
     366function diplomacyFormatStanceButtons(i, hidden)
     367{
     368    for (let stance of ["Ally", "Neutral", "Enemy"])
     369    {
     370        let button = Engine.GetGUIObjectByName("diplomacyPlayer"+stance+"["+(i-1)+"]");
     371        button.hidden = hidden;
     372        if (hidden)
     373            continue;
     374
     375        button.caption = g_Players[g_ViewedPlayer]["is" + stance][i] ? translate("x") : "";
     376        button.enabled = controlsPlayer(g_ViewedPlayer);
     377        button.onpress = (function(player, stance) { return function() {
     378            Engine.PostNetworkCommand({ "type": "diplomacy", "player": i, "to": stance.toLowerCase() });
     379        }; })(i, stance);
     380    }
    376381}
    377382
    378383function closeDiplomacy()
    379384{
    380385    g_IsDiplomacyOpen = false;
  • binaries/data/mods/public/simulation/components/Player.js

    Player.prototype.GetTeam = function()  
    361361    return this.team;
    362362};
    363363
    364364Player.prototype.SetTeam = function(team)
    365365{
    366     if (!this.teamsLocked)
    367     {
    368         this.team = team;
     366    this.team = team;
    369367
    370         var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    371         if (cmpPlayerManager && this.team != -1)
     368    let cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     369    if (cmpPlayerManager && this.team != -1)
     370    {
     371        // Set all team members as allies
     372        for (let i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
    372373        {
    373             // Set all team members as allies
    374             for (var i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
    375             {
    376                 var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player);
    377                 if (this.team == cmpPlayer.GetTeam())
    378                 {
    379                     this.SetAlly(i);
    380                     cmpPlayer.SetAlly(this.playerID);
    381                 }
    382             }
    383         }
     374            let cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player);
     375            if (this.team != cmpPlayer.GetTeam())
     376                continue;
    384377
    385         Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
     378            this.SetAlly(i);
     379            cmpPlayer.SetAlly(this.playerID);
     380        }
    386381    }
     382
     383    Engine.BroadcastMessage(MT_DiplomacyChanged, { "player": this.playerID });
    387384};
    388385
    389386Player.prototype.SetLockTeams = function(value)
    390387{
    391388    this.teamsLocked = value;
    Player.prototype.GetDiplomacy = function  
    401398    return this.diplomacy;
    402399};
    403400
    404401Player.prototype.SetDiplomacy = function(dipl)
    405402{
    406     // Should we check for teamsLocked here?
    407403    this.diplomacy = dipl;
    408     Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
     404    Engine.BroadcastMessage(MT_DiplomacyChanged, { "player": this.playerID });
    409405};
    410406
    411407Player.prototype.SetDiplomacyIndex = function(idx, value)
    412408{
    413409    var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    Player.prototype.SetDiplomacyIndex = fun  
    419415        return;
    420416
    421417    if (this.state != "active" || cmpPlayer.state != "active")
    422418        return;
    423419
    424     // You can have alliances with other players,
    425     if (this.teamsLocked)
    426     {
    427         // but can't stab your team members in the back
    428         if (this.team == -1 || this.team != cmpPlayer.GetTeam())
    429         {
    430             // Break alliance or declare war
    431             if (Math.min(this.diplomacy[idx],cmpPlayer.diplomacy[this.playerID]) > value)
    432             {
    433                 this.diplomacy[idx] = value;
    434                 cmpPlayer.SetDiplomacyIndex(this.playerID, value);
    435             }
    436             else
    437             {
    438                 this.diplomacy[idx] = value;
    439             }
    440             Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
    441         }
    442     }
    443     else
    444     {
    445         // Break alliance or declare war (worsening of relations is mutual)
    446         if (Math.min(this.diplomacy[idx],cmpPlayer.diplomacy[this.playerID]) > value)
    447         {
    448             // This is duplicated because otherwise we get too much recursion
    449             this.diplomacy[idx] = value;
    450             cmpPlayer.SetDiplomacyIndex(this.playerID, value);
    451         }
    452         else
    453         {
    454             this.diplomacy[idx] = value;
    455         }
     420    this.diplomacy[idx] = value;
     421    Engine.BroadcastMessage(MT_DiplomacyChanged, { "player": this.playerID });
    456422
    457         Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
    458     }
     423    // Mutual worsening of relations
     424    if (cmpPlayer.diplomacy[this.playerID] > value)
     425        cmpPlayer.SetDiplomacyIndex(this.playerID, value);
    459426};
    460427
    461428Player.prototype.UpdateSharedLos = function()
    462429{
    463430    let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
  • binaries/data/mods/public/simulation/helpers/Commands.js

    var g_Commands = {  
    7373        cmpGuiInterface.PushNotification({"type": "quit", "players": [player]});
    7474    },
    7575
    7676    "diplomacy": function(player, cmd, data)
    7777    {
     78        if (data.cmpPlayer.GetLockTeams())
     79        {
     80            warn("Can't change diplomacy of player " + player + " since teams are locked.");
     81            return;
     82        }
     83
     84        let cmpCeasefireManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_CeasefireManager);
     85        if (cmpCeasefireManager && cmpCeasefireManager.IsCeasefireActive())
     86        {
     87            warn("Can't change diplomacy of player " + player + " while ceasefire is active.");
     88            return;
     89        }
     90
    7891        switch(cmd.to)
    7992        {
    8093        case "ally":
    8194            data.cmpPlayer.SetAlly(cmd.player);
    8295            break;
    var g_Commands = {  
    87100            data.cmpPlayer.SetEnemy(cmd.player);
    88101            break;
    89102        default:
    90103            warn("Invalid command: Could not set "+player+" diplomacy status of player "+cmd.player+" to "+cmd.to);
    91104        }
     105
    92106        var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    93107        cmpGuiInterface.PushNotification({
    94108            "type": "diplomacy",
    95109            "players": [player],
    96110            "targetPlayer": cmd.player,