From c1b1bef263f8f8889e1303b0678f17ec263d942b Mon Sep 17 00:00:00 2001 From: Jean-Patrick Guerrero Date: Wed, 23 Jan 2019 02:15:26 +0100 Subject: [PATCH] Progressive mode: Remember discovered recipes --- README.md | 10 +++- init.lua | 173 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 130 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 4f41e06..4816c02 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@ This crafting guide is a blue book named *"Crafting Guide"* or a wooden sign. This crafting guide features a **progressive mode**. -The progressive mode is a Terraria-like system that only shows recipes you can craft from items in inventory. -The progressive mode can be enabled with `craftguide_progressive_mode = true` in `minetest.conf`. +The progressive mode is a Terraria-like system that only shows recipes you can craft +from items you ever had in your inventory. To enable it: `craftguide_progressive_mode = true` in `minetest.conf`. -`craftguide` is also integrated in `sfinv` (Minetest Game inventory) when you enable it with +`craftguide` is also integrated in `sfinv` (Minetest Game inventory). To enable it: `craftguide_sfinv_only = true` in `minetest.conf`. Use the command `/craft` to show the recipe(s) of the pointed node. @@ -49,6 +49,10 @@ craftguide.register_craft({ This function adds a recipe filter when progressive mode is enabled. The default `craftguide` filter will still be used. +The function should return the recipes to be displayed, given the available +recipes and an `ObjectRef` to the user. Each recipe is a table of the form +returned by `minetest.get_craft_recipe`. + Example function to hide recipes for items from a mod called "secretstuff": ```lua diff --git a/init.lua b/init.lua index d7fa1ab..70116cc 100644 --- a/init.lua +++ b/init.lua @@ -16,6 +16,7 @@ local sfinv_only = mt.settings:get_bool("craftguide_sfinv_only") local reg_items = mt.registered_items local get_result = mt.get_craft_result local show_formspec = mt.show_formspec +local serialize, deserialize = mt.serialize, mt.deserialize -- Intllib local S = dofile(mt.get_modpath("craftguide") .. "/intllib.lua") @@ -47,6 +48,28 @@ local group_stereotypes = { mesecon_conductor_craftable = "mesecons:wire_00000000_off", } +local function table_merge(t, t2) + for i = 1, #t2 do + t[#t + 1] = t2[i] + end + + return t +end + +local function table_clean(t) + local hash, ct = {}, {} + for i = 1, #t do + local v = t[i] + if not hash[v] then + ct[#ct + 1] = v + hash[v] = true + end + end + + sort(ct) + return ct +end + local function __func() return debug.getinfo(2, "n").name end @@ -328,8 +351,8 @@ local function get_recipe_fs(data, iY) return concat(fs) end -local function make_formspec(player_name) - local data = player_data[player_name] +local function make_formspec(name) + local data = player_data[name] local iY = sfinv_only and 4 or data.iX - 5 local ipp = data.iX * iY @@ -376,8 +399,8 @@ local function make_formspec(player_name) local first_item = (data.pagenum - 1) * ipp for i = first_item, first_item + ipp - 1 do - local name = data.items[i + 1] - if not name then + local item = data.items[i + 1] + if not item then break end @@ -389,8 +412,8 @@ local function make_formspec(player_name) Y, 1.1, 1.1, - name, - name) + item, + item) end if data.recipes and #data.recipes > 0 then @@ -400,13 +423,13 @@ local function make_formspec(player_name) return concat(fs) end -local show_fs = function(player, player_name) +local show_fs = function(player, name) if sfinv_only then sfinv.set_player_inventory_formspec(player) else - local data = player_data[player_name] - data.formspec = make_formspec(player_name) - show_formspec(player_name, "craftguide", data.formspec) + local data = player_data[name] + data.formspec = make_formspec(name) + show_formspec(name, "craftguide", data.formspec) end end @@ -417,7 +440,7 @@ local function filter_items(data) return end - local items_list = progressive_mode and data.progressive_items or init_items + local items_list = progressive_mode and data.p_items or init_items local filtered_list, c = {}, 0 for i = 1, #items_list do @@ -478,17 +501,14 @@ local function get_inv_items(player) local inv = player:get_inventory() local stacks = inv:get_list("main") local craftlist = inv:get_list("craft") - - for i = 1, #craftlist do - stacks[#stacks + 1] = craftlist[i] - end + stacks = table_merge(stacks, craftlist) local inv_items = {} for i = 1, #stacks do local stack = stacks[i] if not stack:is_empty() then local name = stack:get_name() - if not inv_items[name] and reg_items[name] then + if reg_items[name] then inv_items[#inv_items + 1] = name end end @@ -498,16 +518,17 @@ local function get_inv_items(player) end local function item_in_inv(item, inv_items) + local inv_items_size = #inv_items if item:sub(1,6) == "group:" then local groups = extract_groups(item) - for i = 1, #inv_items do + for i = 1, inv_items_size do local item_groups = reg_items[inv_items[i]].groups if item_has_groups(item_groups, groups) then return true end end else - for i = 1, #inv_items do + for i = 1, inv_items_size do if inv_items[i] == item then return true end @@ -516,15 +537,25 @@ local function item_in_inv(item, inv_items) end local function progressive_default_filter(recipes, player) + local name = player:get_player_name() + local data = player_data[name] local inv_items = get_inv_items(player) + + if data.inv_items then + inv_items = table_merge(data.inv_items, inv_items) + end + if #inv_items == 0 then return {} end + data.inv_items = inv_items + local filtered = {} for i = 1, #recipes do local recipe = recipes[i] local recipe_in_inv = true + for _, item in pairs(recipe.items) do if not item_in_inv(item, inv_items) then recipe_in_inv = false @@ -571,8 +602,10 @@ local function apply_progressive_filters(recipes, player) return recipes end -local function get_progressive_items(player) - local items = {} +local function get_progressive_items(player, name) + local data = player_data[name] + local items = data and data.p_items or {} + for i = 1, #init_items do local item = init_items[i] local recipes = recipes_cache[item] @@ -585,18 +618,35 @@ local function get_progressive_items(player) end end + items = table_clean(items) return items end local function init_data(player, name) - local p_items = progressive_mode and get_progressive_items(player) or nil player_data[name] = { filter = "", pagenum = 1, iX = sfinv_only and 8 or DEFAULT_SIZE, - items = p_items or init_items, - progressive_items = p_items, } + + local p_items + if progressive_mode then + local meta = player:get_meta() + player_data[name].inv_items = deserialize(meta:get_string("inv_items")) + p_items = get_progressive_items(player, name) + + if p_items then + local old_items = deserialize(meta:get_string("p_items")) + if old_items then + p_items = table_merge(p_items, old_items) + p_items = table_clean(p_items) + end + end + + player_data[name].p_items = p_items + end + + player_data[name].items = p_items or init_items end local function reset_data(data) @@ -605,7 +655,7 @@ local function reset_data(data) data.query_item = nil data.show_usages = nil data.recipes = nil - data.items = progressive_mode and data.progressive_items or init_items + data.items = progressive_mode and data.p_items or init_items end local function get_init_items() @@ -624,15 +674,13 @@ local function get_init_items() sort(init_items) end -mt.register_on_mods_loaded(get_init_items) - local function on_receive_fields(player, fields) - local player_name = player:get_player_name() - local data = player_data[player_name] + local name = player:get_player_name() + local data = player_data[name] if fields.clear then reset_data(data) - show_fs(player, player_name) + show_fs(player, name) elseif fields.alternate then if #data.recipes == 1 then @@ -641,7 +689,7 @@ local function on_receive_fields(player, fields) local num_next = data.rnum + 1 data.rnum = data.recipes[num_next] and num_next or 1 - show_fs(player, player_name) + show_fs(player, name) elseif (fields.key_enter_field == "filter" or fields.search) and fields.filter ~= "" then @@ -653,7 +701,7 @@ local function on_receive_fields(player, fields) data.filter = fltr data.pagenum = 1 filter_items(data) - show_fs(player, player_name) + show_fs(player, name) elseif fields.prev or fields.next then if data.pagemax == 1 then @@ -667,13 +715,13 @@ local function on_receive_fields(player, fields) data.pagenum = data.pagemax end - show_fs(player, player_name) + show_fs(player, name) elseif (fields.size_inc and data.iX < MAX_LIMIT) or (fields.size_dec and data.iX > MIN_LIMIT) then data.pagenum = 1 data.iX = data.iX + (fields.size_inc and 1 or -1) - show_fs(player, player_name) + show_fs(player, name) else local item @@ -728,7 +776,7 @@ local function on_receive_fields(player, fields) data.recipes = recipes data.rnum = 1 - show_fs(player, player_name) + show_fs(player, name) end end @@ -737,18 +785,20 @@ if sfinv_only then title = "Craft Guide", get = function(self, player, context) - local formspec = make_formspec(player:get_player_name()) + local name = player:get_player_name() + local formspec = make_formspec(name) + return sfinv.make_formspec(player, context, formspec) end, on_enter = function(self, player, context) - local player_name = player:get_player_name() - local data = player_data[player_name] + local name = player:get_player_name() + local data = player_data[name] if not data then - init_data(player, player_name) + init_data(player, name) elseif progressive_mode then - data.progressive_items = get_progressive_items(player) + data.p_items = get_progressive_items(player, name) filter_items(data) end end, @@ -765,20 +815,20 @@ else end) local function on_use(user) - local player_name = user:get_player_name() - local data = player_data[player_name] + local name = user:get_player_name() + local data = player_data[name] if not data then - init_data(user, player_name) - data = player_data[player_name] - data.formspec = make_formspec(player_name) + init_data(user, name) + data = player_data[name] + data.formspec = make_formspec(name) elseif progressive_mode then - data.progressive_items = get_progressive_items(user) + data.p_items = get_progressive_items(user, name) filter_items(data) - data.formspec = make_formspec(player_name) + data.formspec = make_formspec(name) end - show_formspec(player_name, "craftguide", data.formspec) + show_formspec(name, "craftguide", data.formspec) end mt.register_craftitem("craftguide:book", { @@ -911,13 +961,36 @@ if not progressive_mode then }) end +mt.register_on_mods_loaded(get_init_items) + +local function save_meta(player, data) + local meta = player:get_meta() + meta:set_string("p_items", serialize(data.p_items)) + meta:set_string("inv_items", serialize(data.inv_items)) +end + mt.register_on_leaveplayer(function(player) - if player then - local name = player:get_player_name() - player_data[name] = nil + local name = player:get_player_name() + if progressive_mode then + save_meta(player, player_data[name]) end + + player_data[name] = nil end) +if progressive_mode then + mt.register_on_shutdown(function() + local players = mt.get_connected_players() + for i = 1, #players do + local player = players[i] + if player then + local name = player:get_player_name() + save_meta(player, player_data[name]) + end + end + end) +end + --[[ Custom recipes (>3x3) test code mt.register_craftitem(":secretstuff:custom_recipe_test", {