Separate recipe filters and progressive mode

This commit is contained in:
Paul Ouellette 2019-02-01 17:10:41 -05:00
parent 5625a0cb1c
commit a0ef0809fe
2 changed files with 128 additions and 148 deletions

View File

@ -42,21 +42,22 @@ craftguide.register_craft({
}) })
``` ```
### Progressive mode ### Recipe filters
#### `craftguide.add_progressive_filter(name, function(recipes, player))` Recipe filters can be used to filter the recipes shown to players. Progressive
mode is implemented as a recipe filter.
This function adds a recipe filter when progressive mode is enabled. #### `craftguide.add_recipe_filter(name, function(recipes, player))`
The default `craftguide` filter will still be used.
The function should return the recipes to be displayed, given the available Adds a recipe filter with the given name. The filter function should return the
recipes and an `ObjectRef` to the user. Each recipe is a table of the form recipes to be displayed, given the available recipes and an `ObjectRef` to the
returned by `minetest.get_craft_recipe`. 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
craftguide.add_progressive_filter("Hide secretstuff", function(recipes) craftguide.add_recipe_filter("Hide secretstuff", function(recipes)
local filtered = {} local filtered = {}
for _, recipe in ipairs(recipes) do for _, recipe in ipairs(recipes) do
if recipe.output:sub(1,12) ~= "secretstuff:" then if recipe.output:sub(1,12) ~= "secretstuff:" then
@ -68,11 +69,14 @@ craftguide.add_progressive_filter("Hide secretstuff", function(recipes)
end) end)
``` ```
#### `craftguide.set_progressive_filter(name, function(recipes, player))` #### `craftguide.remove_recipe_filter(name)`
This function sets an unique recipe filter when progressive mode is enabled. Removes the recipe filter with the given name.
The default `craftguide` progressive filter will be overridden.
#### `craftguide.get_progressive_filters()` #### `craftguide.remove_all_filters()`
This function returns all progressive filters that are applied to recipes in progressive mode. Removes all recipe filters.
#### `craftguide.get_recipe_filters()`
Returns a map of recipe filters, indexed by name.

246
init.lua
View File

@ -107,6 +107,51 @@ craftguide.register_craft({
items = {"default:stone"}, items = {"default:stone"},
}) })
local recipe_filters = {}
function craftguide.add_recipe_filter(name, func)
recipe_filters[#recipe_filters + 1] = {name = name, func = func}
end
function craftguide.remove_recipe_filter(name)
recipe_filters[name] = nil
end
function craftguide.remove_all_filters()
recipe_filters = {}
end
function craftguide.get_recipe_filters()
return recipe_filters
end
local function apply_recipe_filters(recipes, player)
for i = 1, #recipe_filters do
local func = recipe_filters[i].func
recipes = func(recipes, player)
end
return recipes
end
local function get_filtered_items(player)
local items = {}
for i = 1, #init_items do
local item = init_items[i]
local recipes = recipes_cache[item]
if recipes then
recipes = apply_recipe_filters(recipes, player)
if #recipes > 0 then
items[#items + 1] = item
end
end
end
return items
end
local function cache_recipes(output) local function cache_recipes(output)
local recipes = mt.get_all_craft_recipes(output) or {} local recipes = mt.get_all_craft_recipes(output) or {}
for i = 1, #craftguide.custom_crafts do for i = 1, #craftguide.custom_crafts do
@ -427,24 +472,21 @@ 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[name] show_formspec(name, "craftguide", make_formspec(name))
data.formspec = make_formspec(name)
show_formspec(name, "craftguide", data.formspec)
end end
end end
local function filter_items(data) local function exec_search(data)
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.p_items or init_items
local filtered_list, c = {}, 0 local filtered_list, c = {}, 0
for i = 1, #items_list do for i = 1, #data.items_unsearched do
local item = items_list[i] local item = data.items_unsearched[i]
local item_desc = reg_items[item].description:lower() local item_desc = reg_items[item].description:lower()
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
@ -453,7 +495,7 @@ local function filter_items(data)
end end
end end
if not progressive_mode then if #recipe_filters == 0 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
@ -499,9 +541,7 @@ end
local function get_inv_items(player) local function get_inv_items(player)
local inv = player:get_inventory() local inv = player:get_inventory()
local stacks = inv:get_list("main") local stacks = table_merge(inv:get_list("main"), inv:get_list("craft"))
local craftlist = inv:get_list("craft")
stacks = table_merge(stacks, craftlist)
local inv_items = {} local inv_items = {}
for i = 1, #stacks do for i = 1, #stacks do
@ -536,104 +576,14 @@ local function item_in_inv(item, inv_items)
end end
end 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
end
end
if recipe_in_inv then
filtered[#filtered + 1] = recipe
end
end
return filtered
end
local progressive_filters = {{
name = "Default filter",
func = progressive_default_filter,
}}
function craftguide.add_progressive_filter(name, func)
progressive_filters[#progressive_filters + 1] = {
name = name,
func = func,
}
end
function craftguide.set_progressive_filter(name, func)
progressive_filters = {{
name = name,
func = func,
}}
end
function craftguide.get_progressive_filters()
return progressive_filters
end
local function apply_progressive_filters(recipes, player)
for i = 1, #progressive_filters do
local func = progressive_filters[i].func
recipes = func(recipes, player)
end
return recipes
end
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]
if recipes then
recipes = apply_progressive_filters(recipes, player)
if #recipes > 0 then
items[#items + 1] = item
end
end
end
items = table_clean(items)
return items
end
local function init_data(player, name) local function init_data(player, name)
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 = init_items, items = init_items,
items_unsearched = init_items,
} }
if progressive_mode then
local meta = player:get_meta()
player_data[name].inv_items = deserialize(meta:get_string("inv_items"))
end
end end
local function reset_data(data) local function reset_data(data)
@ -642,7 +592,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.p_items or init_items data.items = data.items_unsearched
end end
local function get_init_items() local function get_init_items()
@ -681,13 +631,13 @@ local function on_receive_fields(player, fields)
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
local fltr = fields.filter:lower() local fltr = fields.filter:lower()
if not progressive_mode and data.filter == fltr then if data.filter == fltr then
return return
end end
data.filter = fltr data.filter = fltr
data.pagenum = 1 data.pagenum = 1
filter_items(data) exec_search(data)
show_fs(player, name) show_fs(player, name)
elseif fields.prev or fields.next then elseif fields.prev or fields.next then
@ -728,8 +678,8 @@ local function on_receive_fields(player, fields)
local is_fuel = fuel_cache[item] local is_fuel = fuel_cache[item]
local recipes = recipes_cache[item] local recipes = recipes_cache[item]
if progressive_mode and recipes then if recipes then
recipes = apply_progressive_filters(recipes, player) recipes = apply_recipe_filters(recipes, player)
end end
local no_recipes = not recipes or #recipes == 0 local no_recipes = not recipes or #recipes == 0
@ -748,11 +698,7 @@ local function on_receive_fields(player, fields)
end end
if data.show_usages then if data.show_usages then
recipes = get_item_usages(item) recipes = apply_recipe_filters(get_item_usages(item), player)
if progressive_mode then
recipes = apply_progressive_filters(recipes, player)
end
if #recipes == 0 then if #recipes == 0 then
return return
@ -782,9 +728,9 @@ if sfinv_only then
local name = player:get_player_name() local name = player:get_player_name()
local data = player_data[name] local data = player_data[name]
if progressive_mode then if #recipe_filters > 0 then
data.p_items = get_progressive_items(player, name) data.items_unsearched = get_filtered_items(player)
filter_items(data) exec_search(data)
end end
end, end,
@ -803,13 +749,12 @@ else
local name = user:get_player_name() local name = user:get_player_name()
local data = player_data[name] local data = player_data[name]
if progressive_mode then if #recipe_filters > 0 then
data.p_items = get_progressive_items(user, name) data.items_unsearched = get_filtered_items(user)
filter_items(data) exec_search(data)
end end
data.formspec = make_formspec(name) show_formspec(name, "craftguide", make_formspec(name))
show_formspec(name, "craftguide", data.formspec)
end end
mt.register_craftitem("craftguide:book", { mt.register_craftitem("craftguide:book", {
@ -886,7 +831,7 @@ else
end end
end end
if not progressive_mode then if #recipe_filters == 0 then
mt.register_chatcommand("craft", { mt.register_chatcommand("craft", {
description = S("Show recipe(s) of the pointed node"), description = S("Show recipe(s) of the pointed node"),
func = function(name) func = function(name)
@ -947,29 +892,60 @@ mt.register_on_joinplayer(function(player)
init_data(player, name) init_data(player, name)
end) end)
local function save_meta(player, data)
local meta = player:get_meta()
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() local name = player:get_player_name()
if progressive_mode then
save_meta(player, player_data[name])
end
player_data[name] = nil player_data[name] = nil
end) end)
if progressive_mode then if progressive_mode then
craftguide.add_recipe_filter("Progressive filter", function(recipes, player)
local name = player:get_player_name()
local discovered = player_data[name].inv_items
local inv_items = get_inv_items(player)
discovered = table_clean(table_merge(discovered, inv_items))
if #discovered == 0 then
return {}
end
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, discovered) then
recipe_in_inv = false
end
end
if recipe_in_inv then
filtered[#filtered + 1] = recipe
end
end
return filtered
end)
mt.register_on_joinplayer(function(player)
local meta = player:get_meta()
local inv_items = deserialize(meta:get_string("inv_items")) or {}
local name = player:get_player_name()
player_data[name].inv_items = inv_items
end)
local function save_meta(player)
local meta = player:get_meta()
local name = player:get_player_name()
meta:set_string("inv_items", serialize(player_data[name].inv_items))
end
mt.register_on_leaveplayer(save_meta)
mt.register_on_shutdown(function() mt.register_on_shutdown(function()
local players = mt.get_connected_players() local players = mt.get_connected_players()
for i = 1, #players do for i = 1, #players do
local player = players[i] save_meta(players[i])
if player then
local name = player:get_player_name()
save_meta(player, player_data[name])
end
end end
end) end)
end end