digtron/nodes/node_duplicator.lua
FaceDeer 3c0fbca66f make it possible to edit builders that are part of an assembled digtron
Should make it possible to edit other components too, later on.
2020-03-01 23:52:24 -07:00

277 lines
9.7 KiB
Lua

local S = digtron.S
-- Determines how many of each type of Digtron node is needed to build another Digtron
local get_manifest = function(pos)
local manifest = {}
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local template = inv:get_stack("template", 1)
local stack_meta = template:get_meta()
local digtron_id = stack_meta:get_string("digtron_id")
if digtron_id ~= "" then
local layout = digtron.get_layout(digtron_id)
for layout_node_id, data in pairs(layout) do
local item = data.node.name
local item_def = minetest.registered_items[item]
-- Some digtron nodes change into other nodes when they become active.
-- This determines what the original node was in those cases
if item_def._digtron_disassembled_node then
item = item_def._digtron_disassembled_node
item_def = minetest.registered_items[item]
end
local desc = item_def.description
local entry = manifest[desc]
if entry == nil then
entry = {item = item}
manifest[desc] = entry
elseif entry.item ~= item then
minetest.log("error", "[Digtron] Duplicator found two digtron nodes that are defined with the same description: "
.. item .. " and " .. entry.item .. ". File an issue with Digtron's maintainers.")
end
entry.requires = (entry.requires or 0) + 1
end
end
local main_list = inv:get_list("main")
for _, itemstack in ipairs(main_list) do
if not itemstack:is_empty() then
local desc = itemstack:get_definition().description
local entry = manifest[desc]
if entry == nil then
entry = {item = item}
manifest[desc] = entry
end
entry.contains = (entry.contains or 0) + itemstack:get_count()
end
end
local ok = false
if digtron_id ~= "" then
ok = true
for item, entry in pairs(manifest) do
if entry.requires and (entry.contains == nil or entry.contains < entry.requires) then
ok = false
break
end
end
end
return manifest, ok, digtron_id
end
local cache = {}
local get_formspec = function(pos)
local hash = minetest.hash_node_position(pos)
local cache_val = cache[hash]
if cache_val == nil then
cache_val = {}
cache_val.manifest, cache_val.ok = get_manifest(pos)
cache[hash] = cache_val
end
local manifest = cache_val.manifest
local ok = cache_val.ok
-- Build item table
local manifest_formspec_head = "tablecolumns[color;text,tooltip=" .. S("Digtron component.")
..";text,align=center,tooltip=" .. S("Amount of this component required to copy the template Digtron.")
..";text,align=center,tooltip=" .. S("Amount of this component currently available.")
.."]table[0,0;2.9,3;manifest;#FFFFFF,Item,Required,Available"
local manifest_formspec_body = {}
for desc, entry in pairs(manifest) do
local color = "#FFFFFF"
if entry.requires then
if entry.contains == nil or entry.contains < entry.requires then
color = "#FF0000"
else
color = "#00FF00"
end
end
manifest_formspec_body[#manifest_formspec_body + 1] =
","..color..","..desc..","..(entry.requires or "-")..","..(entry.contains or "-")
end
table.sort(manifest_formspec_body)
local manifest_formspec_tail = ";]"
local manifest_formspec = manifest_formspec_head .. table.concat(manifest_formspec_body) .. manifest_formspec_tail
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
-- Duplicate button
local duplicate_button
if ok and inv:is_empty("copy") then
duplicate_button = "button[1,0;1,1;duplicate;"..S("Duplicate").."]tooltip[duplicate;"
.. S("Puts a copy of the template Digtron into the output inventory slot.") .. "]"
else
duplicate_button = "button[1,0;1,1;no_duplicate;X]tooltip[no_duplicate;"
.. S("Duplication cannot proceed at this time.") .. "]"
end
return "size[8,9.3]"
.. "container[5,1.5]"
.. manifest_formspec
.. "container_end[]"
.."label[0,0;" .. S("Digtron components") .. "]"
.."list[current_name;main;0,0.6;5,4;]"
.."tooltip[0,0;5,4.6;".. S("Digtron components in this inventory will be used to create the duplicate.") .."]"
.."container[5,0]"
.."list[current_name;template;0,0;1,1;]"
.."tooltip[0,0;1,1.25;".. S("Place the Digtron you want to make a copy of here.") .."]"
.."label[0.1,0.8;" .. S("Template") .. "]"
..duplicate_button
.."list[current_name;copy;2,0;1,1;]"
.."tooltip[2,0;1,1.25;".. S("The duplicate Digtron is output here.") .."]"
.."label[2.25,0.8;" .. S("Copy") .. "]"
.."container_end[]"
.."list[current_player;main;0,5.15;8,1;]"
.."list[current_player;main;0,6.38;8,3;8]"
.."listring[current_name;main]"
.."listring[current_player;main]"
..default.get_hotbar_bg(0,5.15)
end
minetest.register_node("digtron:duplicator", {
description = S("Digtron Duplicator"),
_doc_items_longdesc = digtron.doc.duplicator_longdesc,
_doc_items_usagehelp = digtron.doc.duplicator_usagehelp,
groups = {cracky = 3, oddly_breakable_by_hand=3},
sounds = digtron.metal_sounds,
tiles = {"digtron_plate.png^(digtron_axel_side.png^[transformR90)",
"digtron_plate.png^(digtron_axel_side.png^[transformR270)",
"digtron_plate.png^digtron_axel_side.png",
"digtron_plate.png^(digtron_axel_side.png^[transformR180)",
"digtron_plate.png^digtron_builder.png",
"digtron_plate.png",
},
paramtype = "light",
paramtype2= "facedir",
is_ground_content = false,
drawtype="nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.5, 0.3125, 0.3125, 0.5, 0.5, 0.5}, -- FrontFrame_top
{-0.5, -0.5, 0.3125, 0.5, -0.3125, 0.5}, -- FrontFrame_bottom
{0.3125, -0.3125, 0.3125, 0.5, 0.3125, 0.5}, -- FrontFrame_right
{-0.5, -0.3125, 0.3125, -0.3125, 0.3125, 0.5}, -- FrontFrame_left
{-0.0625, -0.3125, 0.3125, 0.0625, 0.3125, 0.375}, -- frontcross_vertical
{-0.3125, -0.0625, 0.3125, 0.3125, 0.0625, 0.375}, -- frontcross_horizontal
{-0.4375, -0.4375, -0.4375, 0.4375, 0.4375, 0.3125}, -- Body
{-0.5, -0.3125, -0.5, -0.3125, 0.3125, -0.3125}, -- backframe_vertical
{0.3125, -0.3125, -0.5, 0.5, 0.3125, -0.3125}, -- backframe_left
{-0.5, 0.3125, -0.5, 0.5, 0.5, -0.3125}, -- backframe_top
{-0.5, -0.5, -0.5, 0.5, -0.3125, -0.3125}, -- backframe_bottom
{-0.0625, -0.0625, -0.5625, 0.0625, 0.0625, -0.4375}, -- back_probe
},
},
selection_box = {
type = "regular"
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", get_formspec(pos))
local inv = meta:get_inventory()
inv:set_size("main", 5*4)
inv:set_size("template", 1)
inv:set_size("copy", 1)
end,
on_destruct = function(pos)
cache[minetest.hash_node_position(pos)] = nil
end,
can_dig = function(pos,player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv:is_empty("main") and inv:is_empty("template") and inv:is_empty("copy")
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local stack_name = stack:get_name()
if listname == "main" and (minetest.get_item_group(stack_name, "digtron") > 0 or stack_name == "digtron:controller_unassembled") then
return stack:get_count()
elseif listname == "template" and stack:get_name() == "digtron:controller" then
return stack:get_count()
end
return 0
end,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
if from_list == to_list then
return count
end
return 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return stack:get_count()
end,
on_metadata_inventory_put = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
cache[minetest.hash_node_position(pos)] = nil
meta:set_string("formspec", get_formspec(pos))
end,
on_metadata_inventory_take = function(pos, listname, index, stack, player)
local meta = minetest.get_meta(pos)
cache[minetest.hash_node_position(pos)] = nil
meta:set_string("formspec", get_formspec(pos))
end,
on_receive_fields = function(pos, formname, fields, sender)
if fields.help then
minetest.after(0.5, doc.show_entry, sender:get_player_name(), "nodes", "digtron:duplicator", true)
end
if fields.no_duplicate then
minetest.sound_play("digtron_error", {gain=0.5, to_player=sender:get_player_name()}) -- Insufficient inventory
end
if fields.duplicate then
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if not inv:is_empty("copy") then
minetest.log("error", "[Digtron] duplicator was sent a 'duplicate' command by " .. player_name
.. "but there was an item in the output inventory already. This should be impossible.")
minetest.sound_play("digtron_error", {gain=0.5, to_player=sender:get_player_name()})
return
end
local manifest, ok, digtron_id = get_manifest(pos) -- don't trust formspec fields, that's hackable. Recalculate manifest.
if not ok then
local player_name = sender:get_player_name()
minetest.log("error", "[Digtron] duplicator was sent a 'duplicate' command by " .. player_name
.. "but get_manifest reported insufficent inputs. This should be impossible.")
minetest.sound_play("digtron_error", {gain=0.5, to_player=player_name})
return
end
-- deduct nodes from duplicator inventory
for desc, entry in pairs(manifest) do
if entry.requires then
local count = entry.requires
while count > 0 do
-- We need to do this loop because we may be wanting to remove more items than
-- a single stack of that item can hold.
-- https://github.com/minetest/minetest/issues/8883
local stack_to_remove = ItemStack({name=entry.item, count=count})
stack_to_remove:set_count(math.min(count, stack_to_remove:get_stack_max()))
local removed = inv:remove_item("main", stack_to_remove)
count = count - removed:get_count()
end
end
end
local new_digtron = digtron.duplicate(digtron_id)
inv:set_stack("copy", 1, new_digtron)
minetest.sound_play("digtron_machine_assemble", {gain=1.0, pos=pos})
end
end,
})