Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 61 additions & 122 deletions internal/caravan/trade.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,14 @@ local common = reqscript('internal/caravan/common')
local gui = require('gui')
local overlay = require('plugins.overlay')
local predicates = reqscript('internal/caravan/predicates')
local utils = require('utils')
local widgets = require('gui.widgets')

trader_selected_state = trader_selected_state or {}
broker_selected_state = broker_selected_state or {}
handle_ctrl_click_on_render = handle_ctrl_click_on_render or false
handle_shift_click_on_render = handle_shift_click_on_render or false

local GOODFLAG = {
UNCONTAINED_UNSELECTED = 0,
UNCONTAINED_SELECTED = 1,
CONTAINED_UNSELECTED = 2,
CONTAINED_SELECTED = 3,
CONTAINER_COLLAPSED_UNSELECTED = 4,
CONTAINER_COLLAPSED_SELECTED = 5,
}

local trade = df.global.game.main_interface.trade

-- -------------------
Expand All @@ -39,50 +31,13 @@ Trade.ATTRS {
resize_min={w=48, h=40},
}

local TOGGLE_MAP = {
[GOODFLAG.UNCONTAINED_UNSELECTED] = GOODFLAG.UNCONTAINED_SELECTED,
[GOODFLAG.UNCONTAINED_SELECTED] = GOODFLAG.UNCONTAINED_UNSELECTED,
[GOODFLAG.CONTAINED_UNSELECTED] = GOODFLAG.CONTAINED_SELECTED,
[GOODFLAG.CONTAINED_SELECTED] = GOODFLAG.CONTAINED_UNSELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED] = GOODFLAG.CONTAINER_COLLAPSED_SELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_SELECTED] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED,
}

local TARGET_MAP = {
[true]={
[GOODFLAG.UNCONTAINED_UNSELECTED] = GOODFLAG.UNCONTAINED_SELECTED,
[GOODFLAG.UNCONTAINED_SELECTED] = GOODFLAG.UNCONTAINED_SELECTED,
[GOODFLAG.CONTAINED_UNSELECTED] = GOODFLAG.CONTAINED_SELECTED,
[GOODFLAG.CONTAINED_SELECTED] = GOODFLAG.CONTAINED_SELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED] = GOODFLAG.CONTAINER_COLLAPSED_SELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_SELECTED] = GOODFLAG.CONTAINER_COLLAPSED_SELECTED,
},
[false]={
[GOODFLAG.UNCONTAINED_UNSELECTED] = GOODFLAG.UNCONTAINED_UNSELECTED,
[GOODFLAG.UNCONTAINED_SELECTED] = GOODFLAG.UNCONTAINED_UNSELECTED,
[GOODFLAG.CONTAINED_UNSELECTED] = GOODFLAG.CONTAINED_UNSELECTED,
[GOODFLAG.CONTAINED_SELECTED] = GOODFLAG.CONTAINED_UNSELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_SELECTED] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED,
},
}

local TARGET_REVMAP = {
[GOODFLAG.UNCONTAINED_UNSELECTED] = false,
[GOODFLAG.UNCONTAINED_SELECTED] = true,
[GOODFLAG.CONTAINED_UNSELECTED] = false,
[GOODFLAG.CONTAINED_SELECTED] = true,
[GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED] = false,
[GOODFLAG.CONTAINER_COLLAPSED_SELECTED] = true,
}

local function get_entry_icon(data)
if TARGET_REVMAP[trade.goodflag[data.list_idx][data.item_idx]] then
if trade.goodflag[data.list_idx][data.item_idx].selected then
return common.ALL_PEN
end
end

local function sort_noop(a, b)
local function sort_noop()
-- this function is used as a marker and never actually gets called
error('sort_noop should not be called')
end
Expand Down Expand Up @@ -375,7 +330,7 @@ function Trade:cache_choices(list_idx, trade_bins)
local parent_data
for item_idx, item in ipairs(trade.good[list_idx]) do
local goodflag = goodflags[item_idx]
if goodflag ~= GOODFLAG.CONTAINED_UNSELECTED and goodflag ~= GOODFLAG.CONTAINED_SELECTED then
if not goodflag.contained then
parent_data = nil
end
local is_banned, is_risky = common.scan_banned(item, self.risky_items)
Expand Down Expand Up @@ -487,11 +442,13 @@ end

