2017-01-07 17:52:39 +01:00
digtron.get_all_digtron_neighbours = function ( pos , player )
-- returns table containing a list of all digtron node locations, lists of special digtron node types, a table of the coordinate extents of the digtron array, a Pointset of protected nodes, and a number to determine how many adjacent solid non-digtron nodes there are (for traction)
local layout = { }
--initialize. We're assuming that the start position is a controller digtron, should be a safe assumption since only the controller node should call this
layout.traction = 0
layout.all = { }
layout.inventories = { }
layout.fuelstores = { }
layout.diggers = { }
layout.builders = { }
layout.extents = { }
layout.water_touching = false
layout.lava_touching = false
layout.protected = Pointset.create ( ) -- if any nodes we look at are protected, make note of that. That way we don't need to keep re-testing protection state later.
layout.controller = { x = pos.x , y = pos.y , z = pos.z } --Make a deep copy of the pos parameter just in case the calling code wants to play silly buggers with it
table.insert ( layout.all , layout.controller )
layout.extents . max_x = pos.x
layout.extents . min_x = pos.x
layout.extents . max_y = pos.y
layout.extents . min_y = pos.y
layout.extents . max_z = pos.z
layout.extents . min_z = pos.z
-- temporary pointsets used while searching
local to_test = Pointset.create ( )
local tested = Pointset.create ( )
tested : set ( pos.x , pos.y , pos.z , true )
to_test : set ( pos.x + 1 , pos.y , pos.z , true )
to_test : set ( pos.x - 1 , pos.y , pos.z , true )
to_test : set ( pos.x , pos.y + 1 , pos.z , true )
to_test : set ( pos.x , pos.y - 1 , pos.z , true )
to_test : set ( pos.x , pos.y , pos.z + 1 , true )
to_test : set ( pos.x , pos.y , pos.z - 1 , true )
if minetest.is_protected ( pos , player : get_player_name ( ) ) and not minetest.check_player_privs ( player , " protection_bypass " ) then
layout.protected : set ( pos.x , pos.y , pos.z , true )
end
-- Do a loop on to_test positions, adding new to_test positions as we find digtron nodes. This is a flood fill operation
-- that follows node faces (no diagonals)
local testpos , _ = to_test : pop ( )
while testpos ~= nil do
tested : set ( testpos.x , testpos.y , testpos.z , true ) -- track nodes we've looked at to prevent infinite loops
local node = minetest.get_node ( testpos )
if node.name == " ignore " then
--buildtron array is next to unloaded nodes, too dangerous to do anything. Abort.
layout.all = nil
return layout
end
if minetest.is_protected ( pos , player : get_player_name ( ) ) and not minetest.check_player_privs ( player , " protection_bypass " ) then
layout.protected : set ( testpos.x , testpos.y , testpos.z , true )
end
if minetest.get_item_group ( node.name , " water " ) ~= 0 then
layout.water_touching = true
elseif minetest.get_item_group ( node.name , " lava " ) ~= 0 then
layout.lava_touching = true
if digtron.lava_impassible == true then
layout.protected : set ( testpos.x , testpos.y , testpos.z , true )
end
end
local group_number = minetest.get_item_group ( node.name , " digtron " )
if group_number > 0 then
--found one. Add it to the digtrons output
table.insert ( layout.all , testpos )
-- update extents
layout.extents . max_x = math.max ( layout.extents . max_x , testpos.x )
layout.extents . min_x = math.min ( layout.extents . min_x , testpos.x )
layout.extents . max_y = math.max ( layout.extents . max_y , testpos.y )
layout.extents . min_y = math.min ( layout.extents . min_y , testpos.y )
layout.extents . max_z = math.max ( layout.extents . max_z , testpos.z )
layout.extents . min_z = math.min ( layout.extents . min_z , testpos.z )
-- add a reference to this node's position to special node lists
if group_number == 2 then
table.insert ( layout.inventories , testpos )
elseif group_number == 3 then
table.insert ( layout.diggers , testpos )
elseif group_number == 4 then
table.insert ( layout.builders , testpos )
elseif group_number == 5 then
table.insert ( layout.fuelstores , testpos )
elseif group_number == 6 then
table.insert ( layout.inventories , testpos )
table.insert ( layout.fuelstores , testpos )
end
--queue up potential new test points adjacent to this digtron node
to_test : set_if_not_in ( tested , testpos.x + 1 , testpos.y , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x - 1 , testpos.y , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y + 1 , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y - 1 , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y , testpos.z + 1 , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y , testpos.z - 1 , true )
elseif minetest.registered_nodes [ node.name ] . buildable_to ~= true then
-- Tracks whether the digtron is hovering in mid-air. If any part of the digtron array touches something solid it gains traction.
layout.traction = layout.traction + 1
end
testpos , _ = to_test : pop ( )
end
return layout
2017-01-08 09:23:10 +01:00
end
-- Rotation magic
--------------------------------------------------------------------------------------------------------
local facedir_rotate = {
[ ' x ' ] = {
[ - 1 ] = { [ 0 ] = 4 , 5 , 6 , 7 , 22 , 23 , 20 , 21 , 0 , 1 , 2 , 3 , 13 , 14 , 15 , 12 , 19 , 16 , 17 , 18 , 10 , 11 , 8 , 9 } , -- 270 degrees
[ 1 ] = { [ 0 ] = 8 , 9 , 10 , 11 , 0 , 1 , 2 , 3 , 22 , 23 , 20 , 21 , 15 , 12 , 13 , 14 , 17 , 18 , 19 , 16 , 6 , 7 , 4 , 5 } , -- 90 degrees
} ,
[ ' y ' ] = {
[ - 1 ] = { [ 0 ] = 3 , 0 , 1 , 2 , 19 , 16 , 17 , 18 , 15 , 12 , 13 , 14 , 7 , 4 , 5 , 6 , 11 , 8 , 9 , 10 , 21 , 22 , 23 , 20 } , -- 270 degrees
[ 1 ] = { [ 0 ] = 1 , 2 , 3 , 0 , 13 , 14 , 15 , 12 , 17 , 18 , 19 , 16 , 9 , 10 , 11 , 8 , 5 , 6 , 7 , 4 , 23 , 20 , 21 , 22 } , -- 90 degrees
} ,
[ ' z ' ] = {
[ - 1 ] = { [ 0 ] = 16 , 17 , 18 , 19 , 5 , 6 , 7 , 4 , 11 , 8 , 9 , 10 , 0 , 1 , 2 , 3 , 20 , 21 , 22 , 23 , 12 , 13 , 14 , 15 } , -- 270 degrees
[ 1 ] = { [ 0 ] = 12 , 13 , 14 , 15 , 7 , 4 , 5 , 6 , 9 , 10 , 11 , 8 , 20 , 21 , 22 , 23 , 0 , 1 , 2 , 3 , 16 , 17 , 18 , 19 } , -- 90 degrees
}
}
local wallmounted_rotate = {
[ ' x ' ] = {
[ - 1 ] = { [ 0 ] = 4 , 5 , 2 , 3 , 1 , 0 } , -- 270 degrees
[ 1 ] = { [ 0 ] = 5 , 4 , 2 , 3 , 0 , 1 } , -- 90 degrees
} ,
[ ' y ' ] = {
[ - 1 ] = { [ 0 ] = 0 , 1 , 4 , 5 , 3 , 2 } , -- 270 degrees
[ 1 ] = { [ 0 ] = 0 , 1 , 5 , 4 , 2 , 3 } , -- 90 degrees
} ,
[ ' z ' ] = {
[ - 1 ] = { [ 0 ] = 3 , 2 , 0 , 1 , 4 , 5 } , -- 270 degrees
[ 1 ] = { [ 0 ] = 2 , 3 , 1 , 0 , 4 , 5 } , -- 90 degrees
}
}
--90 degrees CW about x-axis: (x, y, z) -> (x, -z, y)
--90 degrees CCW about x-axis: (x, y, z) -> (x, z, -y)
--90 degrees CW about y-axis: (x, y, z) -> (-z, y, x)
--90 degrees CCW about y-axis: (x, y, z) -> (z, y, -x)
--90 degrees CW about z-axis: (x, y, z) -> (y, -x, z)
--90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z)
local rotate_pos = function ( axis , direction , pos )
if axis == " x " then
if direction < 0 then
return { x = pos.x , y = - pos.z , z = pos.y }
else
return { x = pos.x , y = pos.z , z = - pos.y }
end
elseif axis == " y " then
if direction < 0 then
return { x = - pos.z , y = pos.y , z = pos.x }
else
return { x = pos.z , y = pos.y , z = - pos.x }
end
else
if direction < 0 then
return { x = - pos.y , y = pos.x , z = pos.z }
else
return { x = pos.y , y = - pos.x , z = pos.z }
end
end
end
local get_node_image = function ( pos , node )
local node_image = { node = node , pos = { x = pos.x , y = pos.y , z = pos.z } }
node_image.paramtype2 = minetest.registered_nodes [ node.name ] . paramtype2
local meta = minetest.get_meta ( pos )
node_image.meta = meta : to_table ( )
-- Record what kind of thing we've got in a builder node so its facing can be rotated properly
if minetest.get_item_group ( node.name , " digtron " ) == 4 then
local build_item = node_image.meta . inventory.main [ 1 ]
if build_item ~= " " then
local build_item_def = minetest.registered_nodes [ ItemStack ( build_item ) : get_name ( ) ]
node_image.build_item_paramtype2 = build_item_def.paramtype2
end
end
return node_image
end
local rotate_node_image = function ( node_image , origin , axis , direction , old_pos_pointset )
-- Facings
if node_image.paramtype2 == " wallmounted " then
node_image.node . param2 = wallmounted_rotate [ axis ] [ direction ] [ node_image.node . param2 ]
elseif node_image.paramtype2 == " facedir " then
node_image.node . param2 = facedir_rotate [ axis ] [ direction ] [ node_image.node . param2 ]
end
if node_image.build_item_paramtype2 == " wallmounted " then
node_image.meta . fields.build_facing = wallmounted_rotate [ axis ] [ direction ] [ node_image.meta . fields.build_facing ]
elseif node_image.build_item_paramtype2 == " facedir " then
node_image.meta . fields.build_facing = facedir_rotate [ axis ] [ direction ] [ node_image.meta . fields.build_facing ]
end
node_image.meta . fields.waiting = nil -- If we're rotating a controller that's in the "waiting" state, clear it. Otherwise it may stick like that.
-- record the old location so we can destroy the old node if the rotation operation is possible
old_pos_pointset : set ( node_image.pos . x , node_image.pos . y , node_image.pos . z , true )
-- position in space relative to origin
local pos = vector.subtract ( node_image.pos , origin )
pos = rotate_pos ( axis , direction , pos )
-- Move back to original reference frame
node_image.pos = vector.add ( pos , origin )
return node_image
end
digtron.rotate_layout_image = function ( layout_image , facedir )
-- To convert this into the direction the "top" of the axel node is pointing in:
-- 0, 1, 2, 3 == (0,1,0)
-- 4, 5, 6, 7 == (0,0,1)
-- 8, 9, 10, 11 == (0,0,-1)
-- 12, 13, 14, 15 == (1,0,0)
-- 16, 17, 18, 19 == (-1,0,0)
-- 20, 21, 22, 23== (0,-1,0)
local top = {
[ 0 ] = { axis = " y " , dir =- 1 } ,
{ axis = " z " , dir = 1 } ,
{ axis = " z " , dir =- 1 } ,
{ axis = " x " , dir = 1 } ,
{ axis = " x " , dir =- 1 } ,
{ axis = " y " , dir = 1 } ,
}
local params = top [ math.floor ( facedir / 4 ) ]
layout_image.old_pos_pointset = Pointset : create ( )
for k , node_image in pairs ( layout_image.all ) do
rotate_node_image ( node_image , layout_image.controller , params.axis , params.dir , layout_image.old_pos_pointset )
end
return layout_image
end
digtron.can_write_layout_image = function ( layout_image , player )
for k , node_image in pairs ( layout_image.all ) do
if not layout_image.old_pos_pointset : get ( node_image.pos . x , node_image.pos . y , node_image.pos . z )
and not minetest.registered_nodes [ minetest.get_node ( node_image.pos ) . name ] . buildable_to then
return false
elseif minetest.is_protected ( node_image.pos , player : get_player_name ( ) ) and not minetest.check_player_privs ( player , " protection_bypass " ) then
return false
end
end
return true
end
digtron.write_layout_image = function ( layout_image )
-- destroy the old digtron
local oldpos , _ = layout_image.old_pos_pointset : pop ( )
while oldpos ~= nil do
local old_def = minetest.registered_nodes [ minetest.get_node ( oldpos ) . name ]
minetest.remove_node ( oldpos )
if old_def.after_dig_node ~= nil then
old_def.after_dig_node ( oldpos )
end
oldpos , _ = layout_image.old_pos_pointset : pop ( )
end
-- create the new one
for k , node_image in pairs ( layout_image.all ) do
minetest.add_node ( node_image.pos , node_image.node )
minetest.get_meta ( node_image.pos ) : from_table ( node_image.meta )
local new_def = minetest.registered_nodes [ node_image.node . name ]
if new_def.after_place_node ~= nil then
new_def.after_place_node ( node_image.pos )
end
end
end
-- Similar to get_layout, but far more comprehensive. This produces a data structure plus a set of temporary inventories that will allow the digtron to be rotated and then recreated.
digtron.get_layout_image = function ( pos , player )
local image = { }
--initialize. We're assuming that the start position is a controller digtron, should be a safe assumption since only the controller node should call this
image.all = { }
image.extents = { }
image.controller = { x = pos.x , y = pos.y , z = pos.z } --Make a deep copy of the pos parameter just in case the calling code wants to play silly buggers with it
image.contains_protected_node = false -- used to indicate if at least one node in this digtron array is protected from the player.
table.insert ( image.all , get_node_image ( pos , minetest.get_node ( pos ) ) )
image.extents . max_x = pos.x
image.extents . min_x = pos.x
image.extents . max_y = pos.y
image.extents . min_y = pos.y
image.extents . max_z = pos.z
image.extents . min_z = pos.z
-- temporary pointsets used while searching
local to_test = Pointset.create ( )
local tested = Pointset.create ( )
tested : set ( pos.x , pos.y , pos.z , true )
to_test : set ( pos.x + 1 , pos.y , pos.z , true )
to_test : set ( pos.x - 1 , pos.y , pos.z , true )
to_test : set ( pos.x , pos.y + 1 , pos.z , true )
to_test : set ( pos.x , pos.y - 1 , pos.z , true )
to_test : set ( pos.x , pos.y , pos.z + 1 , true )
to_test : set ( pos.x , pos.y , pos.z - 1 , true )
if minetest.is_protected ( pos , player : get_player_name ( ) ) and not minetest.check_player_privs ( player , " protection_bypass " ) then
image.contains_protected_node = true
end
-- Do a loop on to_test positions, adding new to_test positions as we find digtron nodes. This is a flood fill operation
-- that follows node faces (no diagonals)
local testpos , _ = to_test : pop ( )
while testpos ~= nil do
tested : set ( testpos.x , testpos.y , testpos.z , true ) -- track nodes we've looked at to prevent infinite loops
local node = minetest.get_node ( testpos )
if node.name == " ignore " then
--buildtron array is next to unloaded nodes, too dangerous to do anything. Abort.
return nil
end
local group_number = minetest.get_item_group ( node.name , " digtron " )
if group_number > 0 then
--found one. Add it to the digtrons output
table.insert ( image.all , get_node_image ( testpos , node ) )
if minetest.is_protected ( pos , player : get_player_name ( ) ) and not minetest.check_player_privs ( player , " protection_bypass " ) then
image.contains_protected_node = true
end
-- update extents
image.extents . max_x = math.max ( image.extents . max_x , testpos.x )
image.extents . min_x = math.min ( image.extents . min_x , testpos.x )
image.extents . max_y = math.max ( image.extents . max_y , testpos.y )
image.extents . min_y = math.min ( image.extents . min_y , testpos.y )
image.extents . max_z = math.max ( image.extents . max_z , testpos.z )
image.extents . min_z = math.min ( image.extents . min_z , testpos.z )
--queue up potential new test points adjacent to this digtron node
to_test : set_if_not_in ( tested , testpos.x + 1 , testpos.y , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x - 1 , testpos.y , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y + 1 , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y - 1 , testpos.z , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y , testpos.z + 1 , true )
to_test : set_if_not_in ( tested , testpos.x , testpos.y , testpos.z - 1 , true )
end
testpos , _ = to_test : pop ( )
end
return image
end