From bda9f2598f28fab39ac0e198504c531a35e99341 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 4 Jan 2023 11:08:20 +0100 Subject: [PATCH] Improve group matching for 'copy to craft' --- api.lua | 40 ++------------------------------- group.lua | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ match_craft.lua | 19 +++++----------- 3 files changed, 68 insertions(+), 51 deletions(-) diff --git a/api.lua b/api.lua index e4b67fa..cfa15ae 100644 --- a/api.lua +++ b/api.lua @@ -146,46 +146,10 @@ minetest.after(0.01, function() end -- Step 1: group-indexed lookup table for items - local spec_matcher = {} - for _, name in ipairs(ui.items_list) do - -- we only need to care about groups, exact items are handled separately - for group, value in pairs(minetest.registered_items[name].groups) do - if value and value ~= 0 then - if not spec_matcher[group] then - spec_matcher[group] = {} - end - spec_matcher[group][name] = true - end - end - end + unified_inventory.init_matching_cache() -- Step 2: Find all matching items for the given spec (groups) - local function get_matching_spec_items(specname) - if specname:sub(1,6) ~= "group:" then - return { [specname] = true } - end - - local accepted = {} - for i, group in ipairs(specname:sub(7):split(",")) do - if i == 1 then - -- First step: Copy all possible item names in this group - for name, _ in pairs(spec_matcher[group] or {}) do - accepted[name] = true - end - else - -- Perform filtering - if spec_matcher[group] then - for name, _ in pairs(accepted) do - accepted[name] = spec_matcher[group][name] - end - else - -- No matching items - return {} - end - end - end - return accepted - end + local get_matching_spec_items = unified_inventory.get_matching_items for _, recipes in pairs(ui.crafts_for.recipe) do -- List of crafts that return this item string (variable "_") diff --git a/group.lua b/group.lua index 2bc8e2f..1483b3c 100644 --- a/group.lua +++ b/group.lua @@ -1,4 +1,5 @@ local S = minetest.get_translator("unified_inventory") +local ui = unified_inventory function unified_inventory.extract_groupnames(groupname) local specname = ItemStack(groupname):get_name() @@ -26,6 +27,7 @@ end -- It may be a comma-separated list of group names. This is really a -- "group:..." ingredient specification, minus the "group:" prefix. +-- TODO Replace this with the more efficient spec matcher (below) local function compute_group_item(group_name_list) local group_names = group_name_list:split(",") local candidate_items = {} @@ -84,3 +86,61 @@ function unified_inventory.get_group_item(group_name) return group_item_cache[group_name] end + +--[[ +This is for filtering known items by groups +e.g. find all items that match "group:flower,yellow" (flower AND yellow groups) +]] +local spec_matcher = {} +function unified_inventory.init_matching_cache() + for _, name in ipairs(ui.items_list) do + -- we only need to care about groups, exact items are handled separately + for group, value in pairs(minetest.registered_items[name].groups) do + if value and value ~= 0 then + if not spec_matcher[group] then + spec_matcher[group] = {} + end + spec_matcher[group][name] = true + end + end + end +end + +--[[ +Retrieves all matching items + +Arguments: + specname (string): Item name or group(s) to filter + +Output: + { + matchingitem1 = true, + ... + } +]] +function unified_inventory.get_matching_items(specname) + if specname:sub(1,6) ~= "group:" then + return { [specname] = true } + end + + local accepted = {} + for i, group in ipairs(specname:sub(7):split(",")) do + if i == 1 then + -- First step: Copy all possible item names in this group + for name, _ in pairs(spec_matcher[group] or {}) do + accepted[name] = true + end + else + -- Perform filtering + if spec_matcher[group] then + for name, _ in pairs(accepted) do + accepted[name] = spec_matcher[group][name] + end + else + -- No matching items + return {} + end + end + end + return accepted +end diff --git a/match_craft.lua b/match_craft.lua index 2dd40b0..b2d18ec 100644 --- a/match_craft.lua +++ b/match_craft.lua @@ -126,25 +126,18 @@ Example output: } --]] function unified_inventory.find_usable_items(inv_items, craft_items) - local get_group = minetest.get_item_group local result = {} for craft_item in pairs(craft_items) do - local group = craft_item:match("^group:(.+)") - local found = {} + -- may specify group:type1,type2 + local items = unified_inventory.get_matching_items(craft_item) - if group ~= nil then - for inv_item in pairs(inv_items) do - if get_group(inv_item, group) > 0 then - found[inv_item] = true - end - end - else - if inv_items[craft_item] ~= nil then - found[craft_item] = true + local found = {} + for itemname, _ in pairs(items) do + if inv_items[itemname] then + found[itemname] = true end end - result[craft_item] = found end