Progressive mode: Remember discovered recipes

This commit is contained in:
Jean-Patrick Guerrero 2019-01-23 02:15:26 +01:00
parent e6268a395f
commit c1b1bef263
2 changed files with 130 additions and 53 deletions

@ -6,10 +6,10 @@
This crafting guide is a blue book named *"Crafting Guide"* or a wooden sign. This crafting guide is a blue book named *"Crafting Guide"* or a wooden sign.
This crafting guide features a **progressive mode**. 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 is a Terraria-like system that only shows recipes you can craft
The progressive mode can be enabled with `craftguide_progressive_mode = true` in `minetest.conf`. 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`. `craftguide_sfinv_only = true` in `minetest.conf`.
Use the command `/craft` to show the recipe(s) of the pointed node. 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. This function adds a recipe filter when progressive mode is enabled.
The default `craftguide` filter will still be used. 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": Example function to hide recipes for items from a mod called "secretstuff":
```lua ```lua

171
init.lua

@ -16,6 +16,7 @@ local sfinv_only = mt.settings:get_bool("craftguide_sfinv_only")
local reg_items = mt.registered_items local reg_items = mt.registered_items
local get_result = mt.get_craft_result local get_result = mt.get_craft_result
local show_formspec = mt.show_formspec local show_formspec = mt.show_formspec
local serialize, deserialize = mt.serialize, mt.deserialize
-- Intllib -- Intllib
local S = dofile(mt.get_modpath("craftguide") .. "/intllib.lua") local S = dofile(mt.get_modpath("craftguide") .. "/intllib.lua")
@ -47,6 +48,28 @@ local group_stereotypes = {
mesecon_conductor_craftable = "mesecons:wire_00000000_off", 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() local function __func()
return debug.getinfo(2, "n").name return debug.getinfo(2, "n").name
end end
@ -328,8 +351,8 @@ local function get_recipe_fs(data, iY)
return concat(fs) return concat(fs)
end end
local function make_formspec(player_name) local function make_formspec(name)
local data = player_data[player_name] local data = player_data[name]
local iY = sfinv_only and 4 or data.iX - 5 local iY = sfinv_only and 4 or data.iX - 5
local ipp = data.iX * iY local ipp = data.iX * iY
@ -376,8 +399,8 @@ local function make_formspec(player_name)
local first_item = (data.pagenum - 1) * ipp local first_item = (data.pagenum - 1) * ipp
for i = first_item, first_item + ipp - 1 do for i = first_item, first_item + ipp - 1 do
local name = data.items[i + 1] local item = data.items[i + 1]
if not name then if not item then
break break
end end
@ -389,8 +412,8 @@ local function make_formspec(player_name)
Y, Y,
1.1, 1.1,
1.1, 1.1,
name, item,
name) item)
end end
if data.recipes and #data.recipes > 0 then if data.recipes and #data.recipes > 0 then
@ -400,13 +423,13 @@ local function make_formspec(player_name)
return concat(fs) return concat(fs)
end end
local show_fs = function(player, player_name) local show_fs = function(player, name)
if sfinv_only then if sfinv_only then
sfinv.set_player_inventory_formspec(player) sfinv.set_player_inventory_formspec(player)
else else
local data = player_data[player_name] local data = player_data[name]
data.formspec = make_formspec(player_name) data.formspec = make_formspec(name)
show_formspec(player_name, "craftguide", data.formspec) show_formspec(name, "craftguide", data.formspec)
end end
end end
@ -417,7 +440,7 @@ local function filter_items(data)
return return
end 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 local filtered_list, c = {}, 0
for i = 1, #items_list do for i = 1, #items_list do
@ -478,17 +501,14 @@ local function get_inv_items(player)
local inv = player:get_inventory() local inv = player:get_inventory()
local stacks = inv:get_list("main") local stacks = inv:get_list("main")
local craftlist = inv:get_list("craft") local craftlist = inv:get_list("craft")
stacks = table_merge(stacks, craftlist)
for i = 1, #craftlist do
stacks[#stacks + 1] = craftlist[i]
end
local inv_items = {} local inv_items = {}
for i = 1, #stacks do for i = 1, #stacks do
local stack = stacks[i] local stack = stacks[i]
if not stack:is_empty() then if not stack:is_empty() then
local name = stack:get_name() 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 inv_items[#inv_items + 1] = name
end end
end end
@ -498,16 +518,17 @@ local function get_inv_items(player)
end end
local function item_in_inv(item, inv_items) local function item_in_inv(item, inv_items)
local inv_items_size = #inv_items
if item:sub(1,6) == "group:" then if item:sub(1,6) == "group:" then
local groups = extract_groups(item) 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 local item_groups = reg_items[inv_items[i]].groups
if item_has_groups(item_groups, groups) then if item_has_groups(item_groups, groups) then
return true return true
end end
end end
else else
for i = 1, #inv_items do for i = 1, inv_items_size do
if inv_items[i] == item then if inv_items[i] == item then
return true return true
end end
@ -516,15 +537,25 @@ local function item_in_inv(item, inv_items)
end end
local function progressive_default_filter(recipes, player) 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) 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 if #inv_items == 0 then
return {} return {}
end end
data.inv_items = inv_items
local filtered = {} local filtered = {}
for i = 1, #recipes do for i = 1, #recipes do
local recipe = recipes[i] local recipe = recipes[i]
local recipe_in_inv = true local recipe_in_inv = true
for _, item in pairs(recipe.items) do for _, item in pairs(recipe.items) do
if not item_in_inv(item, inv_items) then if not item_in_inv(item, inv_items) then
recipe_in_inv = false recipe_in_inv = false
@ -571,8 +602,10 @@ local function apply_progressive_filters(recipes, player)
return recipes return recipes
end end
local function get_progressive_items(player) local function get_progressive_items(player, name)
local items = {} local data = player_data[name]
local items = data and data.p_items or {}
for i = 1, #init_items do for i = 1, #init_items do
local item = init_items[i] local item = init_items[i]
local recipes = recipes_cache[item] local recipes = recipes_cache[item]
@ -585,18 +618,35 @@ local function get_progressive_items(player)
end end
end end
items = table_clean(items)
return items return items
end end
local function init_data(player, name) local function init_data(player, name)
local p_items = progressive_mode and get_progressive_items(player) or nil
player_data[name] = { player_data[name] = {
filter = "", filter = "",
pagenum = 1, pagenum = 1,
iX = sfinv_only and 8 or DEFAULT_SIZE, 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 end
local function reset_data(data) local function reset_data(data)
@ -605,7 +655,7 @@ local function reset_data(data)
data.query_item = nil data.query_item = nil
data.show_usages = nil data.show_usages = nil
data.recipes = 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 end
local function get_init_items() local function get_init_items()
@ -624,15 +674,13 @@ local function get_init_items()
sort(init_items) sort(init_items)
end end
mt.register_on_mods_loaded(get_init_items)
local function on_receive_fields(player, fields) local function on_receive_fields(player, fields)
local player_name = player:get_player_name() local name = player:get_player_name()
local data = player_data[player_name] local data = player_data[name]
if fields.clear then if fields.clear then
reset_data(data) reset_data(data)
show_fs(player, player_name) show_fs(player, name)
elseif fields.alternate then elseif fields.alternate then
if #data.recipes == 1 then if #data.recipes == 1 then
@ -641,7 +689,7 @@ local function on_receive_fields(player, fields)
local num_next = data.rnum + 1 local num_next = data.rnum + 1
data.rnum = data.recipes[num_next] and num_next or 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 elseif (fields.key_enter_field == "filter" or fields.search) and
fields.filter ~= "" then fields.filter ~= "" then
@ -653,7 +701,7 @@ local function on_receive_fields(player, fields)
data.filter = fltr data.filter = fltr
data.pagenum = 1 data.pagenum = 1
filter_items(data) filter_items(data)
show_fs(player, player_name) show_fs(player, name)
elseif fields.prev or fields.next then elseif fields.prev or fields.next then
if data.pagemax == 1 then if data.pagemax == 1 then
@ -667,13 +715,13 @@ local function on_receive_fields(player, fields)
data.pagenum = data.pagemax data.pagenum = data.pagemax
end end
show_fs(player, player_name) show_fs(player, name)
elseif (fields.size_inc and data.iX < MAX_LIMIT) or elseif (fields.size_inc and data.iX < MAX_LIMIT) or
(fields.size_dec and data.iX > MIN_LIMIT) then (fields.size_dec and data.iX > MIN_LIMIT) then
data.pagenum = 1 data.pagenum = 1
data.iX = data.iX + (fields.size_inc and 1 or -1) data.iX = data.iX + (fields.size_inc and 1 or -1)
show_fs(player, player_name) show_fs(player, name)
else else
local item local item
@ -728,7 +776,7 @@ local function on_receive_fields(player, fields)
data.recipes = recipes data.recipes = recipes
data.rnum = 1 data.rnum = 1
show_fs(player, player_name) show_fs(player, name)
end end
end end
@ -737,18 +785,20 @@ if sfinv_only then
title = "Craft Guide", title = "Craft Guide",
get = function(self, player, context) 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) return sfinv.make_formspec(player, context, formspec)
end, end,
on_enter = function(self, player, context) on_enter = function(self, player, context)
local player_name = player:get_player_name() local name = player:get_player_name()
local data = player_data[player_name] local data = player_data[name]
if not data then if not data then
init_data(player, player_name) init_data(player, name)
elseif progressive_mode then elseif progressive_mode then
data.progressive_items = get_progressive_items(player) data.p_items = get_progressive_items(player, name)
filter_items(data) filter_items(data)
end end
end, end,
@ -765,20 +815,20 @@ else
end) end)
local function on_use(user) local function on_use(user)
local player_name = user:get_player_name() local name = user:get_player_name()
local data = player_data[player_name] local data = player_data[name]
if not data then if not data then
init_data(user, player_name) init_data(user, name)
data = player_data[player_name] data = player_data[name]
data.formspec = make_formspec(player_name) data.formspec = make_formspec(name)
elseif progressive_mode then elseif progressive_mode then
data.progressive_items = get_progressive_items(user) data.p_items = get_progressive_items(user, name)
filter_items(data) filter_items(data)
data.formspec = make_formspec(player_name) data.formspec = make_formspec(name)
end end
show_formspec(player_name, "craftguide", data.formspec) show_formspec(name, "craftguide", data.formspec)
end end
mt.register_craftitem("craftguide:book", { mt.register_craftitem("craftguide:book", {
@ -911,12 +961,35 @@ if not progressive_mode then
}) })
end 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) mt.register_on_leaveplayer(function(player)
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 if player then
local name = player:get_player_name() local name = player:get_player_name()
player_data[name] = nil save_meta(player, player_data[name])
end end
end) end
end)
end
--[[ Custom recipes (>3x3) test code --[[ Custom recipes (>3x3) test code