mirror of
https://github.com/minetest-mods/digtron.git
synced 2024-10-05 17:13:08 +02:00
implement duplicator
This commit is contained in:
parent
1fa10d81ba
commit
63a7f6aaaa
@ -32,6 +32,18 @@ local protection_check = function(pos, player_name)
|
||||
return false
|
||||
end
|
||||
|
||||
local function deep_copy(table_in)
|
||||
local table_out = {}
|
||||
for index, value in pairs(table_in) do
|
||||
if type(value) == "table" then
|
||||
table_out[index] = deep_copy(value)
|
||||
else
|
||||
table_out[index] = value
|
||||
end
|
||||
end
|
||||
return table_out
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
local create_new_id = function()
|
||||
@ -126,6 +138,33 @@ local persist_step, retrieve_step = get_table_functions("step") -- actually just
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
-- Layout creation helpers
|
||||
|
||||
digtron.duplicate = function(digtron_id)
|
||||
local layout = retrieve_layout(digtron_id)
|
||||
if layout == nil then
|
||||
minetest.log("error", "[Digtron] digtron.duplicate called with non-existent id " .. digtron_id)
|
||||
return
|
||||
end
|
||||
local new_layout = deep_copy(layout) -- make a copy because persist_layout caches its parameter as-is
|
||||
local new_id = create_new_id()
|
||||
local new_name = S("Copy of @1", get_name(digtron_id))
|
||||
persist_layout(new_id, new_layout)
|
||||
set_name(new_id, new_name)
|
||||
|
||||
local old_inv = retrieve_inventory(digtron_id)
|
||||
local new_inv = retrieve_inventory(new_id)
|
||||
for inv_name, item_list in pairs(old_inv:get_lists()) do
|
||||
-- Don't copy inventory contents, just copy sizes
|
||||
new_inv:set_size(inv_name, #item_list)
|
||||
end
|
||||
persist_inventory(new_id)
|
||||
|
||||
local new_controller = ItemStack("digtron:controller")
|
||||
local meta = new_controller:get_meta()
|
||||
meta:set_string("digtron_id", new_id)
|
||||
meta:set_string("description", new_name)
|
||||
return new_controller
|
||||
end
|
||||
|
||||
-- recursive function searches out all connected unassigned digtron nodes
|
||||
local get_all_digtron_nodes
|
||||
get_all_digtron_nodes = function(pos, digtron_nodes, digtron_adjacent, player_name)
|
||||
@ -346,7 +385,6 @@ local assemble = function(root_pos, player_name)
|
||||
return nil
|
||||
end
|
||||
-- Process inventories specially
|
||||
-- fuel and main get added to corresponding detached inventory lists
|
||||
for listname, items in pairs(current_meta_table.inventory) do
|
||||
local count = #items
|
||||
-- increase the corresponding detached inventory size
|
||||
@ -651,18 +689,6 @@ end
|
||||
------------------------------------------------------------------------
|
||||
-- Rotation
|
||||
|
||||
local function deep_copy(table_in)
|
||||
local table_out = {}
|
||||
for index, value in pairs(table_in) do
|
||||
if type(value) == "table" then
|
||||
table_out[index] = deep_copy(value)
|
||||
else
|
||||
table_out[index] = value
|
||||
end
|
||||
end
|
||||
return table_out
|
||||
end
|
||||
|
||||
local rotate_layout = function(digtron_id, axis)
|
||||
local layout = retrieve_layout(digtron_id)
|
||||
local axis_hash = minetest.hash_node_position(axis)
|
||||
@ -1278,6 +1304,8 @@ digtron.get_sequence = retrieve_sequence
|
||||
digtron.set_step = persist_step
|
||||
digtron.get_step = retrieve_step
|
||||
|
||||
-- Used by duplicator
|
||||
digtron.get_layout = retrieve_layout
|
||||
|
||||
digtron.assemble = assemble
|
||||
digtron.disassemble = disassemble
|
||||
|
1
init.lua
1
init.lua
@ -51,6 +51,7 @@ dofile(modpath.."/nodes/node_misc.lua")
|
||||
dofile(modpath.."/nodes/node_storage.lua")
|
||||
dofile(modpath.."/nodes/node_digger.lua")
|
||||
dofile(modpath.."/nodes/node_builder.lua")
|
||||
dofile(modpath.."/nodes/node_duplicator.lua")
|
||||
dofile(modpath.."/nodes/recipes.lua")
|
||||
|
||||
|
||||
|
284
nodes/node_duplicator.lua
Normal file
284
nodes/node_duplicator.lua
Normal file
@ -0,0 +1,284 @@
|
||||
-- internationalization boilerplate
|
||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local S, NS = dofile(MP.."/intllib.lua")
|
||||
|
||||
local strip_digtron_prefix = function(desc)
|
||||
-- Having "Digtron " prefixing every description is annoying
|
||||
local prefix = "Digtron "
|
||||
if desc:sub(1,#prefix) == prefix then
|
||||
desc = desc:sub(#prefix+1)
|
||||
end
|
||||
return desc
|
||||
end
|
||||
|
||||
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 hash, data in pairs(layout) do
|
||||
local item = data.node.name
|
||||
local item_def = minetest.registered_items[item]
|
||||
if item_def._digtron_disassembled_node then
|
||||
item = item_def._digtron_disassembled_node
|
||||
item_def = minetest.registered_items[item]
|
||||
end
|
||||
local desc = strip_digtron_prefix(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 = strip_digtron_prefix(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,
|
||||
|
||||
})
|
@ -111,14 +111,14 @@ minetest.register_craft({
|
||||
}
|
||||
})
|
||||
|
||||
--minetest.register_craft({
|
||||
-- output = "digtron:duplicator",
|
||||
-- recipe = {
|
||||
-- {"default:mese_crystal","default:mese_crystal","default:mese_crystal"},
|
||||
-- {"default:chest","digtron:digtron_core","default:chest"},
|
||||
-- {"default:mese_crystal","default:mese_crystal","default:mese_crystal"}
|
||||
-- }
|
||||
--})
|
||||
minetest.register_craft({
|
||||
output = "digtron:duplicator",
|
||||
recipe = {
|
||||
{"default:mese_crystal","default:mese_crystal","default:mese_crystal"},
|
||||
{"default:chest","digtron:digtron_core","default:chest"},
|
||||
{"default:mese_crystal","default:mese_crystal","default:mese_crystal"}
|
||||
}
|
||||
})
|
||||
|
||||
--minetest.register_craft({
|
||||
-- output = "digtron:inventory_ejector",
|
||||
@ -174,17 +174,20 @@ minetest.register_craft({
|
||||
type = "shapeless",
|
||||
recipe = {"digtron:soft_digger_static", "digtron:soft_digger_static"},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digtron:dual_digger_static",
|
||||
type = "shapeless",
|
||||
recipe = {"digtron:digger_static", "digtron:digger_static"},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digtron:soft_digger_static 2",
|
||||
recipe = {
|
||||
{"digtron:dual_soft_digger_static"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digtron:digger_static 2",
|
||||
recipe = {
|
||||
@ -200,18 +203,21 @@ minetest.register_craft({
|
||||
{"digtron:structure"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digtron:digtron_core",
|
||||
recipe = {
|
||||
{"digtron:panel"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digtron:digtron_core",
|
||||
recipe = {
|
||||
{"digtron:corner_panel"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "digtron:digtron_core",
|
||||
recipe = {
|
||||
|
Loading…
Reference in New Issue
Block a user