Show item usages in craft guide

When the craft guide is showing a craft, the output slot is now a button,
which causes the craft guide to show ways in which that output can be
used.  This mirrors the way input slots are buttons that show recipes
for the selected ingredient.  Usages of an item can be iterated through
in the same way as recipes for the item.  This incidentally offers some
ability to retrace one's steps through a crafting chain, without storing
actual history.
This commit is contained in:
Zefram 2014-06-13 15:04:20 +01:00 committed by Diego Martinez
parent c33efe8631
commit 87f502a259
7 changed files with 97 additions and 51 deletions

25
api.lua

@ -42,6 +42,25 @@ minetest.after(0.01, function()
end end
end end
end end
for _, recipes in pairs(unified_inventory.crafts_for.recipe) do
for _, recipe in ipairs(recipes) do
local ingredient_items = {}
for _, spec in ipairs(recipe.items) do
local matches_spec = unified_inventory.canonical_item_spec_matcher(spec)
for _, name in ipairs(unified_inventory.items_list) do
if matches_spec(name) then
ingredient_items[name] = true
end
end
end
for name, _ in pairs(ingredient_items) do
if unified_inventory.crafts_for.usage[name] == nil then
unified_inventory.crafts_for.usage[name] = {}
end
table.insert(unified_inventory.crafts_for.usage[name], recipe)
end
end
end
end) end)
@ -101,10 +120,10 @@ function unified_inventory.register_craft(options)
if options.type == "normal" and options.width == 0 then if options.type == "normal" and options.width == 0 then
options = { type = "shapeless", items = options.items, output = options.output, width = 0 } options = { type = "shapeless", items = options.items, output = options.output, width = 0 }
end end
if unified_inventory.crafts_table[itemstack:get_name()] == nil then if unified_inventory.crafts_for.recipe[itemstack:get_name()] == nil then
unified_inventory.crafts_table[itemstack:get_name()] = {} unified_inventory.crafts_for.recipe[itemstack:get_name()] = {}
end end
table.insert(unified_inventory.crafts_table[itemstack:get_name()],options) table.insert(unified_inventory.crafts_for.recipe[itemstack:get_name()],options)
end end

