diff --git a/init.lua b/init.lua index 97cd63a..00aa93b 100644 --- a/init.lua +++ b/init.lua @@ -300,7 +300,7 @@ local function get_formspec(player) end table.insert(fs, "container[0,5.6]") - if data.recipes then + if data.recipes and #data.recipes > 0 then recipe_fs(fs, data) elseif data.prev_item then table.insert(fs, ("label[2,1;%s]"):format(esc(data.show_usages @@ -319,12 +319,12 @@ end local function execute_search(data) local filter = data.filter if filter == "" then - data.items = init_items + data.items = visible_items(data) return end data.items = {} - for _, item in ipairs(init_items) do + for _, item in ipairs(visible_items(data)) do local def = minetest.registered_items[item] local desc = def and minetest.get_translated_string(data.lang_code, def.description) @@ -343,7 +343,7 @@ local function on_receive_fields(player, fields) data.pagenum = 1 data.prev_item = nil data.recipes = nil - data.items = init_items + data.items = visible_items(data) return true elseif (fields.key_enter_field == "filter" or fields.search) @@ -398,9 +398,9 @@ local function on_receive_fields(player, fields) data.show_usages = nil end if data.show_usages then - data.recipes = usages_cache[item] + data.recipes = known_recipes(data, usages_cache[item]) else - data.recipes = recipes_cache[item] + data.recipes = known_recipes(data, recipes_cache[item]) end data.prev_item = item data.rnum = 1 @@ -415,7 +415,10 @@ minetest.register_on_joinplayer(function(player) player_data[name] = { filter = "", pagenum = 1, - items = init_items, + items = {}, + held_items = {}, + held_groups = {}, + known_items = {}, lang_code = info.lang_code } end) @@ -436,3 +439,124 @@ sfinv.register_page("mtg_craftguide:craftguide", { end end }) + +-- ~ Progressive-mode support ~ +-- [The vast majority of changes to mtg_craftguide are found here!] +function table_to_str(table) + local ret = "[" + for k,v in pairs(table) do + ret = ret .. "; " .. k .. " - " + if type(v) == "table" then + ret = ret .. table_to_str(v) + else + ret = ret .. v + end + end + return ret .. "]" +end + +-- Given a player’s data and a recipe, return whether or not that recipe +-- is “known” by the player. (That is, they have held all necessary inputs.) +function known_recipe(data, recipe) + for _,input in pairs(recipe.items) do + if (not data.held_items[input:gsub(" .*", "")]) and (not data.held_groups[input]) then + return false + end + end + return true +end + +-- From a table of recipes, return a sub-table of recipes that are “known.” +function known_recipes(data, recipes) + local ret = {} + if recipes then + for _,recipe in pairs(recipes) do + if known_recipe(data, recipe) then + table.insert(ret, recipe) + end + end + end + return ret +end + +-- Given a player’s data and an item-name, return a table of all items the +-- player should “know of” that can be crafted from that item. +function known_crafting_outputs(data, item_name) + local outputs = {} + local uses = usages_cache[item_name] + if uses then + for _,recipe in pairs(uses) do + if known_recipe(data, recipe) then + local out_name = recipe.output + out_name = out_name:gsub(" .*", "") + table.insert(outputs, out_name) + end + end + end + return outputs +end + +-- Returns a table of all items that are “known” to the player; either they’ve +-- been held by them, or they’re craftable using solely objects held previously. +function visible_items(data) + local ret = {} + for item,_ in pairs(data.held_items) do + table.insert(ret, item) + end + for item,_ in pairs(data.known_items) do + table.insert(ret, item) + end + return ret +end + +-- To be executed whenever a player gains/picks up an item. +-- If the item is new, it is added to the player’s held_items table, and then +-- possible outputs from it (and other known items) are added to known_items. +function update_known_items(player, item_name) + local name = player:get_player_name() + local data = player_data[name] + + if not data.held_items[item_name] then + -- Mark the new item as known. + data.held_items[item_name] = true + + -- Mark the new item’s groups as known. + local groups = minetest.registered_items[item_name].groups + for group,rating in pairs(groups) do + if rating > 0 then + data.held_groups["group:" .. group] = true + end + end + + -- Mark items craftable with this (and other) items as known. + for _,out_item_name in pairs(known_crafting_outputs(data, item_name)) do + data.known_items[out_item_name] = true + end + + -- Reset item-list. + data.items = visible_items(data) + + -- Notify the player. + minetest.chat_send_player(name, S("New recipes unlocked!")) + end +end + +minetest.register_on_item_pickup(function(itemstack, picker) + update_known_items(picker, itemstack:get_name()) +end) + +minetest.register_on_player_inventory_action(function(player, action, inv, inv_info) + if action == "put" then + update_known_items(player, inv_info.stack:get_name()) + end +end) + +minetest.register_on_dignode(function(pos, oldnode, digger) + update_known_items(digger, oldnode.name) +end) + +minetest.register_on_craft(function(itemstack, player) + update_known_items(player, itemstack:get_name()) +end) + +