make it possible to edit builders that are part of an assembled digtron

Should make it possible to edit other components too, later on.
This commit is contained in:
FaceDeer 2020-03-01 23:52:24 -07:00
parent 6745446fcd
commit 3c0fbca66f
6 changed files with 241 additions and 132 deletions

@ -802,10 +802,8 @@ minetest.register_node("digtron:controller_unassembled", combine_defs(base_def,
local player_name = clicker:get_player_name() local player_name = clicker:get_player_name()
local digtron_id = digtron.assemble(pos, player_name) local digtron_id = digtron.assemble(pos, player_name)
if digtron_id then if digtron_id then
local meta = minetest.get_meta(pos) local player_context = get_context(digtron_id, player_name)
meta:set_string("digtron_id", digtron_id) player_context.open = true
meta:mark_as_private("digtron_id")
get_context(digtron_id, player_name)
minetest.show_formspec(player_name, minetest.show_formspec(player_name,
"digtron:controller_assembled", "digtron:controller_assembled",
get_controller_assembled_formspec(digtron_id, player_name)) get_controller_assembled_formspec(digtron_id, player_name))
@ -833,10 +831,12 @@ minetest.register_node("digtron:controller", combine_defs(base_def, {
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local digtron_id = meta:get_string("digtron_id") local digtron_id = meta:get_string("digtron_id")
local digtron_layout_node_id = meta:get_int("digtron_layout_node_id")
local stack = ItemStack({name=node.name, count=1, wear=0}) local stack = ItemStack({name=node.name, count=1, wear=0})
local stack_meta = stack:get_meta() local stack_meta = stack:get_meta()
stack_meta:set_string("digtron_id", digtron_id) stack_meta:set_string("digtron_id", digtron_id)
stack_meta:set_int("digtron_layout_node_id", digtron_layout_node_id)
stack_meta:set_string("description", digtron.get_name(digtron_id)) stack_meta:set_string("description", digtron.get_name(digtron_id))
local inv = digger:get_inventory() local inv = digger:get_inventory()
local stack = inv:add_item("main", stack) local stack = inv:add_item("main", stack)
@ -863,6 +863,7 @@ minetest.register_node("digtron:controller", combine_defs(base_def, {
if dropped:get_name() == "digtron:controller" then if dropped:get_name() == "digtron:controller" then
local stack_meta = dropped:get_meta() local stack_meta = dropped:get_meta()
stack_meta:set_string("digtron_id", oldmeta:get_string("digtron_id")) stack_meta:set_string("digtron_id", oldmeta:get_string("digtron_id"))
stack_meta:set_int("digtron_layout_node_id", oldmeta:get_int("digtron_layout_node_id"))
stack_meta:set_string("description", oldmeta:get_string("infotext")) stack_meta:set_string("description", oldmeta:get_string("infotext"))
return return
end end

@ -161,7 +161,7 @@ end
-- Used by unassembled builders -- Used by unassembled builders
digtron.update_builder_item = function(pos) digtron.update_builder_item = function(pos)
local node = minetest.get_node(pos) local node = minetest.get_node(pos)
if minetest.get_node_group(node.name, "digtron") ~= 4 then if minetest.get_item_group(node.name, "digtron") ~= 4 then
return return
end end
local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2))
@ -179,11 +179,11 @@ digtron.update_builder_items = function(digtron_id)
local layout = digtron.get_layout(digtron_id) local layout = digtron.get_layout(digtron_id)
local root_pos = digtron.get_pos(digtron_id) local root_pos = digtron.get_pos(digtron_id)
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
local node = data.node local node = data.node
if minetest.get_node_group(node.name, "digtron") == 4 then if minetest.get_item_group(node.name, "digtron") == 4 then
local item = data.meta.fields.item local item = data.meta.fields.item
local pos = vector.add(minetest.get_position_from_hash(hash), root_pos) local pos = vector.add(data.pos, root_pos)
local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2)) local target_pos = vector.add(pos, minetest.facedir_to_dir(node.param2))
digtron.remove_builder_item(target_pos) digtron.remove_builder_item(target_pos)
if item ~= "" then if item ~= "" then
@ -210,12 +210,12 @@ minetest.register_entity("digtron:builder_item", {
on_activate = function(self, staticdata) on_activate = function(self, staticdata)
local props = self.object:get_properties() local props = self.object:get_properties()
if staticdata ~= nil and staticdata ~= "" then if staticdata ~= nil and staticdata ~= "" then
local pos = self.object:getpos() local pos = self.object:get_pos()
local adjacent_builder = false local adjacent_builder = false
for _, dir in ipairs(digtron.cardinal_dirs) do for _, dir in ipairs(digtron.cardinal_dirs) do
local target_pos = vector.add(pos, dir) local target_pos = vector.add(pos, dir)
local node = minetest.get_node(target_pos) local node = minetest.get_node(target_pos)
if minetest.get_node_group(node.name, "digtron") == 4 then if minetest.get_item_group(node.name, "digtron") == 4 then
-- Not checking whether the adjacent builder is aimed right, -- Not checking whether the adjacent builder is aimed right,
-- has the right builder_item, etc. This is just a failsafe -- has the right builder_item, etc. This is just a failsafe
-- to clean up entities that somehow got left behind when a -- to clean up entities that somehow got left behind when a

@ -4,21 +4,36 @@ local cache = {}
local S = digtron.S local S = digtron.S
local zero_pos = {x=0, y=0, z=0}
--minetest.debug(dump(mod_meta:to_table())) minetest.register_chatcommand("digtron_dump", {
--params = "<name> <privilege>", -- Short parameter description
description = "dump digtron data to debug", -- Full description
privs = {server=true}, -- Require the "privs" privilege to run
func = function(name, param)
minetest.debug(dump(mod_meta:to_table()))
end,
})
-- Wipes mod_meta minetest.register_chatcommand("digtron_wipe", {
--for field, value in pairs(mod_meta:to_table().fields) do --params = "<name> <privilege>", -- Short parameter description
-- mod_meta:set_string(field, "") description = "dump digtron data to debug", -- Full description
--end privs = {server=true}, -- Require the "privs" privilege to run
func = function(name, param)
-- Wipes mod_meta
for field, value in pairs(mod_meta:to_table().fields) do
mod_meta:set_string(field, "")
end
end,
})
local damage_hp = digtron.config.damage_hp local damage_hp = digtron.config.damage_hp
-- see predict_dig for how punch_data gets calculated -- see predict_dig for how punch_data gets calculated
local damage_creatures = function(root_pos, punch_data, items_dropped) local damage_creatures = function(punch_data, items_dropped)
local target_pos = punch_data[2] local target_pos = punch_data[2]
local objects = minetest.get_objects_inside_radius(target_pos, 1.0) local objects = minetest.get_objects_inside_radius(target_pos, 1.0)
if objects ~= nil then if objects ~= nil then
local source_pos = vector.add(minetest.get_position_from_hash(punch_data[1]), root_pos) local source_pos = punch_data[1]
for _, obj in ipairs(objects) do for _, obj in ipairs(objects) do
local dir = vector.normalize(vector.subtract(obj:get_pos(), source_pos)) local dir = vector.normalize(vector.subtract(obj:get_pos(), source_pos))
local armour_multiplier = 1 local armour_multiplier = 1
@ -195,7 +210,7 @@ local persist_sequence, retrieve_sequence = get_table_functions("sequence")
------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------
-- Layout creation helpers -- Layout creation helpers
digtron.duplicate = function(digtron_id) local duplicate = function(digtron_id)
local layout = retrieve_layout(digtron_id) local layout = retrieve_layout(digtron_id)
if layout == nil then if layout == nil then
minetest.log("error", "[Digtron] digtron.duplicate called with non-existent id " .. digtron_id) minetest.log("error", "[Digtron] digtron.duplicate called with non-existent id " .. digtron_id)
@ -218,6 +233,7 @@ digtron.duplicate = function(digtron_id)
local new_controller = ItemStack("digtron:controller") local new_controller = ItemStack("digtron:controller")
local meta = new_controller:get_meta() local meta = new_controller:get_meta()
meta:set_string("digtron_id", new_id) meta:set_string("digtron_id", new_id)
meta:set_int("digtron_layout_node_id", 1) -- the root node is always in index 1, see "assemble"
meta:set_string("description", new_name) meta:set_string("description", new_name)
return new_controller return new_controller
end end
@ -268,10 +284,10 @@ local retrieve_bounding_box = function(digtron_id)
local layout = retrieve_layout(digtron_id) local layout = retrieve_layout(digtron_id)
if layout == nil then return nil end if layout == nil then return nil end
local bbox = {minp = {x=0, y=0, z=0}, maxp = {x=0, y=0, z=0}} local bbox = {minp = {x=0, y=0, z=0}, maxp = {x=0, y=0, z=0}}
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
update_bounding_box(bbox, minetest.get_position_from_hash(hash)) update_bounding_box(bbox, data.pos)
end end
cache_bounding_box[digtron_id] = bbox cache_bounding_box[digtron_id] = bbox
return bbox return bbox
@ -283,14 +299,24 @@ cache_all_builder_targets = {}
local refresh_adjacent = function(digtron_id) local refresh_adjacent = function(digtron_id)
local layout = retrieve_layout(digtron_id) local layout = retrieve_layout(digtron_id)
if layout == nil then return nil end if layout == nil then return nil end
local adjacent = {} -- all adjacent nodes. TODO: if implementing traction wheels, won't be needed local adjacent = {} -- all adjacent nodes. TODO: if implementing traction wheels, won't be needed
local adjacent_to_diggers = {} local adjacent_to_diggers = {}
local adjacent_to_builders = {} local adjacent_to_builders = {}
for hash, data in pairs(layout) do
local all_digtron_node_hashes = {} -- track the locations of the digtron nodes themselves, these will be omitted from adjacency
for layout_node_id, data in pairs(layout) do
local hash = minetest.hash_node_position(data.pos)
data.hash = hash
all_digtron_node_hashes[hash] = true
end
for layout_node_id, data in pairs(layout) do
local hash = data.hash
for _, dir_hash in ipairs(digtron.cardinal_dirs_hash) do for _, dir_hash in ipairs(digtron.cardinal_dirs_hash) do
local potential_adjacent = hash + dir_hash local potential_adjacent = hash + dir_hash
if layout[potential_adjacent] == nil then if not all_digtron_node_hashes[potential_adjacent] then
adjacent[potential_adjacent] = true adjacent[potential_adjacent] = true
end end
end end
@ -300,19 +326,19 @@ local refresh_adjacent = function(digtron_id)
-- Diggers -- Diggers
if digtron_group >= 10 and digtron_group <= 13 then if digtron_group >= 10 and digtron_group <= 13 then
-- All diggers target the node directly in front of them -- All diggers target the node directly in front of them
local dir_hashes = {} local targets = {}
local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local dir_hash = digtron.facedir_to_dir_hash(data.node.param2)
local potential_target = hash + dir_hash -- pointed at this hash local potential_target = hash + dir_hash -- pointed at this hash
if layout[potential_target] == nil then -- not pointed at another Digtron node if not all_digtron_node_hashes[potential_target] then
table.insert(dir_hashes, dir_hash) table.insert(targets, minetest.get_position_from_hash(potential_target))
end end
-- If it's a dual digger, add a second dir -- If it's a dual digger, add a second dir
if digtron_group == 11 or digtron_group == 13 then if digtron_group == 11 or digtron_group == 13 then
dir_hash = digtron.facedir_to_down_hash(data.node.param2) dir_hash = digtron.facedir_to_down_hash(data.node.param2)
potential_target = hash + dir_hash -- pointed at this hash potential_target = hash + dir_hash -- pointed at this hash
if layout[potential_target] == nil then -- not pointed at another Digtron node if not all_digtron_node_hashes[potential_target] then
table.insert(dir_hashes, dir_hash) table.insert(targets, minetest.get_position_from_hash(potential_target))
end end
end end
@ -322,35 +348,40 @@ local refresh_adjacent = function(digtron_id)
soft = true soft = true
end end
if #dir_hashes > 0 then local fields = data.meta.fields
local fields = data.meta.fields if #targets > 0 then
adjacent_to_diggers[hash] = { table.insert(adjacent_to_diggers, {
pos = data.pos,
period = tonumber(fields.period) or 1, period = tonumber(fields.period) or 1,
offset = tonumber(fields.offset) or 0, offset = tonumber(fields.offset) or 0,
dir_hashes = dir_hashes, targets = targets,
soft = soft, soft = soft,
} })
end end
end end
-- Builders -- Builders
if digtron_group == 4 then if digtron_group == 4 then
local dir_hash = digtron.facedir_to_dir_hash(data.node.param2) local dir_hash = digtron.facedir_to_dir_hash(data.node.param2)
local potential_target = hash + dir_hash local potential_target = hash + dir_hash
if layout[potential_target] == nil then if not all_digtron_node_hashes[potential_target] then
local fields = data.meta.fields local fields = data.meta.fields
-- TODO: trace extrusion and if it intersects Digtron layout cap it there. -- TODO: trace extrusion and if it intersects Digtron layout cap it there.
adjacent_to_builders[hash] = { -- This is getting pretty edge case, though, don't worry about it until you're
-- completely bored
table.insert(adjacent_to_builders, {
pos = data.pos,
period = tonumber(fields.period) or 1, period = tonumber(fields.period) or 1,
offset = tonumber(fields.offset) or 0, offset = tonumber(fields.offset) or 0,
item = fields.item, item = fields.item,
facing = tonumber(fields.facing) or 0, -- facing of built node facing = tonumber(fields.facing) or 0, -- facing of built node
extrusion = tonumber(fields.extrusion) or 1, extrusion = tonumber(fields.extrusion) or 1,
dir_hash = dir_hash, -- Record in table form, it'll be more convenient for use later dir = minetest.facedir_to_dir(data.node.param2),
} })
end end
end end
end end
cache_all_adjacent_pos[digtron_id] = adjacent cache_all_adjacent_pos[digtron_id] = adjacent
cache_all_digger_targets[digtron_id] = adjacent_to_diggers cache_all_digger_targets[digtron_id] = adjacent_to_diggers
cache_all_builder_targets[digtron_id] = adjacent_to_builders cache_all_builder_targets[digtron_id] = adjacent_to_builders
@ -423,7 +454,7 @@ local assemble = function(root_pos, player_name)
for hash, node in pairs(digtron_nodes) do for hash, node in pairs(digtron_nodes) do
local pos = minetest.get_position_from_hash(hash) local pos = minetest.get_position_from_hash(hash)
local relative_hash = minetest.hash_node_position(vector.subtract(pos, root_pos)) local relative_pos = vector.subtract(pos, root_pos)
local current_meta local current_meta
if hash == root_hash then if hash == root_hash then
@ -455,17 +486,26 @@ local assemble = function(root_pos, player_name)
current_meta_table.inventory[listname] = #items current_meta_table.inventory[listname] = #items
end end
-- If the node being incorporated into the assembled digtron has a "_digtron_assembled_node" property
-- defined, then pretend it's that node rather than the actual node.
local node_def = minetest.registered_nodes[node.name] local node_def = minetest.registered_nodes[node.name]
if node_def and node_def._digtron_assembled_node then if node_def and node_def._digtron_assembled_node then
node.name = node_def._digtron_assembled_node node.name = node_def._digtron_assembled_node
minetest.swap_node(minetest.get_position_from_hash(hash), node) minetest.swap_node(minetest.get_position_from_hash(hash), node)
end end
node.param1 = nil -- we don't care about param1, wipe it to save space node.param1 = nil -- we don't care about param1, wipe it to save space
layout[relative_hash] = {meta = current_meta_table, node = node} table.insert(layout, {meta = current_meta_table, node = node, pos = relative_pos})
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
-- track this so that we can interact with individual node settings in the assembled digtron end
meta:set_string("digtron_relative_hash", relative_hash)
-- Ensure the root node is in position 1
for layout_node_id, data in ipairs(layout) do
if vector.equals(data.pos, zero_pos) then
layout[layout_node_id] = layout[1]
layout[1] = data
break
end
end end
persist_inventory(digtron_id) persist_inventory(digtron_id)
@ -476,13 +516,14 @@ local assemble = function(root_pos, player_name)
persist_sequence(digtron_id, digtron.default_sequence()) persist_sequence(digtron_id, digtron.default_sequence())
-- Wipe out the inventories of all in-world nodes, it's stored in the mod_meta now. -- Wipe out the inventories of all in-world nodes, it's stored in the mod_meta now.
-- Wait until now to do it in case the above loop fails partway through. -- Wait until now to do it in case the above loop fails partway through and we need to abort.
for hash, node in pairs(digtron_nodes) do for layout_node_id, data in ipairs(layout) do
local node_pos = vector.add(root_pos, data.pos)
local node_meta local node_meta
if hash == root_hash then if vector.equals(root_pos, node_pos) then
node_meta = root_meta -- we're processing the controller, we already have a reference to its meta node_meta = root_meta -- we're processing the controller, we already have a reference to its meta
else else
node_meta = minetest.get_meta(minetest.get_position_from_hash(hash)) node_meta = minetest.get_meta(node_pos)
end end
local inv = node_meta:get_inventory() local inv = node_meta:get_inventory()
@ -492,8 +533,11 @@ local assemble = function(root_pos, player_name)
end end
end end
-- Set metadata on the nodes to indicate that they've been incorporated into an assembled Digtron
node_meta:set_string("digtron_id", digtron_id) node_meta:set_string("digtron_id", digtron_id)
node_meta:set_int("digtron_layout_node_id", layout_node_id)
node_meta:mark_as_private("digtron_id") node_meta:mark_as_private("digtron_id")
node_meta:mark_as_private("digtron_layout_node_id")
end end
minetest.log("action", "Digtron " .. digtron_id .. " assembled at " .. minetest.pos_to_string(root_pos) minetest.log("action", "Digtron " .. digtron_id .. " assembled at " .. minetest.pos_to_string(root_pos)
@ -503,30 +547,48 @@ local assemble = function(root_pos, player_name)
return digtron_id return digtron_id
end end
local function log_prefix(function_name, digtron_id, target_name, target_pos)
return "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s "
.. target_name .. "s at " .. minetest.pos_to_string(target_pos)
end
-- Returns pos, node, and meta for the digtron node provided the in-world node matches the layout -- Returns pos, node, and meta for the digtron node provided the in-world node matches the layout
-- returns nil otherwise -- returns nil otherwise
local get_valid_data = function(digtron_id, root_pos, hash, data, function_name) local get_valid_data = function(digtron_id, layout_node_id, root_pos, data, function_name)
local node_pos = vector.add(minetest.get_position_from_hash(hash), root_pos) local node_pos = data.pos
if not node_pos then
minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with " .. digtron_id
.. " node id " .. layout_node_id .. " but there was no pos in its data: " .. dump(data))
return
end
node_pos = vector.add(root_pos, node_pos)
local data_node_name = data.node.name
local node = minetest.get_node(node_pos) local node = minetest.get_node(node_pos)
local node_meta = minetest.get_meta(node_pos) local node_meta = minetest.get_meta(node_pos)
local target_digtron_id = node_meta:get_string("digtron_id") local target_digtron_id = node_meta:get_string("digtron_id")
local target_node_id = node_meta:get_int("digtron_layout_node_id")
if data.node.name ~= node.name then
minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with one of ".. digtron_id .. "'s " if target_node_id ~= layout_node_id then
.. data.node.name .. "s at " .. minetest.pos_to_string(node_pos) .. " but the node at that location was of type " -- A recoverable error, but may indicate something's wrong elsewhere.
.. node.name) minetest.log("warning", log_prefix(function_name, digtron_id, data_node_name, node_pos)
.. " but the node at that location was marked as layout id " .. target_node_id
.. " instead of " .. layout_node_id)
node_meta:set_int("digtron_layout_node_id", layout_node_id)
node_meta:mark_as_private("digtron_layout_node_id")
end
if data_node_name ~= node.name then
minetest.log("error", log_prefix(function_name, digtron_id, data_node_name, node_pos)
.. " but the node at that location was of type " .. node.name)
return return
elseif target_digtron_id ~= digtron_id then elseif target_digtron_id ~= digtron_id then
if target_digtron_id ~= "" then if target_digtron_id ~= "" then
minetest.log("error", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " minetest.log("error", log_prefix(function_name, digtron_id, data_node_name, node_pos)
.. data.node.name .. " at " .. minetest.pos_to_string(node_pos)
.. " but the node at that location had a non-matching digtron_id value of \"" .. " but the node at that location had a non-matching digtron_id value of \""
.. target_digtron_id .. "\"") .. target_digtron_id .. "\"")
return return
else else
-- Allow digtron to recover from bad map metadata writes, the bane of Digtron 1.0's existence -- Allow digtron to recover from bad map metadata writes, the bane of Digtron 1.0's existence
minetest.log("warning", "[Digtron] " .. function_name .. " tried interacting with ".. digtron_id .. "'s " minetest.log("warning", log_prefix(function_name, digtron_id, data_node_name, node_pos)
.. data.node.name .. " at " .. minetest.pos_to_string(node_pos)
.. " but the node at that location had no digtron_id in its metadata. " .. " but the node at that location had no digtron_id in its metadata. "
.. "Since the node type matched the layout, however, it was included anyway. It's possible " .. "Since the node type matched the layout, however, it was included anyway. It's possible "
.. "its metadata was not written correctly by a previous Digtron activity.") .. "its metadata was not written correctly by a previous Digtron activity.")
@ -558,8 +620,8 @@ local disassemble = function(digtron_id, player_name)
end end
-- Write metadata and inventory to in-world node at this location -- Write metadata and inventory to in-world node at this location
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
local node_pos, node, node_meta = get_valid_data(digtron_id, root_pos, hash, data, "disassemble") local node_pos, node, node_meta = get_valid_data(digtron_id, layout_node_id, root_pos, data, "disassemble")
if node_pos then if node_pos then
local node_inv = node_meta:get_inventory() local node_inv = node_meta:get_inventory()
@ -640,8 +702,8 @@ local remove_from_world = function(digtron_id, player_name)
end end
local nodes_to_destroy = {} local nodes_to_destroy = {}
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
local node_pos = get_valid_data(digtron_id, root_pos, hash, data, "remove_from_world") local node_pos = get_valid_data(digtron_id, layout_node_id, root_pos, data, "remove_from_world")
if node_pos then if node_pos then
table.insert(nodes_to_destroy, node_pos) table.insert(nodes_to_destroy, node_pos)
end end
@ -664,8 +726,8 @@ local is_buildable_to = function(digtron_id, layout, root_pos, player_name, igno
local ignore_hashes = {} local ignore_hashes = {}
if old_root_pos then if old_root_pos then
for hash, _ in pairs(old_layout) do for layout_node_id, data in pairs(old_layout) do
local old_hash = minetest.hash_node_position(vector.add(minetest.get_position_from_hash(hash), old_root_pos)) local old_hash = minetest.hash_node_position(vector.add(data.pos, old_root_pos))
ignore_hashes[old_hash] = true ignore_hashes[old_hash] = true
end end
end end
@ -710,16 +772,20 @@ local build_to_world = function(digtron_id, layout, root_pos, player_name)
layout = retrieve_layout(digtron_id) layout = retrieve_layout(digtron_id)
end end
local built_positions = {} local built_positions = {}
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
-- Don't use get_valid_data, the Digtron isn't in-world yet -- Don't use get_valid_data, the Digtron isn't in-world yet
local node_pos = vector.add(minetest.get_position_from_hash(hash), root_pos) local node_pos = vector.add(data.pos, root_pos)
minetest.debug("setting root at " .. minetest.pos_to_string(root_pos))
minetest.debug("setting node at " .. minetest.pos_to_string(data.pos))
minetest.set_node(node_pos, data.node) minetest.set_node(node_pos, data.node)
local meta = minetest.get_meta(node_pos) local meta = minetest.get_meta(node_pos)
for field, value in pairs(data.meta.fields) do for field, value in pairs(data.meta.fields) do
meta:set_string(field, value) meta:set_string(field, value)
end end
meta:set_string("digtron_id", digtron_id) meta:set_string("digtron_id", digtron_id)
meta:set_int("digtron_layout_node_id", layout_node_id)
meta:mark_as_private("digtron_id") meta:mark_as_private("digtron_id")
meta:mark_as_private("digtron_layout_node_id")
table.insert(built_positions, node_pos) table.insert(built_positions, node_pos)
end end
persist_pos(digtron_id, root_pos) persist_pos(digtron_id, root_pos)
@ -754,7 +820,7 @@ local rotate_layout = function(digtron_id, axis)
local layout = retrieve_layout(digtron_id) local layout = retrieve_layout(digtron_id)
local axis_hash = minetest.hash_node_position(axis) local axis_hash = minetest.hash_node_position(axis)
local rotated_layout = {} local rotated_layout = {}
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
local duplicate_data = deep_copy(data) local duplicate_data = deep_copy(data)
-- Facings -- Facings
local node_name = duplicate_data.node.name local node_name = duplicate_data.node.name
@ -777,10 +843,9 @@ local rotate_layout = function(digtron_id, axis)
end end
-- Position -- Position
local pos = minetest.get_position_from_hash(hash) local pos = data.pos
pos = digtron.rotate_pos(axis_hash, pos) duplicate_data.pos = digtron.rotate_pos(axis_hash, pos)
local new_hash = minetest.hash_node_position(pos) rotated_layout[layout_node_id] = duplicate_data
rotated_layout[new_hash] = duplicate_data
end end
return rotated_layout return rotated_layout
@ -865,11 +930,11 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate)
punches_thrown = {} punches_thrown = {}
end end
for digger_hash, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do for _, digger_data in pairs(retrieve_all_digger_targets(digtron_id)) do
for _, dir_hash in ipairs(digger_data.dir_hashes) do for _, target in ipairs(digger_data.targets) do
local target_hash = digger_hash + dir_hash local target_pos = vector.add(root_pos, target)
local target_hash = minetest.hash_node_position(target_pos)
if not dug_hashes[target_hash] then if not dug_hashes[target_hash] then
local target_pos = vector.add(minetest.get_position_from_hash(target_hash), root_pos)
local target_node = minetest.get_node(target_pos) local target_node = minetest.get_node(target_pos)
local target_name = target_node.name local target_name = target_node.name
local targetdef = minetest.registered_nodes[target_name] local targetdef = minetest.registered_nodes[target_name]
@ -887,10 +952,7 @@ local predict_dig = function(digtron_id, player_name, controlling_coordinate)
and (not digger_data.soft or is_soft_material(target_name)) and (not digger_data.soft or is_soft_material(target_name))
then then
if punches_thrown then if punches_thrown then
-- storing digger_hash rather than converting it into a vector because table.insert(punches_thrown, {vector.add(root_pos, digger_data.pos), target_pos})
-- in most cases there won't be something to punch and that calculation can be skipped
-- convert to digger_pos by adding root_pos
table.insert(punches_thrown, {digger_hash, target_pos})
end end
if target_name ~= "air" then -- TODO: generalise this somehow for liquids and other undiggables if target_name ~= "air" then -- TODO: generalise this somehow for liquids and other undiggables
if digtron.config.uses_resources then if digtron.config.uses_resources then
@ -992,11 +1054,11 @@ local predict_build = function(digtron_id, root_pos, player_name, ignore_nodes,
local built_nodes = {} local built_nodes = {}
local cost = 0 local cost = 0
for target_hash, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do for _, builder_data in pairs(retrieve_all_builder_targets(digtron_id)) do
local dir_hash = builder_data.dir_hash local dir = builder_data.dir
local periodicity_permitted = nil local periodicity_permitted = nil
for i = 1, builder_data.extrusion do for i = 1, builder_data.extrusion do
local target_pos = vector.add(minetest.get_position_from_hash(target_hash + i * dir_hash), root_pos) local target_pos = vector.add(vector.add(builder_data.pos, vector.multiply(dir, i)), root_pos)
local test_hash = minetest.hash_node_position(target_pos) local test_hash = minetest.hash_node_position(target_pos)
if periodicity_permitted == nil then if periodicity_permitted == nil then
-- test periodicity and offset once -- test periodicity and offset once
@ -1189,12 +1251,12 @@ local execute_dig_move_build_cycle = function(digtron_id, player_name, dig_down)
local items_dropped = {} local items_dropped = {}
if punches_thrown then if punches_thrown then
for _, punch_data in ipairs(punches_thrown) do for _, punch_data in ipairs(punches_thrown) do
damage_creatures(old_root_pos, punch_data, items_dropped) damage_creatures(punch_data, items_dropped)
end end
end end
-- Building new Digtron -- Building new Digtron
digtron.build_to_world(digtron_id, layout, new_root_pos, player_name) build_to_world(digtron_id, layout, new_root_pos, player_name)
minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos}) minetest.sound_play("digtron_construction", {gain = 0.5, pos=new_root_pos})
local build_leftovers, success_count = build_nodes(built_nodes, player_name) local build_leftovers, success_count = build_nodes(built_nodes, player_name)
@ -1360,9 +1422,10 @@ local recover_digtron_id = function(root_pos)
.."recover_digtron_id will now attempt to restore the digtron_id metadata key to all " .."recover_digtron_id will now attempt to restore the digtron_id metadata key to all "
.."nodes in this Digtron's layout.") .."nodes in this Digtron's layout.")
local layout = retrieve_layout(digtron_id) local layout = retrieve_layout(digtron_id)
for hash, data in pairs(layout) do local root_pos = retrieve_pos(digtron_id)
for layout_node_id, data in pairs(layout) do
-- get_valid_data will attempt to repair node metadata that's missing digtron_id -- get_valid_data will attempt to repair node metadata that's missing digtron_id
local node_pos, node, node_meta = get_valid_data(digtron_id, root_pos, hash, data, "recover_digtron_id") local node_pos, node, node_meta = get_valid_data(digtron_id, layout_node_id, root_pos, data, "recover_digtron_id")
end end
return true return true
end end
@ -1388,8 +1451,12 @@ digtron.get_inventory = retrieve_inventory
digtron.set_sequence = persist_sequence digtron.set_sequence = persist_sequence
digtron.get_sequence = retrieve_sequence digtron.get_sequence = retrieve_sequence
-- Used by duplicator
digtron.get_layout = retrieve_layout digtron.get_layout = retrieve_layout
digtron.set_layout = function(digtron_id, layout)
invalidate_layout_cache(digtron_id)
persist_layout(digtron_id, layout)
end
digtron.duplicate = duplicate
digtron.assemble = assemble digtron.assemble = assemble
digtron.disassemble = disassemble digtron.disassemble = disassemble

@ -57,7 +57,6 @@ dofile(modpath.."/nodes/node_builder.lua")
dofile(modpath.."/nodes/node_duplicator.lua") dofile(modpath.."/nodes/node_duplicator.lua")
dofile(modpath.."/nodes/recipes.lua") dofile(modpath.."/nodes/recipes.lua")
-- digtron group numbers: -- digtron group numbers:
-- 1 - generic digtron node, nothing special is done with these. They're just dragged along. -- 1 - generic digtron node, nothing special is done with these. They're just dragged along.
-- 2 - inventory-holding digtron, has a "main" inventory that the digtron can add to and take from. -- 2 - inventory-holding digtron, has a "main" inventory that the digtron can add to and take from.

@ -6,15 +6,13 @@ local S = digtron.S
-- TODO make this global -- TODO make this global
local player_interacting_with_builder_pos = {} local player_interacting_with_builder_pos = {}
local get_formspec = function(pos) local get_formspec = function(meta_fields)
local meta = minetest.get_meta(pos) local period = tonumber(meta_fields.period)
local period = meta:get_int("period")
if period < 1 then period = 1 end if period < 1 then period = 1 end
local offset = meta:get_int("offset") local offset = tonumber(meta_fields.offset)
local extrusion = meta:get_int("extrusion") local extrusion = tonumber(meta_fields.extrusion)
local facing = meta:get_int("facing") local facing = tonumber(meta_fields.facing)
local item_name = meta:get_string("item") local item_name = meta_fields.item
return "size[8,5.2]" .. return "size[8,5.2]" ..
"item_image[0,0;1,1;" .. item_name .. "]".. "item_image[0,0;1,1;" .. item_name .. "]"..
@ -87,22 +85,40 @@ local inv = minetest.create_detached_inventory("digtron:builder_item", {
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local digtron_id = meta:get_string("digtron_id") local digtron_id = meta:get_string("digtron_id")
if digtron_id ~= "" then local digtron_layout_id = meta:get_int("digtron_layout_node_id")
minetest.log("warning", "[Digtron] builder detached inventory had player " .. player_name
.. " attempt to set " .. item .. " at " .. minetest.pos_to_string(pos) .. local layout
" but the builder node at that location was already assembled into " .. digtron_id) local layout_fields
return 0 minetest.chat_send_all(digtron_id)
minetest.chat_send_all(digtron_layout_id)
if digtron_id ~= "" and digtron_layout_id ~= 0 then
layout = digtron.get_layout(digtron_id)
layout_fields = layout[digtron_layout_id].meta.fields
end end
-- If we're adding a wallmounted item and the build facing is greater than 5, reset it to 0 -- If we're adding a wallmounted item and the build facing is greater than 5, reset it to 0
if stack_def ~= nil and stack_def.paramtype2 == "wallmounted" and tonumber(meta:get_int("facing")) > 5 then if stack_def ~= nil and stack_def.paramtype2 == "wallmounted" and tonumber(meta:get_int("facing")) > 5 then
meta:set_int("facing", 0) meta:set_int("facing", 0)
if layout_fields then
layout_fields.facing = 0
end
end end
meta:set_string("item", item) meta:set_string("item", item)
if layout_fields then
layout_fields.item = item
end
if layout_fields then
minetest.chat_send_all("setting layout")
digtron.set_layout(digtron_id, layout)
else
layout_fields = meta:to_table().fields
end
digtron.update_builder_item(pos) digtron.update_builder_item(pos)
minetest.show_formspec(player_name, "digtron:builder", get_formspec(pos))
minetest.show_formspec(player_name, "digtron:builder", get_formspec(layout_fields))
return 0 return 0
end, end,
allow_take = function(inv, listname, index, stack, player) allow_take = function(inv, listname, index, stack, player)
@ -123,17 +139,19 @@ local builder_on_rightclick = function(pos, node, clicker, itemstack, pointed_th
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local digtron_id = meta:get_string("digtron_id") local digtron_id = meta:get_string("digtron_id")
if digtron_id ~= "" then local digtron_node_id = meta:get_int("digtron_layout_node_id")
minetest.sound_play({name = "digtron_error", gain = 0.1}, {to_player=player_name}) if digtron_id ~= "" and digtron_node_id ~= 0 then
minetest.chat_send_player(player_name, S("This Digtron is active, interact with it via the controller node.")) player_interacting_with_builder_pos[player_name] = pos
digtron.update_builder_items(digtron_id) local layout = digtron.get_layout(digtron_id)
local data = layout[digtron_node_id]
if data then
minetest.show_formspec(player_name, "digtron:builder", get_formspec(data.meta.fields))
end
return return
end end
player_interacting_with_builder_pos[player_name] = pos player_interacting_with_builder_pos[player_name] = pos
minetest.show_formspec(player_name, minetest.show_formspec(player_name, "digtron:builder", get_formspec(meta:to_table().fields))
"digtron:builder",
get_formspec(pos))
end end
minetest.register_on_player_receive_fields(function(sender, formname, fields) minetest.register_on_player_receive_fields(function(sender, formname, fields)
@ -150,47 +168,62 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
end end
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
local meta_table
local item = meta:get_string("item")
local digtron_id = meta:get_string("digtron_id")
local digtron_layout_id = meta:get_int("digtron_layout_node_id")
local layout
if digtron_id ~= "" and digtron_layout_id ~= 0 then
-- If this builder is part of an assembled Digtron, then the persisted Digtron
-- layout will have primacy over any other metadata this node might have.
layout = digtron.get_layout(digtron_id)
meta_table = layout[digtron_layout_id].meta
else
meta_table = meta:to_table()
end
local meta_fields = meta_table.fields
local item = meta_fields.item
local period = tonumber(fields.period) local period = tonumber(fields.period)
if period and period > 0 then if period and period > 0 then
meta:set_int("period", math.floor(period)) meta_fields.period = math.floor(period)
else else
period = meta:get_int("period") period = tonumber(meta_fields.period) or 1
end end
local offset = tonumber(fields.offset) local offset = tonumber(fields.offset)
if offset then if offset then
meta:set_int("offset", math.floor(offset)) meta_fields.offset = math.floor(offset)
else else
offset = meta:get_int("offset") offset = tonumber(meta_fields.offset) or 0
end end
local facing = tonumber(fields.facing) local facing = tonumber(fields.facing)
if facing and facing >= 0 and facing < 24 then if facing and facing >= 0 and facing < 24 then
local target_item = ItemStack(item) local target_item = ItemStack(item)
if target_item:get_definition().paramtype2 == "wallmounted" then if target_item:get_definition().paramtype2 == "wallmounted" then
-- wallmounted facings only run from 0-5
if facing < 6 then if facing < 6 then
meta:set_int("facing", math.floor(facing)) meta_fields.facing = math.floor(facing)
-- wallmounted facings only run from 0-5
end end
else else
meta:set_int("facing", math.floor(facing)) meta_fields.facing = math.floor(facing)
end end
else else
facing = meta:get_int("facing") facing = tonumber(meta_fields.facing) or 0
end end
local extrusion = tonumber(fields.extrusion) local extrusion = tonumber(fields.extrusion)
if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then if extrusion and extrusion > 0 and extrusion <= digtron.config.maximum_extrusion then
meta:set_int("extrusion", math.floor(extrusion)) meta_fields.extrusion = math.floor(extrusion)
else else
extrusion = meta:get_int("extrusion") extrusion = tonumber(meta_fields.extrusion) or 1
end end
if fields.set then if fields.set then
--digtron.show_offset_markers(pos, offset, period) --TODO digtron.show_offset_markers(pos, offset, period)
end end
if fields.read then if fields.read then
@ -202,10 +235,9 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
target_name = digtron.builder_read_item_substitutions[target_name] target_name = digtron.builder_read_item_substitutions[target_name]
end end
if target_name ~= "air" and is_item_allowed(target_name) then if target_name ~= "air" and is_item_allowed(target_name) then
local meta = minetest.get_meta(pos)
item = target_name item = target_name
meta:set_string("item", item) meta_fields.item = item
meta:set_int("facing", target_node.param2) meta_fields.facing = target_node.param2
end end
end end
@ -214,14 +246,21 @@ minetest.register_on_player_receive_fields(function(sender, formname, fields)
end end
local item_def = minetest.registered_items[item] local item_def = minetest.registered_items[item]
local item_desc = "Nothing" local item_desc = S("Nothing")
if item_def then if item_def then
item_desc = item_def.description item_desc = item_def.description
end end
meta:set_string("infotext", S("Builder for @1\nperiod @2, offset @3, extrusion @4", item_desc, period, offset, extrusion)) meta_fields.infotext = S("Builder for @1\nperiod @2, offset @3, extrusion @4", item_desc, period, offset, extrusion)
if layout then
digtron.set_layout(digtron_id, layout)
end
meta_fields.digtron_id = digtron_id
meta_fields.digtron_layout_node_id = digtron_layout_id
meta:from_table(meta_table)
digtron.update_builder_item(pos) digtron.update_builder_item(pos)
minetest.show_formspec(player_name, "digtron:builder", get_formspec(pos)) minetest.show_formspec(player_name, "digtron:builder", get_formspec(meta_fields))
end) end)

@ -1,5 +1,6 @@
local S = digtron.S 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 get_manifest = function(pos)
local manifest = {} local manifest = {}
local meta = minetest.get_meta(pos) local meta = minetest.get_meta(pos)
@ -9,9 +10,11 @@ local get_manifest = function(pos)
local digtron_id = stack_meta:get_string("digtron_id") local digtron_id = stack_meta:get_string("digtron_id")
if digtron_id ~= "" then if digtron_id ~= "" then
local layout = digtron.get_layout(digtron_id) local layout = digtron.get_layout(digtron_id)
for hash, data in pairs(layout) do for layout_node_id, data in pairs(layout) do
local item = data.node.name local item = data.node.name
local item_def = minetest.registered_items[item] 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 if item_def._digtron_disassembled_node then
item = item_def._digtron_disassembled_node item = item_def._digtron_disassembled_node
item_def = minetest.registered_items[item] item_def = minetest.registered_items[item]