hyperloop/tube.lua
2017-07-30 15:07:36 +02:00

281 lines
7.8 KiB
Lua

--[[
Hyperloop Mod
=============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
-- Tube chain arrangement:
-- [0] = tube0 (single node)
-- [1] = tube1 (head node)
-- [2] = tube2 (link node)
-- [J] = junction
--
-- [0] [1]-[1] [1]-[2]-[1] [J]-[1]-[2]-...-[2]-[1]-[J]
-- Scan all 8 neighbor positions for tube nodes.
-- Return:
-- 0, nodes - no node available
-- 1, nodes - one head/single node available
-- 3, nodes - two head nodes available
-- 4, nodes - one link node available
-- 5, nodes - invalid position
-- 12, nodes - two link nodes available
function hyperloop.scan_neighbours(pos)
local nodes = {}
local node, npos, idx
local res = 0
for _,dir in ipairs(hyperloop.NeighborPos) do
npos = vector.add(pos, dir)
node = minetest.get_node(npos)
if string.find(node.name, "hyperloop:tube") then
node.pos = npos
table.insert(nodes, node)
-- tubes on invalid level?
if not hyperloop.free_tube_placement_enabled and npos.y ~= pos.y then
return 5, nodes
end
idx = string.byte(node.name, -1) - 48
if idx == 0 then -- single node?
idx = 1
elseif idx == 2 then -- link node?
idx = 4
end
res = res * 2 + idx
end
end
if res > 4 and res ~= 12 then
res = 5
end
return res, nodes
end
-- update head tube meta data
-- param pos: position as string
-- param peer_pos: position as string
function hyperloop.update_head_node(pos, peer_pos)
if hyperloop.debugging then
print("update head node(pos="..pos.." peer_pos="..peer_pos..")")
end
pos = minetest.string_to_pos(pos)
if pos ~= nil then
local meta = minetest.get_meta(pos)
meta:set_string("peer", peer_pos)
meta:set_string("infotext", peer_pos)
hyperloop.update_junction(pos)
end
end
-- Degrade one node.
-- Needed when a new node is placed nearby.
function hyperloop.degrade_tupe_node(node, new_node_pos)
if node.name == "hyperloop:tube0" then
node.name = "hyperloop:tube1"
elseif node.name == "hyperloop:tube1" then
node.name = "hyperloop:tube2"
node.diggable = false
minetest.get_meta(node.pos):from_table(nil)
else
return
end
-- determine the correct tube facedir
local dir = vector.subtract(node.pos, new_node_pos)
node.param2 = minetest.dir_to_facedir(dir)
minetest.swap_node(node.pos, node)
end
-- Remove the given node
function hyperloop.remove_node(pos, node)
-- can't call "remove_node(pos)" because subsequently "on_destruct" will be called
node.name = "air"
node.diggable = true
minetest.swap_node(pos, node)
end
-- Upgrade the tube and add meta data
-- param node: node to be replaced
-- param peer_pos: peer node position as string
function hyperloop.swap_tube_node(node, peer_pos)
local pos = minetest.pos_to_string(node.pos)
hyperloop.update_head_node(pos, peer_pos)
hyperloop.update_head_node(peer_pos, pos)
-- upgrade
node.diggable = true
if node.name == "hyperloop:tube2" then -- 2 connections?
node.name = "hyperloop:tube1"
elseif node.name == "hyperloop:tube1" then -- 1 connection?
node.name = "hyperloop:tube0"
end
minetest.get_meta(node.pos):set_string("infotext", peer_pos)
minetest.swap_node(node.pos, node)
end
-- Upgrade one node.
-- Needed when a tube node is digged.
function hyperloop.upgrade_node(digged_node_pos)
local res, nodes = hyperloop.scan_neighbours(digged_node_pos)
if res == 1 or res == 4 then
local new_head_node = nodes[1]
-- copy peer pos first
local peer_pos = minetest.get_meta(digged_node_pos):get_string("peer")
hyperloop.swap_tube_node(new_head_node, peer_pos)
end
end
-- Place a node without neighbours
local function single_node(node)
local meta = minetest.get_meta(node.pos)
local str_pos = minetest.pos_to_string(node.pos)
meta:set_string("peer", str_pos)
minetest.get_meta(node.pos):set_string("infotext", str_pos)
-- upgrade self to single node
node.name = "hyperloop:tube0"
minetest.swap_node(node.pos, node)
return true
end
-- Place a node with one neighbor
local function head_node(node, old_head)
-- determine peer pos
local peer_pos = minetest.get_meta(old_head.pos):get_string("peer")
-- update self
local str_pos = minetest.pos_to_string(node.pos)
hyperloop.update_head_node(str_pos, peer_pos)
-- update peer
hyperloop.update_head_node(peer_pos, str_pos)
-- upgrade self
minetest.get_meta(node.pos):set_string("infotext", peer_pos)
node.name = "hyperloop:tube1"
-- determine the correct tube facedir
local dir = vector.subtract(node.pos, old_head.pos)
node.param2 = minetest.dir_to_facedir(dir)
minetest.swap_node(node.pos, node)
-- degrade old head
hyperloop.degrade_tupe_node(old_head, node.pos)
return true
end
local function link_node(node, node1, node2)
-- both nodes on the same level?
if node1.pos.y == node2.pos.y then
-- determine the meta data from both head nodes
local pos1 = minetest.get_meta(node1.pos):get_string("peer")
local pos2 = minetest.get_meta(node2.pos):get_string("peer")
if minetest.pos_to_string(node1.pos) == pos2 then -- closed tube ring?
return false
end
-- exchange position data
hyperloop.update_head_node(pos1, pos2)
hyperloop.update_head_node(pos2, pos1)
-- set to tube2
node.name = "hyperloop:tube2"
node.diggable = true
minetest.swap_node(node.pos, node)
-- degrade both nodes
hyperloop.degrade_tupe_node(node1, node.pos)
hyperloop.degrade_tupe_node(node2, node.pos)
return true
end
return false
end
-- called when a new node is placed
local function node_placed(pos, itemstack, placer)
local res, nodes = hyperloop.scan_neighbours(pos)
local node = minetest.get_node(pos)
local placed = false
node.pos = pos
if res == 0 then -- no neighbor available?
hyperloop.check_network_level(node.pos, placer)
placed = single_node(node)
elseif res == 1 then -- one neighbor available?
placed = head_node(node, nodes[1])
elseif res == 3 then -- two neighbours available?
placed = link_node(node, nodes[1], nodes[2])
end
if not placed then
hyperloop.remove_node(pos, node)
return itemstack
end
hyperloop.update_junction(pos)
end
-- simple tube without logic or "memory"
minetest.register_node("hyperloop:tube2", {
description = "Hyperloop Tube",
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^[transformR90]",
"hyperloop_tube_locked.png^[transformR90]",
"hyperloop_tube_locked.png",
},
diggable = false,
paramtype2 = "facedir",
groups = {cracky=1, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
-- single-node and head-node with meta data about the peer head node position
for idx = 0,1 do
minetest.register_node("hyperloop:tube"..idx, {
description = "Hyperloop Tube",
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^[transformR90]",
"hyperloop_tube_locked.png^[transformR90]",
'hyperloop_tube_closed.png',
'hyperloop_tube_closed.png',
'hyperloop_tube_open.png',
'hyperloop_tube_open.png',
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
return node_placed(pos, itemstack, placer)
end,
on_destruct = function(pos)
hyperloop.upgrade_node(pos)
hyperloop.update_junction(pos)
end,
paramtype2 = "facedir",
groups = {cracky=2, not_in_creative_inventory=idx},
is_ground_content = false,
drop = "hyperloop:tube0",
sounds = default.node_sound_metal_defaults(),
})
end
-- for tube viaducts
minetest.register_node("hyperloop:pillar", {
description = "Hyperloop Pillar",
tiles = {"hyperloop_tube_locked.png^[transformR90]"},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -3/8, -4/8, -3/8, 3/8, 4/8, 3/8},
},
},
is_ground_content = false,
groups = {cracky = 2, stone = 2},
sounds = default.node_sound_metal_defaults(),
})
function hyperloop.after_tube_placed(pos, itemstack)
return node_placed(pos, itemstack) == nil
end