From a934dfad5f16bbf12d28f8d538b87e2ebeef8fb5 Mon Sep 17 00:00:00 2001 From: elexis Date: Tue, 28 Mar 2017 02:34:32 +0000 Subject: [PATCH] Add barter buttons to the trade window for quicker access and to support mods with more than four resource types. The dialog is resized automatically and can be opened with a new hotkey. Patch By: s0600204 Differential Revision: https://code.wildfiregames.com/D88 Fixes #4366 Refs #3934 This was SVN commit r19354. --- binaries/data/config/default.cfg | 7 +- .../mods/public/gui/session/hotkeys/misc.xml | 17 +- binaries/data/mods/public/gui/session/menu.js | 205 +++++++++++++++--- .../public/gui/session/selection_panels.js | 83 +------ .../gui/session/selection_panels_helpers.js | 3 - .../data/mods/public/gui/session/session.js | 15 ++ .../gui/session/top_panel/button_trade.xml | 3 +- .../mods/public/gui/session/trade_window.xml | 125 ++++++++--- .../mods/public/gui/session/unit_actions.js | 2 +- .../simulation/components/GuiInterface.js | 14 +- .../components/tests/test_GuiInterface.js | 11 +- 11 files changed, 325 insertions(+), 160 deletions(-) diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index 9617e16952..dabd9d624b 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -157,8 +157,6 @@ console.toggle = BackQuote, F9 ; Open/close console fps.toggle = "Alt+F" ; Toggle frame counter realtime.toggle = "Alt+T" ; Toggle current display of computer time session.devcommands.toggle = "Alt+D" ; Toggle developer commands panel -session.gui.toggle = "Alt+G" ; Toggle visibility of session GUI -menu.toggle = "F10" ; Toggle in-game menu timeelapsedcounter.toggle = "F12" ; Toggle time elapsed counter session.showstatusbars = Tab ; Toggle display of status bars session.highlightguarding = PgDn ; Toggle highlight of guarding units @@ -295,6 +293,11 @@ deselectgroup = Ctrl ; Modifier to deselect units when clicking group ic rotate.cw = RightBracket ; Rotate building placement preview clockwise rotate.ccw = LeftBracket ; Rotate building placement preview anticlockwise +[hotkey.session.gui] +toggle = "Alt+G" ; Toggle visibility of session GUI +menu.toggle = "F10" ; Toggle in-game menu +barter.toggle = "Ctrl+B" ; Toggle in-game barter/trade page + [hotkey.session.savedgames] delete = Delete ; Delete the selected saved game asking confirmation noconfirmation = Shift ; Do not ask confirmation when deleting a game diff --git a/binaries/data/mods/public/gui/session/hotkeys/misc.xml b/binaries/data/mods/public/gui/session/hotkeys/misc.xml index 2e2fda1b0d..a81d2e6b81 100644 --- a/binaries/data/mods/public/gui/session/hotkeys/misc.xml +++ b/binaries/data/mods/public/gui/session/hotkeys/misc.xml @@ -16,10 +16,14 @@ openChat(g_LastChatAddressee); - + toggleMenu(); + + toggleTrade(); + + var newSetting = !Engine.Renderer_GetSilhouettesEnabled(); @@ -72,8 +76,15 @@ - updateSelectionDetails(); - updateSelectionDetails(); + + updateSelectionDetails(); + updateBarterButtons(); + + + + updateSelectionDetails(); + updateBarterButtons(); + diff --git a/binaries/data/mods/public/gui/session/menu.js b/binaries/data/mods/public/gui/session/menu.js index 2b1d0d3f4f..474e9a272d 100644 --- a/binaries/data/mods/public/gui/session/menu.js +++ b/binaries/data/mods/public/gui/session/menu.js @@ -28,6 +28,26 @@ const STEP = 5; // Shown in the trade dialog. const g_IdleTraderTextColor = "orange"; +/** + * Quantity of goods to sell per click. + */ +const g_BarterResourceSellQuantity = 100; + +/** + * Multiplier to be applied when holding the massbarter hotkey. + */ +const g_BarterMultiplier = 5; + +/** + * Barter actions, as mapped to the names of GUI Buttons. + */ +const g_BarterActions = ["Buy", "Sell"]; + +/** + * Currently selected resource type to sell in the barter GUI. + */ +var g_BarterSell; + var g_IsMenuOpen = false; var g_IsDiplomacyOpen = false; @@ -565,39 +585,51 @@ function openTrade() g_IsTradeOpen = true; - var updateButtons = function() + let proba = Engine.GuiInterfaceCall("GetTradingGoods", g_ViewedPlayer); + let button = {}; + let resCodes = g_ResourceData.GetCodes(); + let currTradeSelection = resCodes[0]; + + let updateTradeButtons = function() { for (let res in button) { button[res].label.caption = proba[res] + "%"; - button[res].sel.hidden = !controlsPlayer(g_ViewedPlayer) || res != selec; - button[res].up.hidden = !controlsPlayer(g_ViewedPlayer) || res == selec || proba[res] == 100 || proba[selec] == 0; - button[res].dn.hidden = !controlsPlayer(g_ViewedPlayer) || res == selec || proba[res] == 0 || proba[selec] == 100; + button[res].sel.hidden = !controlsPlayer(g_ViewedPlayer) || res != currTradeSelection; + button[res].up.hidden = !controlsPlayer(g_ViewedPlayer) || res == currTradeSelection || proba[res] == 100 || proba[currTradeSelection] == 0; + button[res].dn.hidden = !controlsPlayer(g_ViewedPlayer) || res == currTradeSelection || proba[res] == 0 || proba[currTradeSelection] == 100; } }; - let proba = Engine.GuiInterfaceCall("GetTradingGoods", g_ViewedPlayer); - let button = {}; - let resCodes = g_ResourceData.GetCodes(); - let selec = resCodes[0]; hideRemaining("tradeResources", resCodes.length); Engine.GetGUIObjectByName("tradeHelp").hidden = false; for (let i = 0; i < resCodes.length; ++i) { + let resCode = resCodes[i]; + + let barterResource = Engine.GetGUIObjectByName("barterResource[" + i + "]") + if (!barterResource) + { + warn("Current GUI limits prevent displaying more than " + i + " resources in the barter dialog!"); + break; + } + + // Barter: + barterOpenCommon(resCode, i, "barter"); + setPanelObjectPosition(barterResource, i, i+1); + + // Trade: let tradeResource = Engine.GetGUIObjectByName("tradeResource["+i+"]"); if (!tradeResource) { - warn("Current GUI limits prevent displaying more than " + r + " resources in the trading goods selection dialog!"); + warn("Current GUI limits prevent displaying more than " + i + " resources in the trading goods selection dialog!"); break; } setPanelObjectPosition(tradeResource, i, i+1); - let resCode = resCodes[i]; - proba[resCode] = proba[resCode] || 0; - let icon = Engine.GetGUIObjectByName("tradeResourceIcon["+i+"]"); icon.sprite = "stretched:session/icons/resources/" + resCode + ".png"; @@ -611,10 +643,12 @@ function openTrade() "sel": Engine.GetGUIObjectByName("tradeResourceSelection["+i+"]") }; + proba[resCode] = proba[resCode] || 0; + let buttonResource = Engine.GetGUIObjectByName("tradeResourceButton["+i+"]"); buttonResource.enabled = controlsPlayer(g_ViewedPlayer); - buttonResource.onPress = (function(resource){ - return function() { + buttonResource.onPress = (resource => { + return () => { if (Engine.HotkeyIsPressed("session.fulltradeswap")) { for (let res of resCodes) @@ -622,39 +656,152 @@ function openTrade() proba[resource] = 100; Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); } - selec = resource; - updateButtons(); + currTradeSelection = resource; + updateTradeButtons(); }; })(resCode); buttonUp.enabled = controlsPlayer(g_ViewedPlayer); - buttonUp.onPress = (function(resource){ - return function() { - proba[resource] += Math.min(STEP, proba[selec]); - proba[selec] -= Math.min(STEP, proba[selec]); + buttonUp.onPress = (resource => { + return () => { + proba[resource] += Math.min(STEP, proba[currTradeSelection]); + proba[currTradeSelection] -= Math.min(STEP, proba[currTradeSelection]); Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); - updateButtons(); + updateTradeButtons(); }; })(resCode); buttonDn.enabled = controlsPlayer(g_ViewedPlayer); - buttonDn.onPress = (function(resource){ - return function() { - proba[selec] += Math.min(STEP, proba[resource]); + buttonDn.onPress = (resource => { + return () => { + proba[currTradeSelection] += Math.min(STEP, proba[resource]); proba[resource] -= Math.min(STEP, proba[resource]); Engine.PostNetworkCommand({"type": "set-trading-goods", "tradingGoods": proba}); - updateButtons(); + updateTradeButtons(); }; })(resCode); } - updateButtons(); - let traderNumber = Engine.GuiInterfaceCall("GetTraderNumber", g_ViewedPlayer); - Engine.GetGUIObjectByName("landTraders").caption = getIdleLandTradersText(traderNumber); - Engine.GetGUIObjectByName("shipTraders").caption = getIdleShipTradersText(traderNumber); + updateTradeButtons(); + updateTraderTexts(); + Engine.GetGUIObjectByName("tradeDialogPanel").hidden = false; } +function updateTraderTexts() +{ + let traderNumber = Engine.GuiInterfaceCall("GetTraderNumber", g_ViewedPlayer); + Engine.GetGUIObjectByName("traderCountText").caption = getIdleLandTradersText(traderNumber) + "\n\n" + getIdleShipTradersText(traderNumber); +} + +/** + * Code common to both the Barter Panel and the Trade/Barter Dialog, that + * only needs to be run when the panel or dialog is opened by the player. + * + * @param {string} resourceCode + * @param {number} idx - Element index within its set + * @param {string} prefix - Common prefix of the gui elements to be worked upon + */ +function barterOpenCommon(resourceCode, idx, prefix) +{ + let barterButton = {}; + for (let action of g_BarterActions) + barterButton[action] = Engine.GetGUIObjectByName(prefix + action + "Button[" + idx + "]"); + + let resource = getLocalizedResourceName(g_ResourceData.GetNames()[resourceCode], "withinSentence"); + barterButton.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource }); + barterButton.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource }); + + barterButton.Sell.onPress = function() { + g_BarterSell = resourceCode; + updateSelectionDetails(); + updateBarterButtons(); + }; +} + +/** + * Code common to both the Barter Panel and the Trade/Barter Dialog, that + * needs to be run on simulation update and when relevant hotkeys + * (i.e. massbarter) are pressed. + * + * @param {string} resourceCode + * @param {number} idx - Element index within its set + * @param {string} prefix - Common prefix of the gui elements to be worked upon + * @param {number} player + */ +function barterUpdateCommon(resourceCode, idx, prefix, player) +{ + let barterButton = {}; + let barterIcon = {}; + let barterAmount = {}; + for (let action of g_BarterActions) + { + barterButton[action] = Engine.GetGUIObjectByName(prefix + action + "Button[" + idx + "]"); + barterIcon[action] = Engine.GetGUIObjectByName(prefix + action + "Icon[" + idx + "]"); + barterAmount[action] = Engine.GetGUIObjectByName(prefix + action + "Amount[" + idx + "]"); + } + let selectionIcon = Engine.GetGUIObjectByName(prefix + "SellSelection[" + idx + "]"); + + let amountToSell = g_BarterResourceSellQuantity; + if (Engine.HotkeyIsPressed("session.massbarter")) + amountToSell *= g_BarterMultiplier; + + let isSelected = resourceCode == g_BarterSell; + let grayscale = isSelected ? "color:0 0 0 100:grayscale:" : ""; + + // Select color of the sell button + let neededRes = {}; + neededRes[resourceCode] = amountToSell; + let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", { + "cost": neededRes, + "player": player + }) ? "color:255 0 0 80:" : ""; + + // Select color of the buy button + neededRes = {}; + neededRes[g_BarterSell] = amountToSell; + let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", { + "cost": neededRes, + "player": player + }) ? "color:255 0 0 80:" : ""; + + barterIcon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + resourceCode + ".png"; + barterIcon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + resourceCode + ".png"; + + barterAmount.Sell.caption = "-" + amountToSell; + let prices = GetSimState().barterPrices; + barterAmount.Buy.caption = "+" + Math.round(prices.sell[g_BarterSell] / prices.buy[resourceCode] * amountToSell); + + barterButton.Buy.onPress = function() { + Engine.PostNetworkCommand({ + "type": "barter", + "sell": g_BarterSell, + "buy": resourceCode, + "amount": amountToSell + }); + }; + + barterButton.Buy.hidden = isSelected; + barterButton.Buy.enabled = controlsPlayer(player); + barterButton.Sell.hidden = false; + selectionIcon.hidden = !isSelected; +} + +function updateBarterButtons() +{ + let canBarter = GetSimState().players[g_ViewedPlayer].canBarter; + Engine.GetGUIObjectByName("barterNoMarketsMessage").hidden = canBarter; + Engine.GetGUIObjectByName("barterResources").hidden = !canBarter; + Engine.GetGUIObjectByName("barterHelp").hidden = !canBarter; + + if (!canBarter) + return; + + let resCodes = g_ResourceData.GetCodes(); + for (let i = 0; i < resCodes.length; ++i) + barterUpdateCommon(resCodes[i], i, "barter", g_ViewedPlayer); +} + function getIdleLandTradersText(traderNumber) { let active = traderNumber.landTrader.trading; diff --git a/binaries/data/mods/public/gui/session/selection_panels.js b/binaries/data/mods/public/gui/session/selection_panels.js index f6b3f793aa..dc258ada35 100644 --- a/binaries/data/mods/public/gui/session/selection_panels.js +++ b/binaries/data/mods/public/gui/session/selection_panels.js @@ -33,8 +33,6 @@ let g_FormationsInfo = new Map(); let g_SelectionPanels = {}; -let g_BarterSell; - g_SelectionPanels.Alert = { "getMaxNumberOfItems": function() { @@ -104,86 +102,19 @@ g_SelectionPanels.Barter = { "conflictsWith": ["Alert", "Garrison"], "getItems": function(unitEntStates) { - if (unitEntStates.every(state => !state.barterMarket)) + // If more than `rowLength` resources, don't display icons. + if (unitEntStates.every(state => !state.isBarterMarket) || g_ResourceData.GetCodes().length > this.rowLength) return []; return g_ResourceData.GetCodes(); }, "setupButton": function(data) { - // data.item is the resource name in this case + barterOpenCommon(data.item, data.i, "unitBarter"); + barterUpdateCommon(data.item, data.i, "unitBarter", data.player); + let button = {}; - let icon = {}; - let amount = {}; - for (let a of BARTER_ACTIONS) - { - button[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Button[" + data.i + "]"); - icon[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Icon[" + data.i + "]"); - amount[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Amount[" + data.i + "]"); - } - let selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection[" + data.i + "]"); - - let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL; - if (Engine.HotkeyIsPressed("session.massbarter")) - amountToSell *= BARTER_BUNCH_MULTIPLIER; - - if (!g_BarterSell) - g_BarterSell = g_ResourceData.GetCodes()[0]; - - amount.Sell.caption = "-" + amountToSell; - let prices; - for (let state of data.unitEntStates) - if (state.barterMarket) - { - prices = state.barterMarket.prices; - break; - } - - amount.Buy.caption = "+" + Math.round(prices.sell[g_BarterSell] / prices.buy[data.item] * amountToSell); - - let resource = getLocalizedResourceName(g_ResourceData.GetNames()[data.item], "withinSentence"); - button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource }); - button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource }); - - button.Sell.onPress = function() { - g_BarterSell = data.item; - updateSelectionDetails(); - }; - - button.Buy.onPress = function() { - Engine.PostNetworkCommand({ - "type": "barter", - "sell": g_BarterSell, - "buy": data.item, - "amount": amountToSell - }); - }; - - let isSelected = data.item == g_BarterSell; - let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : ""; - - // do we have enough of this resource to sell? - let neededRes = {}; - neededRes[data.item] = amountToSell; - let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", { - "cost": neededRes, - "player": data.player - }) ? "color:255 0 0 80:" : ""; - - // Let's see if we have enough resources to barter. - neededRes = {}; - neededRes[g_BarterSell] = amountToSell; - let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", { - "cost": neededRes, - "player": data.player - }) ? "color:255 0 0 80:" : ""; - - icon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png"; - icon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png"; - - button.Buy.hidden = isSelected; - button.Buy.enabled = controlsPlayer(data.player); - button.Sell.hidden = false; - selectionIcon.hidden = !isSelected; + for (let action of g_BarterActions) + button[action] = Engine.GetGUIObjectByName("unitBarter" + action + "Button[" + data.i + "]"); setPanelObjectPosition(button.Sell, data.i, data.rowLength); setPanelObjectPosition(button.Buy, data.i + data.rowLength, data.rowLength); diff --git a/binaries/data/mods/public/gui/session/selection_panels_helpers.js b/binaries/data/mods/public/gui/session/selection_panels_helpers.js index 32f7c95de1..64979399eb 100644 --- a/binaries/data/mods/public/gui/session/selection_panels_helpers.js +++ b/binaries/data/mods/public/gui/session/selection_panels_helpers.js @@ -1,6 +1,3 @@ -const BARTER_RESOURCE_AMOUNT_TO_SELL = 100; -const BARTER_BUNCH_MULTIPLIER = 5; -const BARTER_ACTIONS = ["Sell", "Buy"]; const GATE_ACTIONS = ["lock", "unlock"]; const UPGRADING_NOT_STARTED = -2; diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index 5fbd6792e6..c68f72778c 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -271,6 +271,8 @@ function init(initData, hotloadData) g_CivData = loadCivData(); g_CivData.gaia = { "Code": "gaia", "Name": translate("Gaia") }; + g_BarterSell = g_ResourceData.GetCodes()[0]; + initializeMusic(); // before changing the perspective let gameSpeed = Engine.GetGUIObjectByName("gameSpeed"); @@ -398,6 +400,13 @@ function updateHotkeyTooltips() Engine.GetGUIObjectByName("tradeHelp").tooltip = colorizeHotkey( translate("Select one type of goods you want to modify by clicking on it (Pressing %(hotkey)s while selecting will also bring its share to 100%%) and then use the arrows of the other types to modify their shares."), "session.fulltradeswap"); + + Engine.GetGUIObjectByName("barterHelp").tooltip = sprintf( + translate("Start by selecting the resource from the upper row that you wish to sell. Upon each press on one of the lower buttons, %(quantity)s of the upper resource will be sold for the displayed quantity of the lower. Press and hold %(hotkey)s to temporarily multiply all quantities by %(multiplier)s."), { + "quantity": g_BarterResourceSellQuantity, + "hotkey": colorizeHotkey("%(hotkey)s", "session.massbarter"), + "multiplier": g_BarterMultiplier + }); } function initGUIHeroes(slot) @@ -833,6 +842,12 @@ function updateGUIObjects() updateTimeNotifications(); updateIdleWorkerButton(); + if (g_IsTradeOpen) + { + updateTraderTexts(); + updateBarterButtons(); + } + if (g_ViewedPlayer > 0) { let playerState = GetSimState().players[g_ViewedPlayer]; diff --git a/binaries/data/mods/public/gui/session/top_panel/button_trade.xml b/binaries/data/mods/public/gui/session/top_panel/button_trade.xml index 787c1f7602..66e9d5651a 100644 --- a/binaries/data/mods/public/gui/session/top_panel/button_trade.xml +++ b/binaries/data/mods/public/gui/session/top_panel/button_trade.xml @@ -5,9 +5,8 @@ style="iconButton" tooltip_style="sessionToolTip" > - - Trade + Barter & Trade toggleTrade(); diff --git a/binaries/data/mods/public/gui/session/trade_window.xml b/binaries/data/mods/public/gui/session/trade_window.xml index 537eac5051..d6012c6241 100644 --- a/binaries/data/mods/public/gui/session/trade_window.xml +++ b/binaries/data/mods/public/gui/session/trade_window.xml @@ -1,48 +1,111 @@