local function toggle_item_base(choice, target_value)
local goodflag = trade.goodflag[choice.data.list_idx][choice.data.item_idx]
local goodflag_map = target_value == nil and TOGGLE_MAP or TARGET_MAP[target_value]
trade.goodflag[choice.data.list_idx][choice.data.item_idx] = goodflag_map[goodflag]
target_value = TARGET_REVMAP[trade.goodflag[choice.data.list_idx][choice.data.item_idx]]
if target_value == nil then
target_value = not goodflag.selected
end
local prev_value = goodflag.selected
goodflag.selected = target_value
if choice.data.update_container_fn then
choice.data.update_container_fn(TARGET_REVMAP[goodflag], target_value)
choice.data.update_container_fn(prev_value, target_value)
end
return target_value
end
Expand Down Expand Up @@ -591,39 +548,40 @@ local function set_height(list_idx, delta)
trade.i_height[list_idx] - page_height))
end

local function select_shift_clicked_container_items(new_state, old_state, list_idx)
local function flags_match(goodflag1, goodflag2)
return goodflag1.selected == goodflag2.selected and
goodflag1.contained == goodflag2.contained and
goodflag1.container_collapsed == goodflag2.container_collapsed and
goodflag1.filtered_off == goodflag2.filtered_off
end

local function select_shift_clicked_container_items(new_state, old_state_fn, list_idx)
-- if ctrl is also held, collapse the container too
local also_collapse = dfhack.internal.getModifiers().ctrl
local collapsed_item_count, collapsing_container, in_container = 0, false, false
local collapsed_item_count, collapsing_container, in_target_container = 0, false, false
for k, goodflag in ipairs(new_state) do
if in_container then
if goodflag <= GOODFLAG.UNCONTAINED_SELECTED
or goodflag >= GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED then
break
end

new_state[k] = GOODFLAG.CONTAINED_SELECTED

if in_target_container then
if not goodflag.contained then break end
goodflag.selected = true
if collapsing_container then
collapsed_item_count = collapsed_item_count + 1
end
goto continue
end

if goodflag == old_state[k] then goto continue end
local old_goodflag = old_state_fn(k)
if flags_match(goodflag, old_goodflag) then goto continue end
local is_container = df.item_binst:is_instance(trade.good[list_idx][k])
if not is_container then goto continue end

-- deselect the container itself
if also_collapse or
old_state[k] == GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED or
old_state[k] == GOODFLAG.CONTAINER_COLLAPSED_SELECTED then
collapsing_container = goodflag == GOODFLAG.UNCONTAINED_SELECTED
new_state[k] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED
else
new_state[k] = GOODFLAG.UNCONTAINED_UNSELECTED
goodflag.selected = false

if also_collapse or old_goodflag.container_collapsed then
goodflag.container_collapsed = true
collapsing_container = not old_goodflag.container_collapsed
end
in_container = true
in_target_container = true

::continue::
end
Expand All @@ -633,37 +591,27 @@ local function select_shift_clicked_container_items(new_state, old_state, list_i
end
end

local CTRL_CLICK_STATE_MAP = {
[GOODFLAG.UNCONTAINED_UNSELECTED] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED,
[GOODFLAG.UNCONTAINED_SELECTED] = GOODFLAG.CONTAINER_COLLAPSED_SELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED] = GOODFLAG.UNCONTAINED_UNSELECTED,
[GOODFLAG.CONTAINER_COLLAPSED_SELECTED] = GOODFLAG.UNCONTAINED_SELECTED,
}

-- collapses uncollapsed containers and restores the selection state for the container
-- and contained items
local function toggle_ctrl_clicked_containers(new_state, old_state, list_idx)
local toggled_item_count, in_container, is_collapsing = 0, false, false
local function toggle_ctrl_clicked_containers(new_state, old_state_fn, list_idx)
local toggled_item_count, in_target_container, is_collapsing = 0, false, false
for k, goodflag in ipairs(new_state) do
if in_container then
if goodflag <= GOODFLAG.UNCONTAINED_SELECTED
or goodflag >= GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED then
break
end
local old_goodflag = old_state_fn(k)
if in_target_container then
if not goodflag.contained then break end
toggled_item_count = toggled_item_count + 1
new_state[k] = old_state[k]
utils.assign(goodflag, old_goodflag)
goto continue
end

if goodflag == old_state[k] then goto continue end
local is_contained = goodflag == GOODFLAG.CONTAINED_UNSELECTED or goodflag == GOODFLAG.CONTAINED_SELECTED
if is_contained then goto continue end
if flags_match(goodflag, old_goodflag) or goodflag.contained then goto continue end
local is_container = df.item_binst:is_instance(trade.good[list_idx][k])
if not is_container then goto continue end

new_state[k] = CTRL_CLICK_STATE_MAP[old_state[k]]
in_container = true
is_collapsing = goodflag == GOODFLAG.UNCONTAINED_UNSELECTED or goodflag == GOODFLAG.UNCONTAINED_SELECTED
goodflag.selected = old_goodflag.selected
goodflag.container_collapsed = not old_goodflag.container_collapsed
in_target_container = true
is_collapsing = goodflag.container_collapsed

