Progressive: refactor, fix some bugs, add API function

This commit is contained in:
pauloue 2019-01-13 01:36:38 +01:00 committed by Jean-Patrick Guerrero
parent 1def071029
commit 50317cc20e
2 changed files with 182 additions and 138 deletions

@ -1,7 +1,7 @@
## ![Preview1](http://i.imgur.com/fIPNYkb.png) Crafting Guide ## # ![Preview1](http://i.imgur.com/fIPNYkb.png) Crafting Guide
#### `craftguide` is the most comprehensive crafting guide on Minetest. #### #### `craftguide` is the most comprehensive crafting guide on Minetest.
#### Consult the [Minetest Wiki](http://wiki.minetest.net/Crafting_guide) for more details. #### #### Consult the [Minetest Wiki](http://wiki.minetest.net/Crafting_guide) for more details.
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.
@ -14,10 +14,16 @@ The progressive mode can be enabled with `craftguide_progressive_mode = true` in
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.
![Preview2](https://i.imgur.com/bToFH38.png)
--- ---
`craftguide` has an API to register **custom recipes**. Demos: ## API
#### Registering a custom crafting type ####
### Custom recipes
#### Registering a custom crafting type
```Lua ```Lua
craftguide.register_craft_type("digging", { craftguide.register_craft_type("digging", {
description = "Digging", description = "Digging",
@ -25,7 +31,8 @@ craftguide.register_craft_type("digging", {
}) })
``` ```
#### Registering a custom crafting recipe #### #### Registering a custom crafting recipe
```Lua ```Lua
craftguide.register_craft({ craftguide.register_craft({
type = "digging", type = "digging",
@ -35,4 +42,27 @@ craftguide.register_craft({
}) })
``` ```
![Preview2](https://i.imgur.com/bToFH38.png) ### Progressive mode
#### `craftguide.progressive_filter_recipes(recipes, player)`
This function is used to filter recipes when progressive mode is enabled. It can
be overridden to change the recipes that are normally displayed.
The function should return the recipes to be displayed, given the available
recipes and an `ObjectRef` to the craft guide 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
function craftguide.progressive_filter_recipes(recipes, player)
local filtered = {}
for _, recipe in ipairs(recipes) do
if recipe.output:sub(1, 12) ~= "secretstuff:" then
filtered[#filtered + 1] = recipe
end
end
return filtered
end
```

270
init.lua

@ -20,7 +20,7 @@ local S = dofile(mt.get_modpath("craftguide") .. "/intllib.lua")
-- Lua 5.3 removed `table.maxn`, use this alternative in case of breakage: -- Lua 5.3 removed `table.maxn`, use this alternative in case of breakage:
-- https://github.com/kilbith/xdecor/blob/master/handlers/helpers.lua#L1 -- https://github.com/kilbith/xdecor/blob/master/handlers/helpers.lua#L1
local remove, maxn, sort, concat = table.remove, table.maxn, table.sort, table.concat local maxn, sort, concat = table.maxn, table.sort, table.concat
local vector_add, vector_mul = vector.add, vector.multiply local vector_add, vector_mul = vector.add, vector.multiply
local min, max, floor, ceil = math.min, math.max, math.floor, math.ceil local min, max, floor, ceil = math.min, math.max, math.floor, math.ceil
local fmt = string.format local fmt = string.format
@ -120,39 +120,6 @@ local function get_fueltime(item)
return get_result({method = "fuel", width = 1, items = {item}}).time return get_result({method = "fuel", width = 1, items = {item}}).time
end end
local function reset_data(data)
data.show_usage = nil
data.filter = ""
data.input = nil
data.pagenum = 1
data.rnum = 1
data.items = progressive_mode and data.init_filter_items or init_items
end
local function in_table(T)
for i = 1, #T do
if T[i] then
return true
end
end
end
local function group_to_items(group)
local items_with_group, c = {}, 0
for name, def in pairs(reg_items) do
if def.groups[group:sub(7)] then
c = c + 1
items_with_group[c] = name
end
end
return items_with_group
end
local function item_in_inv(inv, item)
return inv:contains_item("main", item)
end
local function extract_groups(str) local function extract_groups(str)
return str:sub(7):split(",") return str:sub(7):split(",")
end end
@ -382,10 +349,6 @@ local function get_formspec(player_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
if not data.items then
data.items = init_items
end
data.pagemax = max(1, ceil(#data.items / ipp)) data.pagemax = max(1, ceil(#data.items / ipp))
local fs = {} local fs = {}
@ -459,12 +422,11 @@ local function get_formspec(player_name)
end end
fs = concat(fs) fs = concat(fs)
data.formspec = fs
if sfinv_only then if sfinv_only then
return fs return fs
else else
show_formspec(player_name, "craftguide", fs) data.formspec = fs
end end
end end
@ -474,75 +436,32 @@ local show_fs = function(player, player_name)
sfinv.set_player_inventory_formspec(player, context) sfinv.set_player_inventory_formspec(player, context)
else else
get_formspec(player_name) get_formspec(player_name)
local data = player_data[player_name]
show_formspec(player_name, "craftguide", data.formspec)
end end
end end
local function recipe_in_inv(inv, item_name, recipes_f) local function filter_items(data)
local recipes = recipes_f or get_recipes(item_name)
local show_item_recipes = {}
for i = 1, #recipes do
show_item_recipes[i] = true
for _, item in pairs(recipes[i].items) do
local group_in_inv = false
if item:sub(1,6) == "group:" then
local groups = group_to_items(item)
for j = 1, #groups do
if item_in_inv(inv, groups[j]) then
group_in_inv = true
end
end
end
if not group_in_inv and not item_in_inv(inv, item) then
show_item_recipes[i] = false
end
end
end
for i = #show_item_recipes, 1, -1 do
if not show_item_recipes[i] then
remove(recipes, i)
end
end
return recipes, in_table(show_item_recipes)
end
local function get_filter_items(data, player)
local filter = data.filter local filter = data.filter
if searches[filter] then if searches[filter] then
data.items = searches[filter] data.items = searches[filter]
return return
end end
local items_list = progressive_mode and data.init_filter_items or init_items local items_list = progressive_mode and data.progressive_items or init_items
local inv = player:get_inventory()
local filtered_list, c = {}, 0 local filtered_list, c = {}, 0
for i = 1, #items_list do for i = 1, #items_list do
local item = items_list[i] local item = items_list[i]
local item_desc = reg_items[item].description:lower() local item_desc = reg_items[item].description:lower()
if filter ~= "" then
if item:find(filter, 1, true) or item_desc:find(filter, 1, true) then if item:find(filter, 1, true) or item_desc:find(filter, 1, true) then
c = c + 1 c = c + 1
filtered_list[c] = item filtered_list[c] = item
end end
elseif progressive_mode then
local _, has_item = recipe_in_inv(inv, item)
if has_item then
c = c + 1
filtered_list[c] = item
end
end
end end
if progressive_mode then if not progressive_mode then
if not data.items then
data.init_filter_items = filtered_list
end
elseif filter ~= "" then
-- Cache the results only if searched 2 times -- Cache the results only if searched 2 times
if searches[filter] == nil then if searches[filter] == nil then
searches[filter] = false searches[filter] = false
@ -554,31 +473,6 @@ local function get_filter_items(data, player)
data.items = filtered_list data.items = filtered_list
end end
local function init_data(user, name)
player_data[name] = {filter = "", pagenum = 1, iX = sfinv_only and 8 or DEFAULT_SIZE}
if progressive_mode then
get_filter_items(player_data[name], user)
end
end
local function get_init_items()
local c = 0
for name, def in pairs(reg_items) do
local is_fuel = get_fueltime(name) > 0
if not (def.groups.not_in_craft_guide == 1 or
def.groups.not_in_creative_inventory == 1) and
(get_recipe(name).items or is_fuel) and
def.description and def.description ~= "" then
c = c + 1
init_items[c] = name
end
end
sort(init_items)
end
mt.register_on_mods_loaded(get_init_items)
local function item_in_recipe(item, recipe) local function item_in_recipe(item, recipe)
local item_groups = reg_items[item].groups local item_groups = reg_items[item].groups
for _, recipe_item in pairs(recipe.items) do for _, recipe_item in pairs(recipe.items) do
@ -614,6 +508,120 @@ local function get_item_usages(item)
return usages return usages
end end
local function get_inv_items(player)
local invlist = player:get_inventory():get_list("main")
local inv_items = {}
for i = 1, #invlist do
local stack = invlist[i]
if not stack:is_empty() then
inv_items[#inv_items + 1] = stack:get_name()
end
end
return inv_items
end
local function progressive_show_recipe(recipe, inv_items)
for _, item in pairs(recipe.items) do
local item_in_inv
if item:sub(1,6) == "group:" then
local groups = extract_groups(item)
for i = 1, #inv_items do
local item_def = reg_items[inv_items[i]]
if item_def then
local item_groups = item_def.groups
if item_has_groups(item_groups, groups) then
item_in_inv = true
end
end
end
else
for i = 1, #inv_items do
if inv_items[i] == item then
item_in_inv = true
end
end
end
if not item_in_inv then
return
end
end
return true
end
function craftguide.progressive_filter_recipes(recipes, player)
local inv_items = get_inv_items(player)
if #inv_items == 0 then
return {}
end
local filtered = {}
for i = 1, #recipes do
local recipe = recipes[i]
if progressive_show_recipe(recipe, inv_items) then
filtered[#filtered + 1] = recipe
end
end
return filtered
end
local function get_progressive_items(player)
local items = {}
for i = 1, #init_items do
local item = init_items[i]
local recipes = get_recipes(item)
recipes = craftguide.progressive_filter_recipes(recipes, player)
if #recipes > 0 then
items[#items + 1] = item
end
end
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,
}
end
local function reset_data(data)
data.show_usage = nil
data.filter = ""
data.input = nil
data.pagenum = 1
data.rnum = 1
data.items = progressive_mode and data.progressive_items or init_items
end
local function get_init_items()
local c = 0
for name, def in pairs(reg_items) do
local is_fuel = get_fueltime(name) > 0
if not (def.groups.not_in_craft_guide == 1 or
def.groups.not_in_creative_inventory == 1) and
(get_recipe(name).items or is_fuel) and
def.description and def.description ~= "" then
c = c + 1
init_items[c] = name
end
end
sort(init_items)
end
mt.register_on_mods_loaded(get_init_items)
local function get_fields(player, ...) local function get_fields(player, ...)
local args, formname, fields = {...} local args, formname, fields = {...}
if sfinv_only then if sfinv_only then
@ -657,7 +665,7 @@ local function get_fields(player, ...)
data.filter = fltr data.filter = fltr
data.pagenum = 1 data.pagenum = 1
get_filter_items(data, player) filter_items(data)
show_fs(player, player_name) show_fs(player, player_name)
elseif fields.prev or fields.next then elseif fields.prev or fields.next then
@ -680,9 +688,12 @@ local function get_fields(player, ...)
if item:find(":") then if item:find(":") then
local is_fuel = get_fueltime(item) > 0 local is_fuel = get_fueltime(item) > 0
local recipes = get_recipes(item) local recipes = get_recipes(item)
if progressive_mode then
recipes = craftguide.progressive_filter_recipes(recipes, player)
end
local no_recipes = not next(recipes) local no_recipes = not next(recipes)
if no_recipes and not is_fuel then if no_recipes and (progressive_mode or not is_fuel) then
return return
end end
@ -708,17 +719,12 @@ local function get_fields(player, ...)
} }
end end
if not next(data.usages) then if progressive_mode then
data.show_usage = nil data.usages = craftguide.progressive_filter_recipes(data.usages, player)
end end
elseif progressive_mode then if not next(data.usages) then
local inv = player:get_inventory() data.show_usage = nil
local has_item
recipes, has_item = recipe_in_inv(inv, item, recipes)
if not has_item then
return
end end
end end
@ -749,8 +755,11 @@ if sfinv_only then
local player_name = player:get_player_name() local player_name = player:get_player_name()
local data = player_data[player_name] local data = player_data[player_name]
if progressive_mode or not data then if not data then
init_data(player, player_name) init_data(player, player_name)
elseif progressive_mode then
data.progressive_items = get_progressive_items(player)
filter_items(data)
end end
end, end,
@ -765,12 +774,17 @@ else
local player_name = user:get_player_name() local player_name = user:get_player_name()
local data = player_data[player_name] local data = player_data[player_name]
if progressive_mode or not data then if not data then
init_data(user, player_name) init_data(user, player_name)
get_formspec(player_name) get_formspec(player_name)
else data = player_data[player_name]
show_formspec(player_name, "craftguide", data.formspec) elseif progressive_mode then
data.progressive_items = get_progressive_items(user)
filter_items(data)
get_formspec(player_name)
end end
show_formspec(player_name, "craftguide", data.formspec)
end end
mt.register_craftitem("craftguide:book", { mt.register_craftitem("craftguide:book", {