Files
modolith/mods/unified_inventory_plus/craft_all.lua
T

151 lines
5.3 KiB
Lua

local ui = unified_inventory
local uip = unified_inventory_plus
local has = uip.has
local settings = uip.settings
local S = uip.S
local F = minetest.formspec_escape
-- Backup to inject code
uip.craft_all = ui.pages["craft"].get_formspec
ui.pages["craft"] = {
get_formspec = function(player, perplayer_formspec)
local formspec = uip.craft_all(player, perplayer_formspec).formspec
formspec = formspec ..
("image[%f,%f;%f,%f;ui_crafting_long_arrow.png]"):format(
perplayer_formspec.craft_arrow_x,
perplayer_formspec.craft_y,
ui.imgscale,
ui.imgscale * 3) ..
("button[%f,%f;%f,%f;craft_craftall;%s]"):format(
perplayer_formspec.craft_arrow_x + 0.23,
perplayer_formspec.craft_y + 1.50,
perplayer_formspec.btn_size,
perplayer_formspec.btn_size,
F(S("All"))
)
return { formspec = formspec }
end,
}
-- make sure the width is right
local function infer_width(list, expected)
if not expected or expected:is_empty() then
return
end
local width
for i = 1, 3 do
local output, _ = minetest.get_craft_result({ method = "normal", width = i, items = list })
if output.item:to_string() == expected:to_string() then
width = i
break
end
end
if not width then
uip.log("warning", "Can't infer recipe width for %s", expected:to_string())
end
return width
end
-- Craft max possible items and put the result in the main inventory
local function craft_craftall(player)
local player_name = player:get_player_name()
local player_inv = player:get_inventory()
local craft_list = player_inv:get_list("craft")
local expected_result = player_inv:get_stack("craftpreview", 1)
local craft_width = infer_width(craft_list, expected_result)
if not craft_width then
return
end
local num_crafted = 0
-- don't modify player's inventory until we're done, in case something goes wrong (e.g. crash)
-- use a fake inventory instead of a detached inventory, because detached inventory actions all result in packets
-- sent to the player.
local tmp_inv = fakelib.create_inventory()
tmp_inv:set_list("main", player_inv:get_list("main"))
tmp_inv:set_list("craft", craft_list)
while true do
if (
has.stamina and
stamina.get_saturation and
stamina.get_saturation(player) <= settings.craft_all_min_saturation
) then
minetest.chat_send_player(player_name, S("You are too hungry to use Craft All at this time."))
break
end
-- note that get_craft_result can be *very* slow, until minetest 5.7.0 is released.
-- see https://github.com/minetest/minetest/issues/13231
local output, decremented_input = minetest.get_craft_result({
method = "normal",
width = craft_width,
items = craft_list,
})
if output.item:get_name() ~= expected_result:get_name() then
-- the recipe changed, so we've run out of something. stop processing.
break
end
-- minetest.on_craft expects to see the decremented input list.
tmp_inv:set_list("craft", decremented_input.items)
-- invoke callbacks, for compatibility w/ stamina, skyblock, moretrees, etc.
-- note that this is an *undocumented* handler of the minetest lua API, and possibly is subject to change
-- in the future.
output.item = minetest.on_craft(output.item, player, craft_list, tmp_inv)
-- track items added to the inventory, in case we need to remove them later
local added = {}
if tmp_inv:room_for_item("main", output.item) then
tmp_inv:add_item("main", output.item) -- should be no remainder, ignore it
table.insert(added, output.item)
else
-- no room for the output item, stop
break
end
-- we now try to add all replacements.
local all_added = true
for _, replacement_stk in ipairs(output.replacements) do
if tmp_inv:room_for_item("main", replacement_stk) then
tmp_inv:add_item("main", replacement_stk) -- should be no remainder, ignore it
table.insert(added, replacement_stk)
else
all_added = false
break
end
end
if not all_added then
-- if we failed to add all the replacements, remove what we've added, and abort
for _, stk in ipairs(added) do
tmp_inv:remove_item("main", stk) -- should be no remainder, ignore it
end
break
end
-- the craft list can be modified by the callbacks, so re-load it
craft_list = tmp_inv:get_list("craft")
num_crafted = num_crafted + 1
end
player_inv:set_list("craft", craft_list)
player_inv:set_list("main", tmp_inv:get_list("main"))
uip.log("action", "player %s crafts %s %i", player_name, expected_result:to_string(), num_crafted)
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
for k, _ in pairs(fields) do
if k:match("craft_craftall") then
craft_craftall(player)
return
end
end
end)