::continue::
end
Expand Down Expand Up @@ -696,27 +644,17 @@ end
local function collapseContainers(item_list, list_idx)
local num_items_collapsed = 0
for k, goodflag in ipairs(item_list) do
if goodflag == GOODFLAG.CONTAINED_UNSELECTED
or goodflag == GOODFLAG.CONTAINED_SELECTED then
goto continue
end
if goodflag.contained then goto continue end

local item = trade.good[list_idx][k]
local is_container = df.item_binst:is_instance(item)
if not is_container then goto continue end

local collapsed_this_container = false
if goodflag == GOODFLAG.UNCONTAINED_SELECTED then
item_list[k] = GOODFLAG.CONTAINER_COLLAPSED_SELECTED
collapsed_this_container = true
elseif goodflag == GOODFLAG.UNCONTAINED_UNSELECTED then
item_list[k] = GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED
collapsed_this_container = true
end

if collapsed_this_container then
if not goodflag.container_collapsed then
goodflag.container_collapsed = true
num_items_collapsed = num_items_collapsed + #dfhack.items.getContainedItems(item)
end

::continue::
end

Expand All @@ -736,8 +674,14 @@ local function collapseEverything()
end

local function copyGoodflagState()
trader_selected_state = copyall(trade.goodflag[0])
broker_selected_state = copyall(trade.goodflag[1])
-- utils.clone will return a lua table, with indices offset by 1
-- we'll use getSavedGoodflag to map the index back to the original value
trader_selected_state = utils.clone(trade.goodflag[0], true)
broker_selected_state = utils.clone(trade.goodflag[1], true)
end

local function getSavedGoodflag(saved_state, k)
return saved_state[k+1]
end

TradeOverlay = defclass(TradeOverlay, overlay.OverlayWidget)
Expand Down Expand Up @@ -798,12 +742,12 @@ end
function TradeOverlay:onRenderBody(dc)
if handle_shift_click_on_render then
handle_shift_click_on_render = false
select_shift_clicked_container_items(trade.goodflag[0], trader_selected_state, 0)
select_shift_clicked_container_items(trade.goodflag[1], broker_selected_state, 1)
select_shift_clicked_container_items(trade.goodflag[0], curry(getSavedGoodflag, trader_selected_state), 0)
select_shift_clicked_container_items(trade.goodflag[1], curry(getSavedGoodflag, broker_selected_state), 1)
elseif handle_ctrl_click_on_render then
handle_ctrl_click_on_render = false
toggle_ctrl_clicked_containers(trade.goodflag[0], trader_selected_state, 0)
toggle_ctrl_clicked_containers(trade.goodflag[1], broker_selected_state, 1)
toggle_ctrl_clicked_containers(trade.goodflag[0], curry(getSavedGoodflag, trader_selected_state), 0)
toggle_ctrl_clicked_containers(trade.goodflag[1], curry(getSavedGoodflag, broker_selected_state), 1)
end
end

Expand Down Expand Up @@ -915,12 +859,10 @@ function for_selected_item(list_idx, fn)
local in_selected_container = false
for item_idx, item in ipairs(trade.good[list_idx]) do
local goodflag = goodflags[item_idx]
if goodflag == GOODFLAG.UNCONTAINED_SELECTED or goodflag == GOODFLAG.CONTAINER_COLLAPSED_SELECTED then
in_selected_container = true
elseif goodflag == GOODFLAG.UNCONTAINED_UNSELECTED or goodflag == GOODFLAG.CONTAINER_COLLAPSED_UNSELECTED then
in_selected_container = false
if not goodflag.contained then
in_selected_container = goodflag.selected
end
if in_selected_container or TARGET_REVMAP[goodflag] then
if in_selected_container or goodflag.selected then
if fn(item_idx, item) then
return
end
Expand Down Expand Up @@ -954,10 +896,7 @@ end
function Ethics:deselect_transgressions()
local goodflags = trade.goodflag[1]
for _,choice in ipairs(self.choices) do
local goodflag = goodflags[choice.data.item_idx]
if TARGET_REVMAP[goodflag] then
goodflags[choice.data.item_idx] = TOGGLE_MAP[goodflag]
end
goodflags[choice.data.item_idx].selected = false
end
self:rescan()
end
Expand Down
2 changes: 0 additions & 2 deletions internal/confirm/specs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ end
local function trade_goods_all_selected(which)
local num_selected = 0
trade_internal.for_selected_item(which, function(idx)
print(idx)
num_selected = num_selected + 1
end)
return #mi.trade.goodflag[which] == num_selected
Expand Down Expand Up @@ -336,7 +335,6 @@ ConfirmSpec{
return uniform_has_changes()
end
if clicked_on_confirm_button(mouse_offset) then
print('confirm click detected')
clear_uniform_record()
else
ensure_uniform_record()
Expand Down