@ -14,28 +14,15 @@ minetest.register_on_joinplayer(function(player)
unified_inventory.filtered_items_list[player_name] = unified_inventory.filtered_items_list[player_name] =
unified_inventory.items_list unified_inventory.items_list
unified_inventory.activefilter[player_name] = "" unified_inventory.activefilter[player_name] = ""
unified_inventory.apply_filter(player, "") unified_inventory.active_search_direction[player_name] = "nochange"
unified_inventory.apply_filter(player, "", "nochange")
unified_inventory.current_searchbox[player_name] = "" unified_inventory.current_searchbox[player_name] = ""
unified_inventory.alternate[player_name] = 1 unified_inventory.alternate[player_name] = 1
unified_inventory.current_item[player_name] = nil unified_inventory.current_item[player_name] = nil
unified_inventory.current_craft_direction[player_name] = "recipe"
unified_inventory.set_inventory_formspec(player, unified_inventory.set_inventory_formspec(player,
unified_inventory.default) unified_inventory.default)
-- Crafting guide inventories
local inv = minetest.create_detached_inventory(player_name.."craftrecipe", {
allow_put = function(inv, listname, index, stack, player)
return 0
end,
allow_take = function(inv, listname, index, stack, player)
return 0
end,
allow_move = function(inv, from_list, from_index, to_list,
to_index, count, player)
return 0
end,
})
inv:set_size("output", 1)
-- Refill slot -- Refill slot
local refill = minetest.create_detached_inventory(player_name.."refill", { local refill = minetest.create_detached_inventory(player_name.."refill", {
allow_put = function(inv, listname, index, stack, player) allow_put = function(inv, listname, index, stack, player)
@ -122,12 +109,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local clicked_item = nil local clicked_item = nil
for name, value in pairs(fields) do for name, value in pairs(fields) do
if string.sub(name, 1, 12) == "item_button_" then if string.sub(name, 1, 12) == "item_button_" then
clicked_item = unified_inventory.demangle_for_formspec(string.sub(name, 13)) local new_dir, mangled_item = string.match(name, "^item_button_([a-z]+)_(.*)$")
clicked_item = unified_inventory.demangle_for_formspec(mangled_item)
if string.sub(clicked_item, 1, 6) == "group:" then if string.sub(clicked_item, 1, 6) == "group:" then
minetest.sound_play("click", {to_player=player_name, gain = 0.1}) minetest.sound_play("click", {to_player=player_name, gain = 0.1})
unified_inventory.apply_filter(player, clicked_item) unified_inventory.apply_filter(player, clicked_item, new_dir)
return return
end end
if new_dir == "recipe" or new_dir == "usage" then
unified_inventory.current_craft_direction[player_name] = new_dir
end
break break
end end
end end
@ -156,7 +147,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
if fields.searchbutton then if fields.searchbutton then
unified_inventory.apply_filter(player, unified_inventory.current_searchbox[player_name]) unified_inventory.apply_filter(player, unified_inventory.current_searchbox[player_name], "nochange")
unified_inventory.current_searchbox[player_name] = "" unified_inventory.current_searchbox[player_name] = ""
unified_inventory.set_inventory_formspec(player, unified_inventory.set_inventory_formspec(player,
unified_inventory.current_page[player_name]) unified_inventory.current_page[player_name])
@ -172,7 +163,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if item_name then if item_name then
local alternates = 0 local alternates = 0
local alternate = unified_inventory.alternate[player_name] local alternate = unified_inventory.alternate[player_name]
local crafts = unified_inventory.crafts_table[item_name] local crafts = unified_inventory.crafts_for[unified_inventory.current_craft_direction[player_name]][item_name]
if crafts ~= nil then if crafts ~= nil then
alternates = #crafts alternates = #crafts
end end

@ -1,3 +1,25 @@
function unified_inventory.canonical_item_spec_matcher(spec)
local specname = ItemStack(spec):get_name()
if specname:sub(1, 6) == "group:" then
local group_names = specname:sub(7):split(",")
return function (itemname)
local itemdef = minetest.registered_items[itemname]
for _, group_name in ipairs(group_names) do
if (itemdef.groups[group_name] or 0) == 0 then
return false
end
end
return true
end
else
return function (itemname) return itemname == specname end
end
end
function unified_inventory.item_matches_spec(item, spec)
local itemname = ItemStack(item):get_name()
return unified_inventory.canonical_item_spec_matcher(spec)(itemname)
end
unified_inventory.registered_group_items = { unified_inventory.registered_group_items = {
mesecon_conductor_craftable = "mesecons:wire_00000000_off", mesecon_conductor_craftable = "mesecons:wire_00000000_off",

@ -6,14 +6,15 @@ local worldpath = minetest.get_worldpath()
-- Data tables definitions -- Data tables definitions
unified_inventory = {} unified_inventory = {}
unified_inventory.activefilter = {} unified_inventory.activefilter = {}
unified_inventory.active_search_direction = {}
unified_inventory.alternate = {} unified_inventory.alternate = {}
unified_inventory.current_page = {} unified_inventory.current_page = {}
unified_inventory.current_searchbox = {} unified_inventory.current_searchbox = {}
unified_inventory.current_index = {} unified_inventory.current_index = {}
unified_inventory.current_item = {} unified_inventory.current_item = {}
unified_inventory.current_craft_direction = {}
unified_inventory.registered_craft_types = {} unified_inventory.registered_craft_types = {}
unified_inventory.crafts_table = {} unified_inventory.crafts_for = { usage = {}, recipe = {} }
unified_inventory.crafts_table_count = 0
unified_inventory.players = {} unified_inventory.players = {}
unified_inventory.items_list_size = 0 unified_inventory.items_list_size = 0
unified_inventory.items_list = {} unified_inventory.items_list = {}

@ -71,6 +71,7 @@ function unified_inventory.get_formspec(player, page)
if #unified_inventory.filtered_items_list[player_name] == 0 then if #unified_inventory.filtered_items_list[player_name] == 0 then
formspec = formspec.."label[8.2,0;No matching items]" formspec = formspec.."label[8.2,0;No matching items]"
else else
local dir = unified_inventory.active_search_direction[player_name]
local list_index = unified_inventory.current_index[player_name] local list_index = unified_inventory.current_index[player_name]
local page = math.floor(list_index / (80) + 1) local page = math.floor(list_index / (80) + 1)
local pagemax = math.floor( local pagemax = math.floor(
@ -84,7 +85,7 @@ function unified_inventory.get_formspec(player, page)
formspec = formspec.."item_image_button[" formspec = formspec.."item_image_button["
..(8.2 + x * 0.7).."," ..(8.2 + x * 0.7)..","
..(1 + y * 0.7)..";.81,.81;" ..(1 + y * 0.7)..";.81,.81;"
..name..";item_button_" ..name..";item_button_"..dir.."_"
..unified_inventory.mangle_for_formspec(name)..";]" ..unified_inventory.mangle_for_formspec(name)..";]"
list_index = list_index + 1 list_index = list_index + 1
end end
@ -108,7 +109,7 @@ function unified_inventory.set_inventory_formspec(player, page)
end end
--apply filter to the inventory list (create filtered copy of full one) --apply filter to the inventory list (create filtered copy of full one)
function unified_inventory.apply_filter(player, filter) function unified_inventory.apply_filter(player, filter, search_dir)
local player_name = player:get_player_name() local player_name = player:get_player_name()
local lfilter = string.lower(filter) local lfilter = string.lower(filter)
local ffilter local ffilter
@ -139,6 +140,7 @@ function unified_inventory.apply_filter(player, filter)
unified_inventory.filtered_items_list_size[player_name] = #unified_inventory.filtered_items_list[player_name] unified_inventory.filtered_items_list_size[player_name] = #unified_inventory.filtered_items_list[player_name]
unified_inventory.current_index[player_name] = 1 unified_inventory.current_index[player_name] = 1
unified_inventory.activefilter[player_name] = filter unified_inventory.activefilter[player_name] = filter
unified_inventory.active_search_direction[player_name] = search_dir
unified_inventory.set_inventory_formspec(player, unified_inventory.set_inventory_formspec(player,
unified_inventory.current_page[player_name]) unified_inventory.current_page[player_name])
end end

@ -169,6 +169,23 @@ local function stack_image_button(x, y, w, h, buttonname_prefix, item)
label) label)
end end
local recipe_text = {
recipe = "Recipe",
usage = "Usage",
}
local no_recipe_text = {
recipe = "No recipes",
usage = "No usages",
}
local role_text = {
recipe = "Result",
usage = "Ingredient",
}
local other_dir = {
recipe = "usage",
usage = "recipe",
}
unified_inventory.register_page("craftguide", { unified_inventory.register_page("craftguide", {
get_formspec = function(player) get_formspec = function(player)
local player_name = player:get_player_name() local player_name = player:get_player_name()
@ -179,36 +196,33 @@ unified_inventory.register_page("craftguide", {
local item_name = unified_inventory.current_item[player_name] local item_name = unified_inventory.current_item[player_name]
if not item_name then return {formspec=formspec} end if not item_name then return {formspec=formspec} end
formspec = formspec.."background[0,1;8,3;ui_craftguide_form.png]" local dir = unified_inventory.current_craft_direction[player_name]
formspec = formspec.."textarea[0.3,0.6;10,1;;Result: "..minetest.formspec_escape(item_name)..";]" local crafts = unified_inventory.crafts_for[dir][item_name]
formspec = formspec.."list[detached:"..minetest.formspec_escape(player_name).."craftrecipe;output;6,1;1,1;]" local alternate = unified_inventory.alternate[player_name]
local craftinv = minetest.get_inventory({ local alternates, craft
type = "detached",
name = player_name.."craftrecipe"
})
local alternate, alternates, craft, craft_type
alternate = unified_inventory.alternate[player_name]
local crafts = unified_inventory.crafts_table[item_name]
if crafts ~= nil and #crafts > 0 then if crafts ~= nil and #crafts > 0 then
alternates = #crafts alternates = #crafts
craft = crafts[alternate] craft = crafts[alternate]
end end
formspec = formspec.."background[0,1;8,3;ui_craftguide_form.png]"
formspec = formspec.."textarea[0.3,0.6;10,1;;"..minetest.formspec_escape(role_text[dir]..": "..item_name)..";]"
if not craft then if not craft then
craftinv:set_stack("output", 1, item_name) formspec = formspec.."label[6,3.35;"..minetest.formspec_escape(no_recipe_text[dir]).."]"
craft_type = unified_inventory.craft_type_defaults("", {}) local no_pos = dir == "recipe" and 4 or 6
formspec = formspec.."label[6,3.35;No recipes]" local item_pos = dir == "recipe" and 6 or 4
formspec = formspec.."image[4,1;1.1,1.1;ui_no.png]" formspec = formspec.."image["..no_pos..",1;1.1,1.1;ui_no.png]"
formspec = formspec..stack_image_button(item_pos, 1, 1.1, 1.1, "item_button_"..other_dir[dir].."_", ItemStack(item_name))
return {formspec = formspec} return {formspec = formspec}
end end
craftinv:set_stack("output", 1, craft.output) local craft_type = unified_inventory.registered_craft_types[craft.type] or
craft_type = unified_inventory.registered_craft_types[craft.type] or
unified_inventory.craft_type_defaults(craft.type, {}) unified_inventory.craft_type_defaults(craft.type, {})
formspec = formspec.."label[6,3.35;Method:]" formspec = formspec.."label[6,3.35;Method:]"
formspec = formspec.."label[6,3.75;" formspec = formspec.."label[6,3.75;"
..minetest.formspec_escape(craft_type.description).."]" ..minetest.formspec_escape(craft_type.description).."]"
formspec = formspec..stack_image_button(6, 1, 1.1, 1.1, "item_button_usage_", ItemStack(craft.output))
local display_size = craft_type.dynamic_display_size and craft_type.dynamic_display_size(craft) or { width = craft_type.width, height = craft_type.height } local display_size = craft_type.dynamic_display_size and craft_type.dynamic_display_size(craft) or { width = craft_type.width, height = craft_type.height }
local craft_width = craft_type.get_shaped_craft_width and craft_type.get_shaped_craft_width(craft) or display_size.width local craft_width = craft_type.get_shaped_craft_width and craft_type.get_shaped_craft_width(craft) or display_size.width
@ -224,7 +238,8 @@ unified_inventory.register_page("craftguide", {
if item then if item then
formspec = formspec..stack_image_button( formspec = formspec..stack_image_button(
xoffset + x, y, 1.1, 1.1, xoffset + x, y, 1.1, 1.1,
"item_button_", ItemStack(item)) "item_button_recipe_",
ItemStack(item))
else else
-- Fake buttons just to make grid -- Fake buttons just to make grid
formspec = formspec.."image_button[" formspec = formspec.."image_button["
@ -242,7 +257,7 @@ unified_inventory.register_page("craftguide", {
end end
if alternates and alternates > 1 then if alternates and alternates > 1 then
formspec = formspec.."label[0,2.6;Recipe " formspec = formspec.."label[0,2.6;"..recipe_text[dir].." "
..tostring(alternate).." of " ..tostring(alternate).." of "
..tostring(alternates).."]" ..tostring(alternates).."]"
.."button[0,3.15;2,1;alternate;Alternate]" .."button[0,3.15;2,1;alternate;Alternate]"
@ -259,17 +274,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
if not amount then return end if not amount then return end
local player_name = player:get_player_name() local player_name = player:get_player_name()
local recipe_inv = minetest.get_inventory({
type="detached",
name=player_name.."craftrecipe",
})
local output = unified_inventory.current_item[player_name] local output = unified_inventory.current_item[player_name]
if (not output) or (output == "") then return end if (not output) or (output == "") then return end
local player_inv = player:get_inventory() local player_inv = player:get_inventory()
local crafts = unified_inventory.crafts_table[output] local crafts = unified_inventory.crafts_for[unified_inventory.current_craft_direction[player_name]][output]
if (not crafts) or (#crafts == 0) then return end if (not crafts) or (#crafts == 0) then return end
local alternate = unified_inventory.alternate[player_name] local alternate = unified_inventory.alternate[player_name]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB