2018-03-10 22:14:57 +01:00
|
|
|
--[[
|
|
|
|
|
|
|
|
Tube Library
|
|
|
|
============
|
|
|
|
|
|
|
|
Copyright (C) 2017 Joachim Stolberg
|
|
|
|
|
|
|
|
LGPLv2.1+
|
|
|
|
See LICENSE.txt for more information
|
|
|
|
|
|
|
|
History:
|
|
|
|
see init.lua
|
|
|
|
|
|
|
|
]]--
|
|
|
|
|
|
|
|
|
|
|
|
local MAX_TUBE_LENGTH = 100
|
|
|
|
|
|
|
|
local TubeTypes = {
|
2018-03-31 17:09:16 +02:00
|
|
|
0,0,0,0,0,0,1,3,1,3, -- 01-10
|
|
|
|
4,5,3,1,3,1,4,5,1,3, -- 11-20
|
|
|
|
1,3,4,5,3,1,3,1,4,5, -- 21-30
|
|
|
|
2,2,2,2,0,2,2,2,5,2, -- 31-40
|
|
|
|
5,0, -- 40-41
|
2018-03-10 22:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
local TubeFacedir = {
|
2018-03-31 17:09:16 +02:00
|
|
|
0,0,0,0,0,0,0,2,0,1, -- 01-10
|
|
|
|
2,2,2,1,3,1,3,3,0,3, -- 11-20
|
|
|
|
0,0,0,0,1,1,0,1,1,1, -- 21-30
|
|
|
|
0,0,0,0,0,0,0,0,0,0, -- 31-40
|
|
|
|
0,0, -- 40-41
|
2018-03-10 22:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
tubelib.knownNodes = {
|
|
|
|
["tubelib:tube1"] = true,
|
|
|
|
["tubelib:tube2"] = true,
|
|
|
|
["tubelib:tube3"] = true,
|
|
|
|
["tubelib:tube4"] = true,
|
|
|
|
["tubelib:tube5"] = true,
|
|
|
|
["tubelib:tube6"] = true,
|
|
|
|
["default:chest_locked"] = true,
|
|
|
|
["default:chest"] = true,
|
|
|
|
["default:furnace"] = true,
|
|
|
|
["default:furnace_active"] = true,
|
|
|
|
}
|
|
|
|
|
|
|
|
-- Localize functions to avoid table lookups (better performance).
|
|
|
|
local string_find = string.find
|
|
|
|
local minetest_get_meta = minetest.get_meta
|
|
|
|
local minetest_get_node = minetest.get_node
|
|
|
|
local KnownNodes = tubelib.knownNodes
|
|
|
|
local vector_add = vector.add
|
|
|
|
local vector_equals = vector.equals
|
|
|
|
|
|
|
|
-- Translate from contact side to facedir
|
|
|
|
local SideToFacedir = {B=0, R=1, F=2, L=3, D=4, U=5}
|
|
|
|
|
|
|
|
-- 6D variant of the facedir to dir conversion
|
|
|
|
local Facedir2Dir = {[0] =
|
|
|
|
{x=0, y=0, z=1},
|
|
|
|
{x=1, y=0, z=0},
|
|
|
|
{x=0, y=0, z=-1},
|
|
|
|
{x=-1, y=0, z=0},
|
|
|
|
{x=0, y=-1, z=0},
|
|
|
|
{x=0, y=1, z=0},
|
|
|
|
}
|
|
|
|
|
|
|
|
-- Determine position from and facedir to the node on the other side or the tubes.
|
|
|
|
-- Param pos: my pos
|
|
|
|
-- Param npos: neighbor tube pos
|
|
|
|
local function remote_node(pos, npos)
|
|
|
|
local dest_pos = minetest.string_to_pos(minetest.get_meta(npos):get_string("dest_pos"))
|
|
|
|
-- two possible reasons, why dest_pos == pos:
|
|
|
|
-- 1) wrong side of a single tube node
|
|
|
|
-- 2) node connected with itself. In this case "dest_pos2" is not available
|
|
|
|
if dest_pos and vector_equals(dest_pos, pos) then
|
|
|
|
local dest_pos2 = minetest.string_to_pos(minetest.get_meta(npos):get_string("dest_pos2"))
|
|
|
|
if dest_pos2 == nil then
|
|
|
|
local facedir = minetest.get_meta(npos):get_int("facedir")
|
2018-03-31 17:09:16 +02:00
|
|
|
return pos, facedir -- node connected with itself
|
2018-03-10 22:14:57 +01:00
|
|
|
else
|
|
|
|
local facedir2 = minetest.get_meta(npos):get_int("facedir2")
|
2018-03-31 17:09:16 +02:00
|
|
|
return dest_pos2, facedir2 -- one tube connection
|
2018-03-10 22:14:57 +01:00
|
|
|
end
|
|
|
|
else
|
|
|
|
local facedir = minetest.get_meta(npos):get_int("facedir")
|
2018-03-31 17:09:16 +02:00
|
|
|
return dest_pos, facedir -- multi tube connection
|
2018-03-10 22:14:57 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Calculate the facedir to the other node, based on both node positions
|
|
|
|
local function dir_to_facedir(my_pos, other_pos)
|
|
|
|
if my_pos.z ~= other_pos.z then return my_pos.z - other_pos.z + 1 end
|
|
|
|
if my_pos.x ~= other_pos.x then return my_pos.x - other_pos.x + 2 end
|
|
|
|
if my_pos.y > other_pos.y then return 5 else return 4 end
|
|
|
|
end
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- API functions
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Determine neighbor position and own facedir to the node.
|
|
|
|
-- based on own pos and contact side 'B' - 'U'.
|
|
|
|
-- Function considers also tube connections.
|
|
|
|
function tubelib.get_neighbor_pos(pos, side)
|
|
|
|
local facedir = SideToFacedir[side]
|
|
|
|
if facedir < 4 then
|
|
|
|
local node = minetest_get_node(pos)
|
|
|
|
facedir = (facedir + node.param2) % 4
|
|
|
|
end
|
|
|
|
local npos = vector_add(pos, Facedir2Dir[facedir])
|
|
|
|
local node = minetest_get_node(npos)
|
|
|
|
if node and string_find(node.name, "tubelib:tube") then
|
|
|
|
npos, facedir = remote_node(pos, npos)
|
|
|
|
end
|
|
|
|
return npos, facedir
|
|
|
|
end
|
|
|
|
|
|
|
|
-- use Voxel Manipulator to read the node
|
|
|
|
function tubelib.read_node_with_vm(pos)
|
|
|
|
local vm = VoxelManip()
|
|
|
|
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
|
|
|
local data = vm:get_data()
|
|
|
|
local param2_data = vm:get_param2_data()
|
|
|
|
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
|
|
|
return {
|
|
|
|
name=minetest.get_name_from_content_id(data[area:index(pos.x, pos.y, pos.z)]),
|
|
|
|
param2 = param2_data[area:index(pos.x, pos.y, pos.z)]
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
local read_node_with_vm = tubelib.read_node_with_vm
|
|
|
|
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- Tube placement
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
-- Return neighbor tubes orientation relative to the given pos.
|
|
|
|
local function get_neighbor_tubes_orientation(pos)
|
|
|
|
local orientation = 0
|
|
|
|
local Nodes = {
|
|
|
|
minetest.get_node({x=pos.x , y=pos.y , z=pos.z+1}),
|
|
|
|
minetest.get_node({x=pos.x+1, y=pos.y , z=pos.z }),
|
|
|
|
minetest.get_node({x=pos.x , y=pos.y , z=pos.z-1}),
|
|
|
|
minetest.get_node({x=pos.x-1, y=pos.y , z=pos.z }),
|
|
|
|
minetest.get_node({x=pos.x , y=pos.y-1, z=pos.z }),
|
|
|
|
minetest.get_node({x=pos.x , y=pos.y+1, z=pos.z }),
|
|
|
|
}
|
|
|
|
for side,node in ipairs(Nodes) do
|
|
|
|
if KnownNodes[node.name] then -- registered node?
|
|
|
|
orientation = orientation * 6 + side
|
|
|
|
if orientation > 6 then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return orientation
|
|
|
|
end
|
|
|
|
|
|
|
|
local function determine_tube_node(pos)
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
if not string.find(node.name, "tubelib:tube") then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
local orientation = get_neighbor_tubes_orientation(pos)
|
|
|
|
if orientation > 6 then
|
|
|
|
node.name = "tubelib:tube"..TubeTypes[orientation]
|
|
|
|
node.param2 = TubeFacedir[orientation]
|
|
|
|
return node
|
|
|
|
elseif orientation > 0 then
|
|
|
|
orientation = orientation * 6 + (((node.param2 + 2) % 4) + 1)
|
|
|
|
node.name = "tubelib:tube"..TubeTypes[orientation]
|
|
|
|
node.param2 = TubeFacedir[orientation]
|
|
|
|
return node
|
|
|
|
end
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function update_tube(pos)
|
|
|
|
local node = determine_tube_node(pos)
|
|
|
|
if node then
|
|
|
|
minetest.swap_node(pos, node)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local OffsTable = {
|
|
|
|
{2,0}, -- tube1
|
|
|
|
{4,5}, -- tube2
|
|
|
|
{2,3}, -- tube3
|
|
|
|
{2,4}, -- tube4
|
|
|
|
{2,5}, -- tube5
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
-- The tube on 'pos1' has two ends and thus two neighbor position.
|
|
|
|
-- One is the given 'pos', the other position is returned.
|
|
|
|
-- Param mpos: my node position
|
|
|
|
-- Param opos: the other tube position
|
|
|
|
-- Param node: the tube node
|
|
|
|
local function nodetype_to_pos(mpos, opos, node)
|
|
|
|
local idx = string.byte(node.name, -1) - 48
|
|
|
|
local facedir1 = OffsTable[idx][1]
|
|
|
|
local facedir2 = OffsTable[idx][2]
|
|
|
|
if facedir1 < 4 then
|
|
|
|
facedir1 = (facedir1 + node.param2) % 4
|
|
|
|
end
|
|
|
|
if facedir2 < 4 then
|
|
|
|
facedir2 = (facedir2 + node.param2) % 4
|
|
|
|
end
|
|
|
|
local dir1 = Facedir2Dir[facedir1]
|
|
|
|
local dir2 = Facedir2Dir[facedir2]
|
|
|
|
local p1 = vector.add(opos, dir1)
|
|
|
|
local p2 = vector.add(opos, dir2)
|
|
|
|
|
|
|
|
if mpos == nil then
|
|
|
|
return p1, p2
|
|
|
|
elseif vector.equals(p1, mpos) then
|
|
|
|
return p2
|
|
|
|
else
|
|
|
|
return p1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Walk to the other end of the tube line, starting at 'pos1'.
|
|
|
|
-- Returns: cnt - number of tube nodes
|
|
|
|
-- pos - the peer tube node
|
|
|
|
-- pos1 - the destination position, connected with 'pos'
|
|
|
|
local function walk_to_peer(pos, pos1)
|
|
|
|
local node = minetest.get_node(pos1)
|
|
|
|
local pos2
|
|
|
|
local cnt = 0
|
|
|
|
while string.find(node.name, "tubelib:tube") and cnt < MAX_TUBE_LENGTH do
|
|
|
|
pos2 = nodetype_to_pos(pos, pos1, node)
|
|
|
|
pos, pos1 = pos1, pos2
|
|
|
|
cnt = cnt + 1
|
|
|
|
node = minetest.get_node_or_nil(pos1) or read_node_with_vm(pos1)
|
|
|
|
end
|
|
|
|
return cnt, pos, pos1
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Update head tubes with peer pos and facedir of the other end
|
|
|
|
-- Needed for StackItem exchange.
|
|
|
|
local function update_head_tubes(pos)
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
if string.find(node.name, "tubelib:tube") then
|
|
|
|
local pos1, pos2 = nodetype_to_pos(nil, pos, node)
|
|
|
|
local cnt1, peer1, dest1 = walk_to_peer(pos, pos1)
|
|
|
|
local cnt2, peer2, dest2 = walk_to_peer(pos, pos2)
|
|
|
|
|
2018-03-31 17:09:16 +02:00
|
|
|
if cnt1 == 0 and cnt2 == 0 then -- first tube node placed?
|
2018-03-10 22:14:57 +01:00
|
|
|
-- we have to store both dest positions
|
|
|
|
minetest.get_meta(peer1):set_string("dest_pos", minetest.pos_to_string(dest1))
|
|
|
|
minetest.get_meta(peer1):set_int("facedir", dir_to_facedir(peer1, dest1))
|
|
|
|
minetest.get_meta(peer1):set_string("dest_pos2", minetest.pos_to_string(dest2))
|
|
|
|
minetest.get_meta(peer1):set_int("facedir2", dir_to_facedir(peer2, dest2))
|
|
|
|
else
|
|
|
|
minetest.get_meta(peer1):set_string("dest_pos", minetest.pos_to_string(dest2))
|
|
|
|
minetest.get_meta(peer1):set_int("facedir", dir_to_facedir(peer2, dest2))
|
|
|
|
minetest.get_meta(peer2):set_string("dest_pos", minetest.pos_to_string(dest1))
|
|
|
|
minetest.get_meta(peer2):set_int("facedir", dir_to_facedir(peer1, dest1))
|
|
|
|
end
|
|
|
|
|
|
|
|
-- delete meta data from old head nodes
|
|
|
|
if cnt1 > 1 then
|
|
|
|
minetest.get_meta(pos1):from_table(nil)
|
|
|
|
end
|
|
|
|
if cnt2 > 1 then
|
|
|
|
minetest.get_meta(pos2):from_table(nil)
|
|
|
|
end
|
|
|
|
return cnt1 + cnt2
|
|
|
|
end
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2018-03-31 17:09:16 +02:00
|
|
|
-- Update all tubes arround the currently placed tube
|
2018-03-10 22:14:57 +01:00
|
|
|
local function update_surrounding_tubes(pos)
|
|
|
|
update_tube({x=pos.x , y=pos.y , z=pos.z+1})
|
|
|
|
update_tube({x=pos.x+1, y=pos.y , z=pos.z })
|
|
|
|
update_tube({x=pos.x , y=pos.y , z=pos.z-1})
|
|
|
|
update_tube({x=pos.x-1, y=pos.y , z=pos.z })
|
|
|
|
update_tube({x=pos.x , y=pos.y-1, z=pos.z })
|
|
|
|
update_tube({x=pos.x , y=pos.y+1, z=pos.z })
|
|
|
|
update_tube(pos)
|
|
|
|
return update_head_tubes(pos) < MAX_TUBE_LENGTH
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Update tubes after a tube node is removed
|
|
|
|
local function after_tube_removed(pos, node)
|
|
|
|
local pos1, pos2 = nodetype_to_pos(nil, pos, node)
|
|
|
|
update_head_tubes(pos1)
|
|
|
|
update_head_tubes(pos2)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- API function, called from all nodes, which shall be connected to tubes
|
|
|
|
function tubelib.update_tubes(pos)
|
|
|
|
update_tube( {x=pos.x , y=pos.y , z=pos.z+1})
|
|
|
|
update_head_tubes({x=pos.x , y=pos.y , z=pos.z+1})
|
|
|
|
update_tube( {x=pos.x+1, y=pos.y , z=pos.z })
|
|
|
|
update_head_tubes({x=pos.x+1, y=pos.y , z=pos.z })
|
|
|
|
update_tube( {x=pos.x , y=pos.y , z=pos.z-1})
|
|
|
|
update_head_tubes({x=pos.x , y=pos.y , z=pos.z-1})
|
|
|
|
update_tube( {x=pos.x-1, y=pos.y , z=pos.z })
|
|
|
|
update_head_tubes({x=pos.x-1, y=pos.y , z=pos.z })
|
|
|
|
update_tube( {x=pos.x , y=pos.y-1, z=pos.z })
|
|
|
|
update_head_tubes({x=pos.x , y=pos.y-1, z=pos.z })
|
|
|
|
update_tube( {x=pos.x , y=pos.y+1, z=pos.z })
|
|
|
|
update_head_tubes({x=pos.x , y=pos.y+1, z=pos.z })
|
|
|
|
end
|
|
|
|
|
|
|
|
local DefNodeboxes = {
|
2018-03-31 17:09:16 +02:00
|
|
|
-- x1 y1 z1 x2 y2 z2
|
|
|
|
{ -1/4, -1/4, -1/4, 1/4, 1/4, 1/4 },
|
|
|
|
{ -1/4, -1/4, -1/4, 1/4, 1/4, 1/4 },
|
2018-03-10 22:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
local DirCorrections = {
|
2018-03-31 17:09:16 +02:00
|
|
|
{3, 6}, {2, 5}, -- standard tubes
|
|
|
|
{3, 1}, {3, 2}, {3, 5}, -- knees from front to..
|
2018-03-10 22:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
local SelectBoxes = {
|
|
|
|
{ -1/4, -1/4, -1/2, 1/4, 1/4, 1/2 },
|
|
|
|
{ -1/4, -1/2, -1/4, 1/4, 1/2, 1/4 },
|
|
|
|
{ -1/2, -1/4, -1/2, 1/4, 1/4, 1/4 },
|
|
|
|
{ -1/4, -1/2, -1/2, 1/4, 1/4, 1/4 },
|
|
|
|
{ -1/4, -1/4, -1/2, 1/4, 1/2, 1/4 },
|
|
|
|
}
|
|
|
|
|
|
|
|
local TilesData = {
|
|
|
|
-- up, down, right, left, back, front
|
|
|
|
{
|
|
|
|
"tubelib_tube.png^[transformR90",
|
|
|
|
"tubelib_tube.png^[transformR90",
|
|
|
|
"tubelib_tube.png",
|
|
|
|
"tubelib_tube.png",
|
|
|
|
"tubelib_hole.png",
|
|
|
|
"tubelib_hole.png",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"tubelib_hole.png",
|
|
|
|
"tubelib_hole.png",
|
|
|
|
"tubelib_tube.png^[transformR90",
|
|
|
|
"tubelib_tube.png^[transformR90",
|
|
|
|
"tubelib_tube.png^[transformR90",
|
|
|
|
"tubelib_tube.png^[transformR90",
|
|
|
|
},
|
2018-03-31 17:09:16 +02:00
|
|
|
{
|
|
|
|
"tubelib_knee.png^[transformR270",
|
|
|
|
"tubelib_knee.png^[transformR180",
|
|
|
|
"tubelib_knee2.png^[transformR270",
|
|
|
|
"tubelib_hole2.png^[transformR90",
|
|
|
|
"tubelib_knee2.png^[transformR90",
|
|
|
|
"tubelib_hole2.png^[transformR270",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"tubelib_knee2.png",
|
|
|
|
"tubelib_hole2.png^[transformR180",
|
|
|
|
"tubelib_knee.png^[transformR270",
|
|
|
|
"tubelib_knee.png",
|
|
|
|
"tubelib_knee2.png",
|
|
|
|
"tubelib_hole2.png",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"tubelib_hole2.png",
|
|
|
|
"tubelib_knee2.png^[transformR180",
|
|
|
|
"tubelib_knee.png^[transformR180",
|
|
|
|
"tubelib_knee.png^[transformR90",
|
|
|
|
"tubelib_knee2.png^[transformR180",
|
|
|
|
"tubelib_hole2.png^[transformR180",
|
|
|
|
},
|
2018-03-10 22:14:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for idx,pos in ipairs(DirCorrections) do
|
2018-03-31 17:09:16 +02:00
|
|
|
node_box_data = table.copy(DefNodeboxes)
|
|
|
|
node_box_data[1][pos[1]] = node_box_data[1][pos[1]] * 2
|
|
|
|
node_box_data[2][pos[2]] = node_box_data[2][pos[2]] * 2
|
2018-03-10 22:14:57 +01:00
|
|
|
|
|
|
|
tiles_data = TilesData[idx]
|
|
|
|
|
|
|
|
if idx == 1 then
|
|
|
|
hidden = 0
|
|
|
|
else
|
|
|
|
hidden = 1
|
|
|
|
end
|
2018-03-31 17:09:16 +02:00
|
|
|
minetest.register_node("tubelib:tube"..idx, {
|
|
|
|
description = "Tubelib Tube",
|
|
|
|
tiles = tiles_data,
|
|
|
|
drawtype = "nodebox",
|
|
|
|
node_box = {
|
|
|
|
type = "fixed",
|
|
|
|
fixed = node_box_data,
|
|
|
|
},
|
2018-03-10 22:14:57 +01:00
|
|
|
selection_box = {
|
|
|
|
type = "fixed",
|
|
|
|
fixed = SelectBoxes[idx],
|
|
|
|
},
|
|
|
|
collision_box = {
|
|
|
|
type = "fixed",
|
|
|
|
fixed = SelectBoxes[idx],
|
|
|
|
},
|
|
|
|
|
|
|
|
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
|
|
|
if update_surrounding_tubes(pos) == false then
|
|
|
|
after_tube_removed(pos, minetest.get_node(pos))
|
|
|
|
minetest.remove_node(pos)
|
|
|
|
return itemstack
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
|
|
|
|
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
|
|
|
after_tube_removed(pos, oldnode)
|
|
|
|
end,
|
2018-03-31 17:09:16 +02:00
|
|
|
|
|
|
|
on_rotate = screwdriver.disallow,
|
|
|
|
paramtype2 = "facedir",
|
|
|
|
paramtype = "light",
|
|
|
|
sunlight_propagates = true,
|
|
|
|
is_ground_content = false,
|
2018-03-10 22:14:57 +01:00
|
|
|
groups = {choppy=2, cracky=3, stone=1, not_in_creative_inventory=hidden},
|
|
|
|
drop = "tubelib:tube1",
|
|
|
|
sounds = default.node_sound_wood_defaults(),
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
minetest.register_craft({
|
|
|
|
output = "tubelib:tube1 4",
|
|
|
|
recipe = {
|
|
|
|
{"default:steel_ingot", "", "group:wood"},
|
|
|
|
{"", "group:wood", ""},
|
|
|
|
{"group:wood", "", "default:tin_ingot"},
|
|
|
|
},
|
|
|
|
})
|