2019-08-18 07:12:12 +02:00
local mod_meta = minetest.get_mod_storage ( )
2019-08-20 08:54:18 +02:00
local cache = { }
2019-08-18 21:02:42 +02:00
2019-08-20 04:02:16 +02:00
--minetest.debug(dump(mod_meta:to_table()))
2019-08-19 07:11:24 +02:00
-- Wipes mod_meta
--for field, value in pairs(mod_meta:to_table().fields) do
-- mod_meta:set_string(field, "")
--end
2019-08-18 10:23:08 +02:00
------------------------------------------------------------------------------------
-- Inventory
2019-08-18 23:29:15 +02:00
-- indexed by digtron_id, set to true whenever the detached inventory's contents change
2019-08-18 10:23:08 +02:00
local dirty_inventories = { }
2019-08-18 07:12:12 +02:00
local detached_inventory_callbacks = {
2019-08-18 21:02:42 +02:00
-- Called when a player wants to move items inside the inventory.
-- Return value: number of items allowed to move.
allow_move = function ( inv , from_list , from_index , to_list , to_index , count , player )
--allow anything in "main"
if to_list == " main " then
return count
end
--only allow fuel items in "fuel"
local stack = inv : get_stack ( from_list , from_index )
if minetest.get_craft_result ( { method = " fuel " , width = 1 , items = { stack } } ) . time ~= 0 then
return stack : get_count ( )
end
return 0
end ,
-- Called when a player wants to put something into the inventory.
-- Return value: number of items allowed to put.
-- Return value -1: Allow and don't modify item count in inventory.
allow_put = function ( inv , listname , index , stack , player )
-- Only allow fuel items to be placed in fuel
if listname == " fuel " then
2019-08-18 07:12:12 +02:00
if minetest.get_craft_result ( { method = " fuel " , width = 1 , items = { stack } } ) . time ~= 0 then
return stack : get_count ( )
2019-08-18 21:02:42 +02:00
else
return 0
2019-08-18 07:12:12 +02:00
end
2019-08-18 21:02:42 +02:00
end
return stack : get_count ( ) -- otherwise, allow all drops
end ,
2019-08-18 07:12:12 +02:00
2019-08-18 21:02:42 +02:00
-- Called when a player wants to take something out of the inventory.
-- Return value: number of items allowed to take.
-- Return value -1: Allow and don't modify item count in inventory.
allow_take = function ( inv , listname , index , stack , player )
return stack : get_count ( )
end ,
2019-08-18 07:12:12 +02:00
2019-08-18 21:02:42 +02:00
-- Called after the actual action has happened, according to what was
-- allowed.
-- No return value.
on_move = function ( inv , from_list , from_index , to_list , to_index , count , player )
dirty_inventories [ inv : get_location ( ) . name ] = true
end ,
on_put = function ( inv , listname , index , stack , player )
dirty_inventories [ inv : get_location ( ) . name ] = true
end ,
on_take = function ( inv , listname , index , stack , player )
dirty_inventories [ inv : get_location ( ) . name ] = true
end ,
}
2019-08-18 07:12:12 +02:00
2019-08-18 10:23:08 +02:00
-- If the detached inventory doesn't exist, reads saved metadata version of the inventory and creates it
-- Doesn't do anything if the detached inventory already exists, the detached inventory is authoritative
2019-08-18 23:29:15 +02:00
digtron.retrieve_inventory = function ( digtron_id )
local inv = minetest.get_inventory ( { type = " detached " , name = digtron_id } )
2019-08-18 10:23:08 +02:00
if inv == nil then
2019-08-18 23:29:15 +02:00
inv = minetest.create_detached_inventory ( digtron_id , detached_inventory_callbacks )
2019-08-19 07:11:24 +02:00
local inv_string = mod_meta : get_string ( digtron_id .. " :inv " )
2019-08-18 10:23:08 +02:00
if inv_string ~= " " then
local inventory_table = minetest.deserialize ( inv_string )
for listname , invlist in pairs ( inventory_table ) do
inv : set_size ( listname , # invlist )
inv : set_list ( listname , invlist )
end
end
end
2019-08-18 21:02:42 +02:00
return inv
2019-08-18 10:23:08 +02:00
end
2019-08-18 21:02:42 +02:00
-- Stores contents of detached inventory as a metadata string
2019-08-18 23:29:15 +02:00
local persist_inventory = function ( digtron_id )
local inv = minetest.get_inventory ( { type = " detached " , name = digtron_id } )
2019-08-18 10:23:08 +02:00
if inv == nil then
minetest.log ( " error " , " [Digtron] persist_inventory attempted to record a nonexistent inventory "
2019-08-18 23:29:15 +02:00
.. digtron_id )
2019-08-18 10:23:08 +02:00
return
end
local lists = inv : get_lists ( )
local persist = { }
for listname , invlist in pairs ( lists ) do
local inventory = { }
for i , stack in ipairs ( invlist ) do
table.insert ( inventory , stack : to_string ( ) ) -- convert into strings for serialization
end
persist [ listname ] = inventory
end
2019-08-19 07:11:24 +02:00
mod_meta : set_string ( digtron_id .. " :inv " , minetest.serialize ( persist ) )
2019-08-18 07:12:12 +02:00
end
2019-08-18 10:23:08 +02:00
minetest.register_globalstep ( function ( dtime )
2019-08-18 23:29:15 +02:00
for digtron_id , _ in pairs ( dirty_inventories ) do
persist_inventory ( digtron_id )
dirty_inventories [ digtron_id ] = nil
2019-08-18 10:23:08 +02:00
end
end )
--------------------------------------------------------------------------------------
local create_new_id = function ( )
2019-08-20 08:29:26 +02:00
local digtron_id = " digtron " .. tostring ( math.random ( 1 , 2 ^ 21 ) ) -- TODO: use SecureRandom()
-- It's super unlikely that we'll get a collision, but what the heck - maybe something will go
-- wrong with the random number source
while mod_meta : get_string ( digtron_id .. " :layout " ) ~= " " do
digtron_id = " digtron " .. tostring ( math.random ( 1 , 2 ^ 21 ) )
end
2019-08-18 23:29:15 +02:00
local inv = minetest.create_detached_inventory ( digtron_id , detached_inventory_callbacks )
return digtron_id , inv
2019-08-18 07:12:12 +02:00
end
2019-08-20 08:29:26 +02:00
-- Deletes a Digtron record. Note: just throws everything away, this is not digtron.disassemble.
2019-08-20 08:54:18 +02:00
local dispose_callbacks = { }
2019-08-18 23:29:15 +02:00
local dispose_id = function ( digtron_id )
2019-08-20 08:54:18 +02:00
-- name doesn't bother caching
2019-08-19 07:11:24 +02:00
mod_meta : set_string ( digtron_id .. " :name " , " " )
2019-08-20 08:54:18 +02:00
-- inventory handles itself specially too
mod_meta : set_string ( digtron_id .. " :inv " , " " )
minetest.remove_detached_inventory ( digtron_id )
-- clears the cache tables
for i , func in ipairs ( dispose_callbacks ) do
func ( digtron_id )
end
2019-08-18 23:29:15 +02:00
end
--------------------------------------------------------------------------------------------
-- Name
2019-08-20 08:29:26 +02:00
-- Not bothering with a dynamic table store for names, they're just strings with no need for serialization or deserialization
2019-08-18 23:29:15 +02:00
digtron.get_name = function ( digtron_id )
2019-08-19 07:11:24 +02:00
return mod_meta : get_string ( digtron_id .. " :name " )
2019-08-18 23:29:15 +02:00
end
digtron.set_name = function ( digtron_id , digtron_name )
2019-08-20 08:29:26 +02:00
-- Don't allow a name to be set for a non-existent Digtron
if mod_meta : get ( digtron_id .. " :layout " ) then
mod_meta : set_string ( digtron_id .. " :name " , digtron_name )
end
2019-08-18 10:23:08 +02:00
end
-------------------------------------------------------------------------------------------------------
2019-08-20 08:54:18 +02:00
-- Tables stored to metadata and cached locally
local get_table_functions = function ( identifier )
cache [ identifier ] = { }
-- add a callback for dispose_id
table.insert ( dispose_callbacks , function ( digtron_id )
mod_meta : set_string ( digtron_id .. " : " .. identifier , " " )
cache [ identifier ] [ digtron_id ] = nil
end )
2019-08-18 23:29:15 +02:00
return function ( digtron_id , tbl )
2019-08-20 08:54:18 +02:00
minetest.chat_send_all ( " persisting " .. identifier .. " " .. digtron_id )
2019-08-19 07:11:24 +02:00
mod_meta : set_string ( digtron_id .. " : " .. identifier , minetest.serialize ( tbl ) )
2019-08-20 08:54:18 +02:00
cache [ identifier ] [ digtron_id ] = tbl
end ,
function ( digtron_id )
minetest.chat_send_all ( " retrieving " .. identifier .. " " .. digtron_id )
local current = cache [ identifier ] [ digtron_id ]
2019-08-18 21:02:42 +02:00
if current then
return current
end
2019-08-19 07:11:24 +02:00
local tbl_string = mod_meta : get_string ( digtron_id .. " : " .. identifier )
2019-08-18 21:02:42 +02:00
if tbl_string ~= " " then
current = minetest.deserialize ( tbl_string )
if current then
2019-08-20 08:54:18 +02:00
cache [ identifier ] [ digtron_id ] = current
2019-08-18 21:02:42 +02:00
end
return current
end
2019-08-18 10:23:08 +02:00
end
2019-08-18 07:12:12 +02:00
end
2019-08-20 08:54:18 +02:00
local persist_layout , retrieve_layout = get_table_functions ( " layout " )
local persist_adjacent , retrieve_adjacent = get_table_functions ( " adjacent " )
local persist_bounding_box , retrieve_bounding_box = get_table_functions ( " bounding_box " )
local persist_pos , retrieve_pos = get_table_functions ( " pos " )
2019-08-20 08:29:26 +02:00
digtron.get_pos = retrieve_pos
2019-08-18 07:12:12 +02:00
2019-08-20 08:54:18 +02:00
-------------------------------------------------------------------------------------------------------
-- Layout creation helpers
2019-08-18 07:12:12 +02:00
local cardinal_directions = {
{ x = 1 , y = 0 , z = 0 } ,
{ x =- 1 , y = 0 , z = 0 } ,
{ x = 0 , y = 1 , z = 0 } ,
{ x = 0 , y =- 1 , z = 0 } ,
{ x = 0 , y = 0 , z = 1 } ,
{ x = 0 , y = 0 , z =- 1 } ,
}
2019-08-19 06:41:01 +02:00
local update_bounding_box = function ( bounding_box , pos )
bounding_box.minp . x = math.min ( bounding_box.minp . x , pos.x )
bounding_box.minp . y = math.min ( bounding_box.minp . y , pos.y )
bounding_box.minp . z = math.min ( bounding_box.minp . z , pos.z )
bounding_box.maxp . x = math.max ( bounding_box.maxp . x , pos.x )
bounding_box.maxp . y = math.max ( bounding_box.maxp . y , pos.y )
bounding_box.maxp . z = math.max ( bounding_box.maxp . z , pos.z )
end
2019-08-18 07:12:12 +02:00
-- recursive function searches out all connected unassigned digtron nodes
local get_all_adjacent_digtron_nodes
2019-08-19 06:41:01 +02:00
get_all_adjacent_digtron_nodes = function ( pos , digtron_nodes , digtron_adjacent , bounding_box , player_name )
2019-08-18 07:12:12 +02:00
for _ , dir in ipairs ( cardinal_directions ) do
local test_pos = vector.add ( pos , dir )
local test_hash = minetest.hash_node_position ( test_pos )
2019-08-18 21:02:42 +02:00
if not ( digtron_nodes [ test_hash ] or digtron_adjacent [ test_hash ] ) then -- don't test twice
2019-08-18 07:12:12 +02:00
local test_node = minetest.get_node ( test_pos )
local group_value = minetest.get_item_group ( test_node.name , " digtron " )
if group_value > 0 then
local meta = minetest.get_meta ( test_pos )
if meta : contains ( " digtron_id " ) then
-- Node is part of an existing digtron, don't incorporate it
2019-08-18 21:02:42 +02:00
digtron_adjacent [ test_hash ] = true
2019-08-18 07:12:12 +02:00
--elseif TODO test for protected node status using player_name
else
--test_node.group_value = group_value -- for later ease of reference
digtron_nodes [ test_hash ] = test_node
2019-08-19 06:41:01 +02:00
update_bounding_box ( bounding_box , test_pos )
get_all_adjacent_digtron_nodes ( test_pos , digtron_nodes , digtron_adjacent , bounding_box , player_name ) -- recurse
2019-08-18 07:12:12 +02:00
end
else
-- don't record details, the content of this node will change as the digtron moves
2019-08-18 21:02:42 +02:00
digtron_adjacent [ test_hash ] = true
2019-08-18 07:12:12 +02:00
end
end
end
end
2019-08-18 21:02:42 +02:00
--------------------------------------------------------------------------------------------------------
2019-08-20 08:29:26 +02:00
-- assemble and disassemble
2019-08-18 21:02:42 +02:00
local origin_hash = minetest.hash_node_position ( { x = 0 , y = 0 , z = 0 } )
2019-08-18 07:12:12 +02:00
-- Returns the id of the new Digtron record, or nil on failure
2019-08-20 08:29:26 +02:00
digtron.assemble = function ( root_pos , player_name )
2019-08-18 23:29:15 +02:00
local node = minetest.get_node ( root_pos )
2019-08-20 04:02:16 +02:00
-- TODO: a more generic test? Not needed with the more generic controller design, as far as I can tell. There's only going to be the one type of controller.
2019-08-18 07:12:12 +02:00
if node.name ~= " digtron:controller " then
-- Called on an incorrect node
2019-08-20 08:29:26 +02:00
minetest.log ( " error " , " [Digtron] digtron.assemble called with pos " .. minetest.pos_to_string ( root_pos )
2019-08-18 07:12:12 +02:00
.. " but the node at this location was " .. node.name )
return nil
end
2019-08-18 23:29:15 +02:00
local root_meta = minetest.get_meta ( root_pos )
if root_meta : contains ( " digtron_id " ) then
2019-08-20 08:29:26 +02:00
-- Already assembled. TODO: validate that the digtron_id actually exists as well
minetest.log ( " error " , " [Digtron] digtron.assemble called with pos " .. minetest.pos_to_string ( root_pos )
.. " but the controller at this location was already part of a assembled Digtron. " )
2019-08-18 07:12:12 +02:00
return nil
end
2019-08-18 23:29:15 +02:00
local root_hash = minetest.hash_node_position ( root_pos )
2019-08-20 04:02:16 +02:00
local digtron_nodes = { [ root_hash ] = node } -- Nodes that are part of Digtron.
-- Initialize with the controller, it won't be added by get_all_adjacent_digtron_nodes
2019-08-18 21:02:42 +02:00
local digtron_adjacent = { } -- Nodes that are adjacent to Digtron but not a part of it
2019-08-20 08:29:26 +02:00
local bounding_box = { minp = vector.new ( root_pos ) , maxp = vector.new ( root_pos ) }
2019-08-19 06:41:01 +02:00
get_all_adjacent_digtron_nodes ( root_pos , digtron_nodes , digtron_adjacent , bounding_box , player_name )
2019-08-18 07:12:12 +02:00
2019-08-18 23:29:15 +02:00
local digtron_id , digtron_inv = create_new_id ( root_pos )
2019-08-18 07:12:12 +02:00
local layout = { }
for hash , node in pairs ( digtron_nodes ) do
local relative_hash = hash - root_hash + origin_hash
2019-08-18 23:29:15 +02:00
local current_meta
2019-08-18 07:12:12 +02:00
if hash == root_hash then
2019-08-18 23:29:15 +02:00
current_meta = root_meta -- we're processing the controller, we already have a reference to its meta
2019-08-18 07:12:12 +02:00
else
2019-08-18 23:29:15 +02:00
current_meta = minetest.get_meta ( minetest.get_position_from_hash ( hash ) )
2019-08-18 07:12:12 +02:00
end
2019-08-18 23:29:15 +02:00
local current_meta_table = current_meta : to_table ( )
2019-08-18 07:12:12 +02:00
2019-08-18 23:29:15 +02:00
if current_meta_table.fields . digtron_id then
2019-08-18 07:12:12 +02:00
-- Trying to incorporate part of an existing digtron, should be impossible.
2019-08-20 08:29:26 +02:00
minetest.log ( " error " , " [Digtron] digtron.assemble tried to incorporate a Digtron node of type "
2019-08-18 07:12:12 +02:00
.. node.name .. " at " .. minetest.pos_to_string ( minetest.get_position_from_hash ( hash ) )
2019-08-18 23:29:15 +02:00
.. " that was already assigned to digtron id " .. current_meta_table.fields . digtron_id )
2019-08-18 07:12:12 +02:00
dispose_id ( digtron_id )
return nil
end
-- Process inventories specially
-- TODO Builder inventory gets turned into an itemname in a special key in the builder's meta
-- fuel and main get added to corresponding detached inventory lists
2019-08-18 23:29:15 +02:00
for listname , items in pairs ( current_meta_table.inventory ) do
2019-08-18 07:12:12 +02:00
local count = # items
-- increase the corresponding detached inventory size
digtron_inv : set_size ( listname , digtron_inv : get_size ( listname ) + count )
for _ , stack in ipairs ( items ) do
digtron_inv : add_item ( listname , stack )
end
2019-08-18 21:02:42 +02:00
-- erase actual items from stored layout metadata, the detached inventory is authoritative
-- store the inventory size so the inventory can be easily recreated
2019-08-18 23:29:15 +02:00
current_meta_table.inventory [ listname ] = # items
2019-08-18 07:12:12 +02:00
end
2019-08-18 21:02:42 +02:00
2019-08-18 07:12:12 +02:00
node.param1 = nil -- we don't care about param1, wipe it to save space
2019-08-18 23:29:15 +02:00
layout [ relative_hash ] = { meta = current_meta_table , node = node }
2019-08-18 07:12:12 +02:00
end
2019-08-19 06:41:01 +02:00
bounding_box.minp = vector.subtract ( bounding_box.minp , root_pos )
bounding_box.maxp = vector.subtract ( bounding_box.maxp , root_pos )
2019-08-18 23:51:25 +02:00
digtron.set_name ( digtron_id , root_meta : get_string ( " infotext " ) )
2019-08-18 10:23:08 +02:00
persist_inventory ( digtron_id )
persist_layout ( digtron_id , layout )
2019-08-18 21:02:42 +02:00
persist_adjacent ( digtron_id , digtron_adjacent )
2019-08-19 06:41:01 +02:00
persist_bounding_box ( digtron_id , bounding_box )
2019-08-20 08:29:26 +02:00
persist_pos ( digtron_id , root_pos )
2019-08-18 10:23:08 +02:00
-- 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.
for hash , node in pairs ( digtron_nodes ) do
2019-08-20 08:29:26 +02:00
local node_meta
2019-08-18 10:23:08 +02:00
if hash == root_hash then
2019-08-20 08:29:26 +02:00
node_meta = root_meta -- we're processing the controller, we already have a reference to its meta
2019-08-18 10:23:08 +02:00
else
2019-08-20 08:29:26 +02:00
node_meta = minetest.get_meta ( minetest.get_position_from_hash ( hash ) )
2019-08-18 10:23:08 +02:00
end
2019-08-20 08:29:26 +02:00
local inv = node_meta : get_inventory ( )
2019-08-18 10:23:08 +02:00
2019-08-18 21:02:42 +02:00
for listname , items in pairs ( inv : get_lists ( ) ) do
for i = 1 , # items do
inv : set_stack ( listname , i , ItemStack ( " " ) )
end
end
2019-08-20 08:29:26 +02:00
node_meta : set_string ( " digtron_id " , digtron_id )
node_meta : mark_as_private ( " digtron_id " )
2019-08-18 10:23:08 +02:00
end
2019-08-20 08:29:26 +02:00
minetest.log ( " action " , " Digtron " .. digtron_id .. " assembled at " .. minetest.pos_to_string ( root_pos )
.. " by " .. player_name )
2019-08-18 10:23:08 +02:00
2019-08-18 07:12:12 +02:00
return digtron_id
end
2019-08-19 02:23:51 +02:00
-- Returns pos, node, and meta for the digtron node provided the in-world node matches the layout
-- returns nil otherwise
local get_valid_data = function ( digtron_id , root_hash , hash , data , function_name )
2019-08-20 08:29:26 +02:00
local node_pos = minetest.get_position_from_hash ( hash + root_hash - origin_hash )
local node = minetest.get_node ( node_pos )
local node_meta = minetest.get_meta ( node_pos )
local target_digtron_id = node_meta : get_string ( " digtron_id " )
2019-08-19 02:23:51 +02:00
if data.node . name ~= node.name then
minetest.log ( " error " , " [Digtron] " .. function_name .. " tried interacting with one of " .. digtron_id .. " 's "
2019-08-20 08:29:26 +02:00
.. data.node . name .. " s at " .. minetest.pos_to_string ( node_pos ) .. " but the node at that location was of type "
2019-08-19 02:23:51 +02:00
.. node.name )
2019-08-20 04:02:16 +02:00
return
elseif target_digtron_id ~= digtron_id then
if target_digtron_id ~= " " then
minetest.log ( " error " , " [Digtron] " .. function_name .. " tried interacting with " .. digtron_id .. " 's "
2019-08-20 08:29:26 +02:00
.. data.node . name .. " at " .. minetest.pos_to_string ( node_pos )
2019-08-20 04:02:16 +02:00
.. " but the node at that location had a non-matching digtron_id value of \" "
.. target_digtron_id .. " \" " )
return
else
-- 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 "
2019-08-20 08:29:26 +02:00
.. data.node . name .. " at " .. minetest.pos_to_string ( node_pos )
2019-08-20 04:02:16 +02:00
.. " 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 "
.. " its metadata was not written correctly by a previous Digtron activity. " )
2019-08-20 08:29:26 +02:00
return node_pos , node , node_meta
2019-08-20 04:02:16 +02:00
end
2019-08-19 02:23:51 +02:00
end
2019-08-20 08:29:26 +02:00
return node_pos , node , node_meta
2019-08-19 02:23:51 +02:00
end
-- Turns the Digtron back into pieces
2019-08-20 08:29:26 +02:00
digtron.disassemble = function ( digtron_id , player_name )
local bbox = retrieve_bounding_box ( digtron_id )
local root_pos = retrieve_pos ( digtron_id )
2019-08-18 23:29:15 +02:00
local root_meta = minetest.get_meta ( root_pos )
2019-08-18 23:51:25 +02:00
root_meta : set_string ( " infotext " , digtron.get_name ( digtron_id ) )
2019-08-18 23:29:15 +02:00
2019-08-18 21:02:42 +02:00
local layout = retrieve_layout ( digtron_id )
local inv = digtron.retrieve_inventory ( digtron_id )
2019-08-19 07:11:24 +02:00
if not ( layout and inv ) then
2019-08-20 08:29:26 +02:00
minetest.log ( " error " , " digtron.disassemble was unable to find either layout or inventory record for " .. digtron_id
.. " , disassembly was impossible. Clearing any other remaining data for this id. " )
2019-08-19 07:11:24 +02:00
dispose_id ( digtron_id )
return
end
2019-08-18 23:29:15 +02:00
local root_hash = minetest.hash_node_position ( root_pos )
2019-08-18 10:23:08 +02:00
2019-08-18 21:02:42 +02:00
-- Write metadata and inventory to in-world node at this location
for hash , data in pairs ( layout ) do
2019-08-20 08:29:26 +02:00
local node_pos , node , node_meta = get_valid_data ( digtron_id , root_hash , hash , data , " digtron.disassemble " )
2019-08-19 02:23:51 +02:00
2019-08-20 08:29:26 +02:00
if node_pos then
local node_inv = node_meta : get_inventory ( )
2019-08-18 21:02:42 +02:00
for listname , size in pairs ( data.meta . inventory ) do
2019-08-20 08:29:26 +02:00
node_inv : set_size ( listname , size )
2019-08-18 21:02:42 +02:00
for i , itemstack in ipairs ( inv : get_list ( listname ) ) do
-- add everything, putting leftovers back in the main inventory
2019-08-20 08:29:26 +02:00
inv : set_stack ( listname , i , node_inv : add_item ( listname , itemstack ) )
2019-08-18 21:02:42 +02:00
end
end
-- TODO: special handling for builder node inventories
-- Ensure node metadata fields are all set, too
for field , value in pairs ( data.meta . fields ) do
2019-08-20 08:29:26 +02:00
node_meta : set_string ( field , value )
2019-08-18 21:02:42 +02:00
end
-- Clear digtron_id, this node is no longer part of an active digtron
2019-08-20 08:29:26 +02:00
node_meta : set_string ( " digtron_id " , " " )
2019-08-18 21:02:42 +02:00
end
end
2019-08-18 10:23:08 +02:00
dispose_id ( digtron_id )
2019-08-20 08:29:26 +02:00
return root_pos
2019-08-18 21:02:42 +02:00
end
2019-08-19 02:23:51 +02:00
-- Removes the in-world nodes of a digtron
-- Does not destroy its layout info
digtron.remove_from_world = function ( digtron_id , root_pos , player_name )
local layout = retrieve_layout ( digtron_id )
2019-08-19 07:11:24 +02:00
if not layout then
minetest.log ( " error " , " Unable to find layout record for " .. digtron_id
.. " , wiping any remaining metadata for this id to prevent corruption. Sorry! " )
local meta = minetest.get_meta ( root_pos )
meta : set_string ( " digtron_id " , " " )
dispose_id ( digtron_id )
return
end
2019-08-19 02:23:51 +02:00
local root_hash = minetest.hash_node_position ( root_pos )
local nodes_to_destroy = { }
for hash , data in pairs ( layout ) do
2019-08-20 08:29:26 +02:00
local node_pos , node , node_meta = get_valid_data ( digtron_id , root_hash , hash , data , " digtron.destroy " )
if node_pos then
table.insert ( nodes_to_destroy , node_pos )
2019-08-19 02:23:51 +02:00
end
end
-- TODO: voxelmanip might be better here?
minetest.bulk_set_node ( nodes_to_destroy , { name = " air " } )
end
2019-08-21 02:58:32 +02:00
-- Tests if a Digtron can be built at the designated location
2019-08-21 09:21:03 +02:00
--TODO implement ignore_nodes, needed for ignoring self nodes and nodes that have been flagged as dug
digtron.is_buildable_to = function ( digtron_id , root_pos , player_name , ignore_nodes , return_immediately_on_failure )
2019-08-19 02:23:51 +02:00
local layout = retrieve_layout ( digtron_id )
local root_hash = minetest.hash_node_position ( root_pos )
2019-08-21 09:21:03 +02:00
local show_anything = show_successes or show_failures
local succeeded = { }
local failed = { }
2019-08-19 02:23:51 +02:00
local permitted = true
2019-08-21 09:21:03 +02:00
2019-08-19 02:23:51 +02:00
for hash , data in pairs ( layout ) do
2019-08-20 08:29:26 +02:00
local node_pos = minetest.get_position_from_hash ( hash + root_hash - origin_hash )
local node = minetest.get_node ( node_pos )
2019-08-19 02:23:51 +02:00
local node_def = minetest.registered_nodes [ node.name ]
-- TODO: lots of testing needed here
if not ( node_def and node_def.buildable_to ) then
2019-08-21 09:21:03 +02:00
if return_immediately_on_failure then
return false -- no need to test further, don't return node positions
2019-08-21 02:58:32 +02:00
else
permitted = false
2019-08-21 09:21:03 +02:00
table.insert ( failed , node_pos )
2019-08-21 02:58:32 +02:00
end
2019-08-21 09:21:03 +02:00
elseif not return_immediately_on_failure then
table.insert ( succeeded , node_pos )
end
end
return permitted , succeeded , failed
end
digtron.show_buildable_nodes = function ( succeeded , failed )
if succeeded then
for _ , pos in ipairs ( succeeded ) do
digtron.safe_add_entity ( pos , " digtron:marker_crate_good " )
end
end
if failed then
for _ , pos in ipairs ( failed ) do
digtron.safe_add_entity ( pos , " digtron:marker_crate_bad " )
2019-08-21 02:58:32 +02:00
end
2019-08-19 02:23:51 +02:00
end
2019-08-21 02:58:32 +02:00
end
2019-08-19 02:23:51 +02:00
2019-08-21 02:58:32 +02:00
-- Places the Digtron into the world.
digtron.build_to_world = function ( digtron_id , root_pos , player_name )
local layout = retrieve_layout ( digtron_id )
local root_hash = minetest.hash_node_position ( root_pos )
2019-08-21 09:21:03 +02:00
2019-08-21 02:58:32 +02:00
-- TODO: voxelmanip might be better here, less likely than with destroy though since metadata needs to be written
for hash , data in pairs ( layout ) do
local node_pos = minetest.get_position_from_hash ( hash + root_hash - origin_hash )
minetest.set_node ( node_pos , data.node )
local meta = minetest.get_meta ( node_pos )
for field , value in pairs ( data.meta . fields ) do
meta : set_string ( field , value )
2019-08-19 02:23:51 +02:00
end
2019-08-21 02:58:32 +02:00
meta : set_string ( " digtron_id " , digtron_id )
meta : mark_as_private ( " digtron_id " )
-- Not needed - local inventories not used by active digtron, will be restored if disassembled
-- local inv = meta:get_inventory()
-- for listname, size in pairs(data.meta.inventory) do
-- inv:set_size(listname, size)
-- end
2019-08-19 02:23:51 +02:00
end
2019-08-21 02:58:32 +02:00
local bbox = retrieve_bounding_box ( digtron_id )
persist_bounding_box ( digtron_id , bbox )
persist_pos ( digtron_id , root_pos )
2019-08-19 02:23:51 +02:00
2019-08-21 02:58:32 +02:00
return true
2019-08-19 02:23:51 +02:00
end
2019-08-21 09:21:03 +02:00
digtron.move = function ( digtron_id , dest_pos , player_name )
minetest.chat_send_all ( " move attempt " )
end
2019-08-18 21:02:42 +02:00
---------------------------------------------------------------------------------
-- Misc
2019-08-20 04:56:28 +02:00
-- If the digtron node has an assigned ID and a layout for that ID exists and
-- a matching node exists in the layout then don't let it be dug.
-- TODO: add protection check?
2019-08-18 21:02:42 +02:00
digtron.can_dig = function ( pos , digger )
local meta = minetest.get_meta ( pos )
2019-08-19 07:11:24 +02:00
local digtron_id = meta : get_string ( " digtron_id " )
2019-08-20 04:56:28 +02:00
if digtron_id == " " then
return true
2019-08-18 21:02:42 +02:00
end
2019-08-20 04:56:28 +02:00
local node = minetest.get_node ( pos )
local bbox = retrieve_bounding_box ( digtron_id )
2019-08-20 08:29:26 +02:00
local root_pos = retrieve_pos ( digtron_id )
2019-08-20 04:56:28 +02:00
local layout = retrieve_layout ( digtron_id )
2019-08-20 08:29:26 +02:00
if bbox == nil or root_pos == nil or layout == nil then
2019-08-20 04:56:28 +02:00
-- Somehow, this belongs to a digtron id that's missing information that should exist in persistence.
local missing = " "
if bbox == nil then missing = missing .. " bounding_box " end
2019-08-20 08:29:26 +02:00
if root_pos == nil then missing = missing .. " root_pos " end
2019-08-20 04:56:28 +02:00
if layout == nil then missing = missing .. " layout " end
minetest.log ( " error " , " [Digtron] can_dig was called on a " .. node.name .. " at location "
.. minetest.pos_to_string ( pos ) .. " that claimed to belong to " .. digtron_id
.. " . However, layout and/or location data are missing: " .. missing )
-- May be better to do this to prevent node duplication. But we're already in bug land here so tread gently.
--minetest.remove_node(pos)
--return false
return true
end
2019-08-20 08:29:26 +02:00
local root_hash = minetest.hash_node_position ( root_pos )
2019-08-20 04:56:28 +02:00
local here_hash = minetest.hash_node_position ( pos )
local layout_hash = here_hash - root_hash + origin_hash
local layout_data = layout [ layout_hash ]
if layout_data == nil or layout_data.node == nil then
minetest.log ( " error " , " [Digtron] can_dig was called on a " .. node.name .. " at location "
.. minetest.pos_to_string ( pos ) .. " that claimed to belong to " .. digtron_id
.. " . However, the layout for that digtron_id didn't contain any corresponding node at its location. " )
return true
end
if layout_data.node . name ~= node.name or layout_data.node . param2 ~= node.param2 then
minetest.log ( " error " , " [Digtron] can_dig was called on a " .. node.name .. " with param2 "
.. node.param2 .. " at location " .. minetest.pos_to_string ( pos ) .. " that belonged to " .. digtron_id
.. " . However, the layout for that digtron_id contained a " .. layout_data.node . name
.. " with param2 " .. layout_data.node . param2 .. " at its location. " )
return true
end
-- We're part of a valid Digtron. No touchy.
return false
2019-08-18 21:02:42 +02:00
end
2019-08-20 04:02:16 +02:00
2019-08-20 04:56:28 +02:00
-- put this on all Digtron nodes. If other inventory types are added (eg, batteries)
-- update this.
2019-08-20 04:02:16 +02:00
digtron.on_blast = function ( pos , intensity )
if intensity < 1.0 then return end -- The Almighty Digtron ignores weak-ass explosions
local meta = minetest.get_meta ( pos )
local digtron_id = meta : get_string ( " digtron_id " )
if digtron_id ~= " " then
2019-08-20 08:29:26 +02:00
if not digtron.disassemble ( digtron_id , " an explosion " ) then
2019-08-20 04:02:16 +02:00
minetest.log ( " error " , " [Digtron] a digtron node at " .. minetest.pos_to_string ( pos )
.. " was hit by an explosion and had digtron_id " .. digtron_id
2019-08-20 08:29:26 +02:00
.. " but didn't have a root position recorded, so it could not be disassembled. " )
2019-08-20 04:02:16 +02:00
return
end
end
local drops = { }
default.get_inventory_drops ( pos , " main " , drops )
default.get_inventory_drops ( pos , " fuel " , drops )
local node = minetest.get_node ( pos )
table.insert ( drops , ItemStack ( node.name ) )
minetest.remove_node ( pos )
return drops
end
2019-08-20 08:29:26 +02:00
------------------------------------------------------------------------------------
-- Creative trash
-- This is wrapped in an after() call as a workaround for to https://github.com/minetest/minetest/issues/8827
if minetest.get_modpath ( " creative " ) then
minetest.after ( 1 , function ( )
if minetest.get_inventory ( { type = " detached " , name = " creative_trash " } ) then
if minetest.remove_detached_inventory ( " creative_trash " ) then
-- Create the trash field
local trash = minetest.create_detached_inventory ( " creative_trash " , {
-- Allow the stack to be placed and remove it in on_put()
-- This allows the creative inventory to restore the stack
allow_put = function ( inv , listname , index , stack , player )
return stack : get_count ( )
end ,
on_put = function ( inv , listname , index , stack , player )
local stack = inv : get_stack ( listname , index )
local stack_meta = stack : get_meta ( )
local digtron_id = stack_meta : get_string ( " digtron_id " )
if digtron_id ~= " " then
minetest.log ( " action " , player : get_player_name ( ) .. " disposed of " .. digtron_id
.. " in the creative inventory's trash receptacle. " )
dispose_id ( digtron_id )
end
inv : set_list ( listname , { } )
end ,
} )
trash : set_size ( " main " , 1 )
end
end
end )
end