diff --git a/main.lua b/main.lua index 90ef33b..a441e07 100644 --- a/main.lua +++ b/main.lua @@ -1,5 +1,4 @@ local ui = require("ui") -local term_stack = require("term-stack") require("dbg")("logs.txt") diff --git a/views/main.lua b/views/main.lua index d3da914..da287a6 100644 --- a/views/main.lua +++ b/views/main.lua @@ -19,6 +19,10 @@ local function copy_table(t) return t_copy end +local function is_integer(num) + return num % 1 == 0 +end + local function list_items(inventories) local item_registry = {} for _, name in ipairs(inventories) do @@ -96,6 +100,39 @@ local function is_bundle_name(name) return name:find("#") == 1 end +local function do_bundles_contain_cycle(bundles) + local visited = {} + local stack = {} + + local function is_cyclic_util(bundle_name) + if not visited[bundle_name] then + visited[bundle_name] = true + stack[bundle_name] = true + + for dep_name in pairs(bundles[bundle_name]) do + if is_bundle_name(dep_name) then + dep_name = dep_name:sub(2) + if not visited[dep_name] and is_cyclic_util(dep_name) then + return true + elseif stack[dep_name] then + return true + end + end + end + end + stack[bundle_name] = false + return false + end + + for bundle_name in pairs(bundles) do + if not visited[bundle_name] and is_cyclic_util(bundle_name) then + return true + end + end + + return false +end + -- TODO: improve this function local function resolve_bundle_items(bundles, bundle_name, seen_bundles) seen_bundles = seen_bundles or {} @@ -218,6 +255,8 @@ function main_view:prepare(inventories, bundles, result_inventory) end + + function main_view:list_filtered_names(items, bundles, name_filter) name_filter = name_filter:lower() @@ -293,17 +332,21 @@ function main_view:process_movement_keys(store, area) local bundle = self.bundle_details[bundle_name] if self.right_pressed and lstore.bundles[bundle_name] > 0 then - for item, count in pairs(bundle) do - lstore.items[item] = lstore.items[item] - count - end local transferred = math.min(lstore.bundles[bundle_name], 1) - rstore.bundles[bundle_name] = (rstore.bundles[bundle_name] or 0) + transferred - - elseif self.left_pressed and rstore.bundles[bundle_name] > 0 then for item, count in pairs(bundle) do - lstore.items[item] = lstore.items[item] + count + lstore.items[item] = lstore.items[item] - math.floor(count * transferred) + end + rstore.bundles[bundle_name] = (rstore.bundles[bundle_name] or 0) + transferred + elseif self.left_pressed and rstore.bundles[bundle_name] > 0 then + local transferred + if is_integer(rstore.bundles[bundle_name]) then + transferred = math.min(rstore.bundles[bundle_name], 1) + else + transferred = rstore.bundles[bundle_name] % 1 + end + for item, count in pairs(bundle) do + lstore.items[item] = lstore.items[item] + math.floor(count * transferred) end - local transferred = math.min(rstore.bundles[bundle_name], 1) rstore.bundles[bundle_name] = rstore.bundles[bundle_name] - transferred end else @@ -337,9 +380,6 @@ function main_view:display_list(store, area, selected_idx) local function get_number_width(num) return math.floor(math.log(num, 10) + 1) end - local function is_integer(num) - return num % 1 == 0 - end local count_collumn_width do -- Figure out how wide does the count column need to be @@ -427,9 +467,6 @@ function main_view:display_list_with_search(store, area, active) term_stack.pop_cursor() end - - - function main_view:move_item(item_name, amount, destination) local item_collection = self.item_registry[item_name] @@ -488,8 +525,29 @@ function main_view:deposit_items() self:refresh_items() end -function main_view:save_bundle(name, items) - self.bundles[name] = items +function main_view:save_bundle(name, items, bundles) + local bundle = {} + for item, count in pairs(items) do + if count > 0 then + bundle[item] = count + end + end + for bundle_name, count in pairs(bundles) do + if count > 0 then + bundle[bundle_name] = count + end + end + self.bundles[name] = bundle + if do_bundles_contain_cycle(self.bundles) then + self.bundles[name] = nil + return false + end + + populate_bundle_details(self.bundle_details, self.bundles) + self.left_store.bundles = derive_available_bundles(self.left_store.items, self.bundle_details) + self:refresh_filtered_names(self.left_store, self.left_area) + + return true end function main_view:display_bundle_popup(area) @@ -518,7 +576,10 @@ function main_view:display_bundle_popup(area) self.bundle_popup = false elseif self.submit_pressed and #self.bundle_name > 0 then self.bundle_popup = false - self:save_bundle(self.bundle_name, self.right_store.items) + local success = self:save_bundle(self.bundle_name, self.right_store.items, self.right_store.bundles) + if success then + self.bundle_name = "" + end end end @@ -534,8 +595,9 @@ function main_view:request_items() for bundle_name, bundle_count in pairs(rstore.bundles) do bundle_count = math.max(bundle_count, 1) for item, count in pairs(self.bundle_details[bundle_name]) do - local transferred = self:move_item(item, count*bundle_count, self.result_inventory) - lstore.items[item] = lstore.items[item] + (count*bundle_count - transferred) + local amount = math.floor(count*bundle_count) + local transferred = self:move_item(item, amount, self.result_inventory) + lstore.items[item] = lstore.items[item] + (amount - transferred) end end @@ -548,9 +610,10 @@ function main_view:request_items() local selected_option = lstore.filtered_names[lstore.selected_idx] if is_bundle_name(selected_option) then local bundle_name = selected_option + local bundle_count = math.min(lstore.bundles[bundle_name], 1) for item, count in pairs(self.bundle_details[bundle_name]) do local transferred = self:move_item(item, count, self.result_inventory) - lstore.items[item] = lstore.items[item] - transferred + lstore.items[item] = lstore.items[item] - math.floor(transferred * bundle_count) end else local item = selected_option @@ -626,7 +689,7 @@ function main_view:on_key(key) end if not self.bundle_popup then - if key == keys.n and self:has_selected_items() then + if self.ctrl_down and key == keys.n and self:has_selected_items() then self.bundle_popup = true elseif key == keys.tab then