From 15a768d2322fa44a93586f0f1a7a41bf20297478 Mon Sep 17 00:00:00 2001 From: Hiran Date: Wed, 24 Apr 2024 19:39:31 +0200 Subject: [PATCH 1/3] Added vaalue selector to trade agreements --- docs/caravan.rst | 5 +- internal/caravan/tradeagreement.lua | 154 +++++++++++++++++++++++++--- 2 files changed, 140 insertions(+), 19 deletions(-) diff --git a/docs/caravan.rst b/docs/caravan.rst index d45432619d..576f4c468f 100644 --- a/docs/caravan.rst +++ b/docs/caravan.rst @@ -92,8 +92,9 @@ selected, then the range of items will be selected. Trade agreement ``````````````` -A small panel is shown with a hotkey (``Ctrl-A``) for selecting all/none in the -currently shown category. +A small panel is shown with a hotkeys: +(``Ctrl-A``) for selecting all/none in the currently shown category. +(``Ctrl-M``) for selecting items with specific base material price. Display furniture ````````````````` diff --git a/internal/caravan/tradeagreement.lua b/internal/caravan/tradeagreement.lua index c44659f3a4..25d561cbcb 100644 --- a/internal/caravan/tradeagreement.lua +++ b/internal/caravan/tradeagreement.lua @@ -1,23 +1,85 @@ --@ module = true - +local dlg = require('gui.dialogs') local gui = require('gui') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') TradeAgreementOverlay = defclass(TradeAgreementOverlay, overlay.OverlayWidget) -TradeAgreementOverlay.ATTRS{ - desc='Adds select all/none functionality when requesting trade agreement items.', - default_pos={x=45, y=-6}, - default_enabled=true, - viewscreens='dwarfmode/Diplomacy/Requests', - frame={w=25, h=3}, - frame_style=gui.MEDIUM_FRAME, - frame_background=gui.CLEAR_PEN, +TradeAgreementOverlay.ATTRS { + desc = 'Adds select all/none functionality when requesting trade agreement items.', + default_pos = { x = 45, y = -6 }, + default_enabled = true, + viewscreens = 'dwarfmode/Diplomacy/Requests', + frame = { w = 40, h = 4 }, + frame_style = gui.MEDIUM_FRAME, + frame_background = gui.CLEAR_PEN, } - local diplomacy = df.global.game.main_interface.diplomacy +local civilization = df.historical_entity.find(diplomacy.actor.civ_id) +local resources = civilization.resources +local lasttab, lastresult = -1, nil + +local function TabCtor(pages, labelSingular, labelPlural, getCivResourceList, funcGetValue) + return { + pages = pages, + labelSingular = labelSingular, + labelPlural = labelPlural, + getCivResourceList = getCivResourceList, + funcGetValue = funcGetValue, + } +end + +local Tabs = {} + +local function SquashMatIndexType(matList) + local list = {} + for key, value in pairs(matList.mat_index) do + list[key] = { type = matList.mat_type[key], index = value } + end + return list +end +local function DecodeMatInfoFromSquash(mat) + return dfhack.matinfo.decode(mat.type, mat.index).material.material_value +end + +table.insert(Tabs, TabCtor({ 6, 7 }, "gem", "gems", + function() return resources.gems end, + function(id) return dfhack.matinfo.decode(0, id).material.material_value end) +) +table.insert(Tabs, TabCtor({ 0 }, "leather", "leathers", + function() return SquashMatIndexType(resources.organic.leather) end, + DecodeMatInfoFromSquash) +) +table.insert(Tabs, TabCtor({ 29 }, "meat", "meats", + function() return SquashMatIndexType(resources.misc_mat.meat) end, + DecodeMatInfoFromSquash) +) +table.insert(Tabs, TabCtor({ 62 }, "parchment", "parchments", + function() return SquashMatIndexType(resources.organic.parchment) end, + DecodeMatInfoFromSquash) +) + +local function GetCurrentTabId() + return diplomacy.taking_requests_tablist[diplomacy.taking_requests_selected_tab]; +end + +local function GetTab() + local curtab = GetCurrentTabId() + if (curtab == lasttab) then return lastresult end + lasttab = curtab + for _, tab in ipairs(Tabs) do + for _, page in ipairs(tab.pages) do + if page == curtab then + lastresult = tab + return tab + end + end + end + lastresult = nil +end + local function diplomacy_toggle_cat() - local priority_idx = diplomacy.taking_requests_tablist[diplomacy.taking_requests_selected_tab] + local priority_idx = GetCurrentTabId() local priority = diplomacy.environment.dipev.sell_requests.priority[priority_idx] if #priority == 0 then return end local target_val = priority[0] == 0 and 4 or 0 @@ -26,13 +88,71 @@ local function diplomacy_toggle_cat() end end +local function OrderGemWithMinValue(matPrices, val) + local priority_idx = GetCurrentTabId() + + local priority = diplomacy.environment.dipev.sell_requests.priority[priority_idx] + for i in ipairs(priority) do + if matPrices[i] == val then + priority[i] = 4 + end + end +end + +local function GetCivMatPrices(tabSelected) + local resource = tabSelected.getCivResourceList() + local matPrices = {} + local matValuesUnique = {} + local filter = {} + for civid, matid in pairs(resource) do + local matPrice = tabSelected.funcGetValue(matid) + matPrices[civid] = matPrice + if not filter[matPrice] then + local val = { value = matPrice, count = 1 } + filter[matPrice] = val + table.insert(matValuesUnique, val) + else + filter[matPrice].count = filter[matPrice].count + 1 + end + end + table.sort(matValuesUnique, function(a, b) return a.value < b.value end) + return matPrices, matValuesUnique +end + +local function ValueSelector() + local currTab = GetTab() + local matPrices, matValuesUnique = GetCivMatPrices(currTab) + local list = {} + for index, value in ipairs(matValuesUnique) do + list[index] = tostring(value.value) .. + " - " .. tostring(value.count) .. " " .. (value.count == 1 and currTab.labelSingular or currTab.labelPlural) + end + dlg.showListPrompt( + "Select materials with base value", "", + COLOR_WHITE, + list, + function(id, choice) + OrderGemWithMinValue(matPrices, matValuesUnique[id].value) + end + ) +end + function TradeAgreementOverlay:init() - self:addviews{ - widgets.HotkeyLabel{ - frame={t=0, l=0}, - label='Select all/none', - key='CUSTOM_CTRL_A', - on_activate=diplomacy_toggle_cat, + self:addviews { + widgets.HotkeyLabel { + frame = { t = 0, l = 0 }, + label = 'Select all/none', + key = 'CUSTOM_CTRL_A', + on_activate = diplomacy_toggle_cat, + }, + } + self:addviews { + widgets.HotkeyLabel { + frame = { t = 1, l = 0 }, + label = 'Select materials with value', + key = 'CUSTOM_CTRL_M', + on_activate = ValueSelector, + visible = function() return GetTab() ~= nil end, }, } end From 52b2f3a23502b9b2d3becc0e2814c45444464fca Mon Sep 17 00:00:00 2001 From: Hiran Date: Wed, 24 Apr 2024 21:00:44 +0200 Subject: [PATCH 2/3] Fixed Trade agreement bug marking wrong items --- internal/caravan/tradeagreement.lua | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/internal/caravan/tradeagreement.lua b/internal/caravan/tradeagreement.lua index 25d561cbcb..ec9386c40c 100644 --- a/internal/caravan/tradeagreement.lua +++ b/internal/caravan/tradeagreement.lua @@ -15,11 +15,9 @@ TradeAgreementOverlay.ATTRS { frame_background = gui.CLEAR_PEN, } local diplomacy = df.global.game.main_interface.diplomacy -local civilization = df.historical_entity.find(diplomacy.actor.civ_id) -local resources = civilization.resources local lasttab, lastresult = -1, nil -local function TabCtor(pages, labelSingular, labelPlural, getCivResourceList, funcGetValue) +local function tabsdef(pages, labelSingular, labelPlural, getCivResourceList, funcGetValue) return { pages = pages, labelSingular = labelSingular, @@ -42,20 +40,20 @@ local function DecodeMatInfoFromSquash(mat) return dfhack.matinfo.decode(mat.type, mat.index).material.material_value end -table.insert(Tabs, TabCtor({ 6, 7 }, "gem", "gems", - function() return resources.gems end, +table.insert(Tabs, tabsdef({ 6, 7 }, "gem", "gems", + function(resources) return resources.gems end, function(id) return dfhack.matinfo.decode(0, id).material.material_value end) ) -table.insert(Tabs, TabCtor({ 0 }, "leather", "leathers", - function() return SquashMatIndexType(resources.organic.leather) end, +table.insert(Tabs, tabsdef({ 0 }, "leather", "leathers", + function(resources) return SquashMatIndexType(resources.organic.leather) end, DecodeMatInfoFromSquash) ) -table.insert(Tabs, TabCtor({ 29 }, "meat", "meats", - function() return SquashMatIndexType(resources.misc_mat.meat) end, +table.insert(Tabs, tabsdef({ 29 }, "meat", "meats", + function(resources) return SquashMatIndexType(resources.misc_mat.meat) end, DecodeMatInfoFromSquash) ) -table.insert(Tabs, TabCtor({ 62 }, "parchment", "parchments", - function() return SquashMatIndexType(resources.organic.parchment) end, +table.insert(Tabs, tabsdef({ 62 }, "parchment", "parchments", + function(resources) return SquashMatIndexType(resources.organic.parchment) end, DecodeMatInfoFromSquash) ) @@ -100,7 +98,7 @@ local function OrderGemWithMinValue(matPrices, val) end local function GetCivMatPrices(tabSelected) - local resource = tabSelected.getCivResourceList() + local resource = tabSelected.getCivResourceList(df.historical_entity.find(diplomacy.actor.civ_id).resources) local matPrices = {} local matValuesUnique = {} local filter = {} @@ -125,7 +123,7 @@ local function ValueSelector() local list = {} for index, value in ipairs(matValuesUnique) do list[index] = tostring(value.value) .. - " - " .. tostring(value.count) .. " " .. (value.count == 1 and currTab.labelSingular or currTab.labelPlural) + " - " .. tostring(value.count) .. " " .. (value.count == 1 and currTab.labelSingular or currTab.labelPlural) end dlg.showListPrompt( "Select materials with base value", "", From b1f6aedb9d5138cb96264b6c7b6509d5324fe3c1 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Wed, 12 Jun 2024 19:21:43 -0700 Subject: [PATCH 3/3] clean up implementation; add changelog --- changelog.txt | 1 + docs/caravan.rst | 8 +- internal/caravan/tradeagreement.lua | 187 +++++++++++++--------------- 3 files changed, 92 insertions(+), 104 deletions(-) diff --git a/changelog.txt b/changelog.txt index 318f8a2956..92144ce200 100644 --- a/changelog.txt +++ b/changelog.txt @@ -65,6 +65,7 @@ Template for new versions: - `gui/civ-alert`: you can now register multiple burrows as civilian alert safe spaces - `lever`: enable use of ``reqscript`` to access lever pulling functions. - `exterminate`: add ``all`` target for convenient scorched earth tactics +- `caravan`: add shortcut to the trade request screen for selecting item types by value ## Removed - `max-wave`: merged into `pop-control` diff --git a/docs/caravan.rst b/docs/caravan.rst index 576f4c468f..e29f149844 100644 --- a/docs/caravan.rst +++ b/docs/caravan.rst @@ -92,9 +92,11 @@ selected, then the range of items will be selected. Trade agreement ``````````````` -A small panel is shown with a hotkeys: -(``Ctrl-A``) for selecting all/none in the currently shown category. -(``Ctrl-M``) for selecting items with specific base material price. +A small panel is shown with some useful shortcuts: + +* ``Ctrl-A`` for selecting all/none in the currently shown category. +* ``Ctrl-M`` for selecting items with specific base material price (only + enabled for item categories where this matters, like gems and leather). Display furniture ````````````````` diff --git a/internal/caravan/tradeagreement.lua b/internal/caravan/tradeagreement.lua index ec9386c40c..946d2b7a61 100644 --- a/internal/caravan/tradeagreement.lua +++ b/internal/caravan/tradeagreement.lua @@ -4,81 +4,66 @@ local gui = require('gui') local overlay = require('plugins.overlay') local widgets = require('gui.widgets') -TradeAgreementOverlay = defclass(TradeAgreementOverlay, overlay.OverlayWidget) -TradeAgreementOverlay.ATTRS { - desc = 'Adds select all/none functionality when requesting trade agreement items.', - default_pos = { x = 45, y = -6 }, - default_enabled = true, - viewscreens = 'dwarfmode/Diplomacy/Requests', - frame = { w = 40, h = 4 }, - frame_style = gui.MEDIUM_FRAME, - frame_background = gui.CLEAR_PEN, -} local diplomacy = df.global.game.main_interface.diplomacy -local lasttab, lastresult = -1, nil - -local function tabsdef(pages, labelSingular, labelPlural, getCivResourceList, funcGetValue) - return { - pages = pages, - labelSingular = labelSingular, - labelPlural = labelPlural, - getCivResourceList = getCivResourceList, - funcGetValue = funcGetValue, - } -end -local Tabs = {} +TradeAgreementOverlay = defclass(TradeAgreementOverlay, overlay.OverlayWidget) +TradeAgreementOverlay.ATTRS{ + desc='Adds quick toggles for groups of trade agreement items.', + default_pos={x=45, y=-6}, + default_enabled=true, + viewscreens='dwarfmode/Diplomacy/Requests', + frame={w=40, h=4}, + frame_style=gui.MEDIUM_FRAME, + frame_background=gui.CLEAR_PEN, +} -local function SquashMatIndexType(matList) +local function transform_mat_list(matList) local list = {} for key, value in pairs(matList.mat_index) do - list[key] = { type = matList.mat_type[key], index = value } + list[key] = {type=matList.mat_type[key], index=value} end return list end -local function DecodeMatInfoFromSquash(mat) + +local function decode_mat_list(mat) return dfhack.matinfo.decode(mat.type, mat.index).material.material_value end -table.insert(Tabs, tabsdef({ 6, 7 }, "gem", "gems", - function(resources) return resources.gems end, - function(id) return dfhack.matinfo.decode(0, id).material.material_value end) -) -table.insert(Tabs, tabsdef({ 0 }, "leather", "leathers", - function(resources) return SquashMatIndexType(resources.organic.leather) end, - DecodeMatInfoFromSquash) -) -table.insert(Tabs, tabsdef({ 29 }, "meat", "meats", - function(resources) return SquashMatIndexType(resources.misc_mat.meat) end, - DecodeMatInfoFromSquash) -) -table.insert(Tabs, tabsdef({ 62 }, "parchment", "parchments", - function(resources) return SquashMatIndexType(resources.organic.parchment) end, - DecodeMatInfoFromSquash) -) +local select_by_value_tab = { + Leather={ + get_mats=function(resources) return transform_mat_list(resources.organic.leather) end, + decode=decode_mat_list, + }, + SmallCutGems={ + get_mats=function(resources) return resources.gems end, + decode=function(id) return dfhack.matinfo.decode(0, id).material.material_value end, + }, + Meat={ + get_mats=function(resources) return transform_mat_list(resources.misc_mat.meat) end, + decode=decode_mat_list, + }, + Parchment={ + get_mats=function(resources) return transform_mat_list(resources.organic.parchment) end, + decode=decode_mat_list, + }, +} +select_by_value_tab.LargeCutGems = select_by_value_tab.SmallCutGems -local function GetCurrentTabId() - return diplomacy.taking_requests_tablist[diplomacy.taking_requests_selected_tab]; +local function get_cur_tab_category() + return diplomacy.taking_requests_tablist[diplomacy.taking_requests_selected_tab] end -local function GetTab() - local curtab = GetCurrentTabId() - if (curtab == lasttab) then return lastresult end - lasttab = curtab - for _, tab in ipairs(Tabs) do - for _, page in ipairs(tab.pages) do - if page == curtab then - lastresult = tab - return tab - end - end - end - lastresult = nil +local function get_select_by_value_tab(category) + category = category or get_cur_tab_category() + return select_by_value_tab[df.entity_sell_category[category]] +end + +local function get_cur_priority_list() + return diplomacy.environment.dipev.sell_requests.priority[get_cur_tab_category()] end local function diplomacy_toggle_cat() - local priority_idx = GetCurrentTabId() - local priority = diplomacy.environment.dipev.sell_requests.priority[priority_idx] + local priority = get_cur_priority_list() if #priority == 0 then return end local target_val = priority[0] == 0 and 4 or 0 for i in ipairs(priority) do @@ -86,71 +71,71 @@ local function diplomacy_toggle_cat() end end -local function OrderGemWithMinValue(matPrices, val) - local priority_idx = GetCurrentTabId() - - local priority = diplomacy.environment.dipev.sell_requests.priority[priority_idx] +local function select_by_value(prices, val) + local priority = get_cur_priority_list() for i in ipairs(priority) do - if matPrices[i] == val then + if prices[i] == val then priority[i] = 4 end end end -local function GetCivMatPrices(tabSelected) - local resource = tabSelected.getCivResourceList(df.historical_entity.find(diplomacy.actor.civ_id).resources) - local matPrices = {} +function TradeAgreementOverlay:init() + self:addviews{ + widgets.HotkeyLabel{ + frame={t=0, l=0}, + label='Select all/none', + key='CUSTOM_CTRL_A', + on_activate=diplomacy_toggle_cat, + }, + } + self:addviews{ + widgets.HotkeyLabel{ + frame={t=1, l=0}, + label='Select materials by value', + key='CUSTOM_CTRL_M', + on_activate=self:callback('select_by_value'), + enabled=get_select_by_value_tab, + }, + } +end + +local function get_prices(tab) + local resource = tab.get_mats(df.historical_entity.find(diplomacy.actor.civ_id).resources) + local prices = {} local matValuesUnique = {} local filter = {} for civid, matid in pairs(resource) do - local matPrice = tabSelected.funcGetValue(matid) - matPrices[civid] = matPrice - if not filter[matPrice] then - local val = { value = matPrice, count = 1 } - filter[matPrice] = val + local price = tab.decode(matid) + prices[civid] = price + if not filter[price] then + local val = {value=price, count=1} + filter[price] = val table.insert(matValuesUnique, val) else - filter[matPrice].count = filter[matPrice].count + 1 + filter[price].count = filter[price].count + 1 end end table.sort(matValuesUnique, function(a, b) return a.value < b.value end) - return matPrices, matValuesUnique + return prices, matValuesUnique end -local function ValueSelector() - local currTab = GetTab() - local matPrices, matValuesUnique = GetCivMatPrices(currTab) +function TradeAgreementOverlay:select_by_value() + local cat = get_cur_tab_category() + local cur_tab = get_select_by_value_tab(cat) + + local resource_name = df.entity_sell_category[cat] + if resource_name:endswith('Gems') then resource_name = 'Gem' end + local prices, matValuesUnique = get_prices(cur_tab) local list = {} for index, value in ipairs(matValuesUnique) do - list[index] = tostring(value.value) .. - " - " .. tostring(value.count) .. " " .. (value.count == 1 and currTab.labelSingular or currTab.labelPlural) + list[index] = ('%4d%s (%d type%s of %s)'):format( + value.value, string.char(15), value.count, value.count == 1 and '' or 's', resource_name:lower()) end dlg.showListPrompt( "Select materials with base value", "", COLOR_WHITE, list, - function(id, choice) - OrderGemWithMinValue(matPrices, matValuesUnique[id].value) - end + function(id) select_by_value(prices, matValuesUnique[id].value) end ) end - -function TradeAgreementOverlay:init() - self:addviews { - widgets.HotkeyLabel { - frame = { t = 0, l = 0 }, - label = 'Select all/none', - key = 'CUSTOM_CTRL_A', - on_activate = diplomacy_toggle_cat, - }, - } - self:addviews { - widgets.HotkeyLabel { - frame = { t = 1, l = 0 }, - label = 'Select materials with value', - key = 'CUSTOM_CTRL_M', - on_activate = ValueSelector, - visible = function() return GetTab() ~= nil end, - }, - } -end