crafting/gui.lua

260 lines
8.0 KiB
Lua

-- Crafting Mod - semi-realistic crafting in minetest
-- Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
local function get_item_description(name)
if name:sub(1, 6) == "group:" then
local group = name:sub(7, #name):gsub("%_", " ")
return "Any " .. group
else
local def = minetest.registered_items[name] or {}
return def.description or name
end
end
function crafting.make_result_selector(player, type, level, size, context)
local page = context.crafting_page or 1
local full_recipes = crafting.get_all_for_player(player, type, level)
local recipes
if context.crafting_query then
recipes = {}
for i = 1, #full_recipes do
local output = full_recipes[i].recipe.output
local desc = get_item_description(output):lower()
if string.find(output, context.crafting_query, 1, true) or
string.find(desc, context.crafting_query, 1, true) then
recipes[#recipes + 1] = full_recipes[i]
end
end
else
recipes = full_recipes
end
local num_per_page = size.x * size.y
local max_pages = 1
if #recipes > 0 then
max_pages = math.floor((#recipes + num_per_page - 1) / num_per_page)
end
if page > max_pages or page < 1 then
page = ((page - 1) % max_pages) + 1
context.crafting_page = page
end
local start_i = (page - 1) * num_per_page + 1
local formspec = {}
formspec[#formspec + 1] = "container["
formspec[#formspec + 1] = tostring(size.x)
formspec[#formspec + 1] = ","
formspec[#formspec + 1] = tostring(size.y)
formspec[#formspec + 1] = "]"
formspec[#formspec + 1] = "style_type[item_image_button;border=false]"
formspec[#formspec + 1] = "field_close_on_enter[query;false]"
formspec[#formspec + 1] = "field[-4.75,0.81;3,0.8;query;;"
formspec[#formspec + 1] = context.crafting_query
formspec[#formspec + 1] = "]button[-2.2,0.5;0.8,0.8;search;?]"
formspec[#formspec + 1] = "button[-1.4,0.5;0.8,0.8;prev;<]"
formspec[#formspec + 1] = "button[-0.8,0.5;0.8,0.8;next;>]"
formspec[#formspec + 1] = "container_end[]"
formspec[#formspec + 1] = "label[0,-0.25;"
formspec[#formspec + 1] = minetest.formspec_escape("Page: " ..
page .. "/" .. max_pages ..
" | Unlocked: " .. #full_recipes .. " / " .. #crafting.recipes[type])
formspec[#formspec + 1] = "]"
local x = 0
local y = 0
local y_offset = 0.2
for i = start_i, math.min(#recipes, start_i * num_per_page) do
local result = recipes[i]
local recipe = result.recipe
local itemname = ItemStack(recipe.output):get_name()
local item_description = get_item_description(itemname)
formspec[#formspec + 1] = "item_image_button["
formspec[#formspec + 1] = x
formspec[#formspec + 1] = ","
formspec[#formspec + 1] = y + y_offset
formspec[#formspec + 1] = ";1,1;"
formspec[#formspec + 1] = recipe.output
formspec[#formspec + 1] = ";result_"
formspec[#formspec + 1] = tostring(recipe.id)
formspec[#formspec + 1] = ";]"
formspec[#formspec + 1] = "tooltip[result_"
formspec[#formspec + 1] = tostring(recipe.id)
formspec[#formspec + 1] = ";"
formspec[#formspec + 1] = minetest.formspec_escape(item_description .. "\n")
for j, item in pairs(result.items) do
local color = item.have >= item.need and "#6f6" or "#f66"
local itemtab = {
"\n",
minetest.get_color_escape_sequence(color),
get_item_description(item.name), ": ",
item.have, "/", item.need
}
formspec[#formspec + 1] = minetest.formspec_escape(table.concat(itemtab, ""))
end
formspec[#formspec + 1] = minetest.get_color_escape_sequence("#ffffff")
formspec[#formspec + 1] = "]"
formspec[#formspec + 1] = "image["
formspec[#formspec + 1] = x
formspec[#formspec + 1] = ","
formspec[#formspec + 1] = y + y_offset
if result.craftable then
formspec[#formspec + 1] = ";1,1;crafting_slot_craftable.png]"
else
formspec[#formspec + 1] = ";1,1;crafting_slot_uncraftable.png]"
end
x = x + 1
if x == size.x then
x = 0
y = y + 1
end
if y == size.y then
break
end
end
while y < size.y do
while x < size.x do
formspec[#formspec + 1] = "image["
formspec[#formspec + 1] = tostring(x)
formspec[#formspec + 1] = ","
formspec[#formspec + 1] = tostring(y + y_offset)
formspec[#formspec + 1] = ";1,1;crafting_slot_empty.png]"
x = x + 1
end
x = 0
y = y + 1
end
return table.concat(formspec, "")
end
function crafting.result_select_on_receive_results(player, type, level, context, fields)
if fields.prev then
context.crafting_page = (context.crafting_page or 1) - 1
return true
elseif fields.next then
context.crafting_page = (context.crafting_page or 1) + 1
return true
elseif fields.search or fields.key_enter_field == "query" then
context.crafting_query = fields.query:trim():lower()
context.crafting_page = 1
if context.crafting_query == "" then
context.crafting_query = nil
end
return true
end
for key, value in pairs(fields) do
if key:sub(1, 7) == "result_" then
local num = string.match(key, "result_([0-9]+)")
if num then
local inv = player:get_inventory()
local recipe = crafting.get_recipe(tonumber(num))
local name = player:get_player_name()
if not crafting.can_craft(name, type, level, recipe) then
minetest.log("error", "[crafting] Player clicked a button they shouldn't have been able to")
return true
elseif crafting.perform_craft(name, inv, "main", "main", recipe) then
return true -- crafted
else
minetest.chat_send_player(name, "Missing required items!")
return false
end
end
end
end
end
if minetest.global_exists("sfinv") then
sfinv.override_page("sfinv:crafting", {
get = function(self, player, context)
local formspec = crafting.make_result_selector(player, "inv", 1, { x = 8, y = 3 }, context)
formspec = formspec .. "list[detached:creative_trash;main;0,3.4;1,1;]" ..
"image[0.05,3.5;0.8,0.8;creative_trash_icon.png]"
return sfinv.make_formspec(player, context, formspec, true)
end,
on_player_receive_fields = function(self, player, context, fields)
if crafting.result_select_on_receive_results(player, "inv", 1, context, fields) then
sfinv.set_player_inventory_formspec(player)
end
return true
end
})
end
local node_fs_context = {}
local node_serial = 0
function crafting.make_on_rightclick(type, level, inv_size)
node_serial = node_serial + 1
local formname = "crafting:node_" .. node_serial
local function show(player, context)
local formspec = crafting.make_result_selector(player, type, level, inv_size, context)
formspec = "size[" .. inv_size.x .. "," .. (inv_size.y + 5.6) ..
"]list[current_player;main;0," .. (inv_size.y + 1.7) ..";8,1;]" ..
"list[current_player;main;0," .. (inv_size.y + 2.85) ..";8,3;8]" .. formspec
minetest.show_formspec(player:get_player_name(), formname, formspec)
end
minetest.register_on_player_receive_fields(function(player, _formname, fields)
if formname ~= _formname then
return
end
local context = node_fs_context[player:get_player_name()]
if not context then
return false
end
if crafting.result_select_on_receive_results(player, type, level, context, fields) then
show(player, context)
end
return true
end)
return function(pos, node, player)
local name = player:get_player_name()
local context = node_fs_context[name] or {}
node_fs_context[name] = context
context.pos = vector.new(pos)
context.type = type
context.level = level
show(player, context)
end
end