514 lines
18 KiB
Lua
514 lines
18 KiB
Lua
|
|
local dbg
|
|
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
|
|
|
|
--- Determine whether the given node is a rail.
|
|
-- @param node The node to check (which can be nil)
|
|
-- @return true if it's a rail
|
|
function is_rail(node)
|
|
if node == nil then return false end
|
|
name = node.name
|
|
if name == "default:rail" or
|
|
name == "railcarts:controlrail" or
|
|
name == "railcarts:controlrail_off" or
|
|
name == "railcarts:digicontrol" or
|
|
name == "railcarts:rail_switched" then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
--- Determine whether a railtype is 'unloaded'
|
|
-- @param railtype A railtype (as per get_rail_status)
|
|
-- @return true if unloaded
|
|
function is_unloaded(railtype)
|
|
if railtype == "unloaded" then return true end
|
|
if railtype == "unloaded-near" then return true end
|
|
return false
|
|
end
|
|
|
|
|
|
---Determine if a node is a grabber of carts
|
|
--@param node The node to check (which can be nil)
|
|
--@return True if it is
|
|
function is_grabber(node)
|
|
if node == nil then return false end
|
|
if node.name == "railcarts:autolauncher" then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
---Get rail control information coming from a node.
|
|
-- Only switched on control rails are considered, since if they're switched
|
|
-- off they should behave exactly like a normal rail.
|
|
--@param node The node to check (which can be nil)
|
|
--@param pos The position to check
|
|
--@param status The status to update.
|
|
--The control field might be added, containing any of the specifications
|
|
--as described in the README.
|
|
-- The digiline field might be set to the pos that should send a signal
|
|
function get_railcontrol(node, pos, status)
|
|
if node and (node.name == "railcarts:controlrail"
|
|
or node.name == "railcarts:digicontrol") then
|
|
local meta = minetest.get_meta(pos)
|
|
if meta then
|
|
local it = meta:get_string("infotext")
|
|
if it and it ~= "" then
|
|
status.control = it
|
|
end
|
|
end
|
|
if node.name == "railcarts:digicontrol" then
|
|
status.digiline = pos
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--- Determine if a node is a launcher
|
|
-- @param node The node (which can be nil)
|
|
-- @return True if it is
|
|
function is_launcher(node)
|
|
|
|
if node == nil then return false end
|
|
if node.name == "railcarts:launcher_on" then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Determine if a node is an autolauncher
|
|
-- @param node The node (which can be nil)
|
|
-- @return True if it is
|
|
function is_autolauncher(node)
|
|
|
|
if node == nil then return false end
|
|
if node.name == "railcarts:autolauncher" then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- Get status of rails
|
|
-- @param fppos The position to check at
|
|
-- @param direction Movement direction (can be nil, in which case some checks
|
|
-- are not possible).
|
|
-- @param speed Movement speed (only relevant if direction is not nil)
|
|
-- checks can be completed.
|
|
-- @return A table which always contains railtype. One of:
|
|
-- "inv" - invalid position
|
|
-- "unloaded" or "unloaded-near" - entering as-yet-unloaded territory
|
|
-- use is_unloaded() to check this!
|
|
-- "x", "z" - straight track in that direction (can be sloped)
|
|
-- "x+", x-", "z+", "z-" - curve (see check_curve ireturn value)
|
|
-- It can also optionally contain:
|
|
-- unloadedpos, when railtype is unloaded or unloaded-near, is the position
|
|
-- that caused the unloaded status
|
|
-- slope, if the rail is sloped, the up direction, 0-3 (and railtype can only
|
|
-- be "x" or "z"
|
|
-- eject, which if true means any player in the cart should be
|
|
-- grab, which if it exists is the position of a node which should 'grab' the
|
|
-- cart into its inventory (i.e. an autolauncher)
|
|
-- launch, which is a launch direction
|
|
-- autolaunch, which is an autolaunch direction
|
|
-- control, usually nil, otherwise control information coming from the current
|
|
-- rail - values as per get_controlrail()
|
|
-- onautolauncher, which if true means there is an autolauncher below the rails
|
|
-- detectors, if present, is the pos for a cart detector to trigger
|
|
-- hopper, if present, is the pos for a hopper above the cart
|
|
-- only detected when stationary
|
|
function get_railstatus(fppos, direction, speed)
|
|
|
|
if fppos == nil then
|
|
dbg.v2("get_railstatus for nil pos!?")
|
|
return {railtype="inv"}
|
|
end
|
|
|
|
-- Use rounded position for all lookups and calculations in here
|
|
pos = vector.round(fppos)
|
|
|
|
local current_node = minetest.get_node(pos)
|
|
|
|
if current_node.name == "ignore" then
|
|
return {railtype="unloaded", unloadedpos=pos}
|
|
end
|
|
|
|
if not is_rail(current_node) then
|
|
return {railtype="inv"}
|
|
end
|
|
|
|
status = {}
|
|
|
|
-- Get all surrounding nodes. This builds a little cache of all the
|
|
-- surrounding nodes we might need to look at.
|
|
-- TODO: make this lazily get positions only when requested, because
|
|
-- sometimes we don't need them all
|
|
-- TODO: make this shared across a whole step (because getrailstatus
|
|
-- can be called twice, with overlap, within a step)
|
|
-- BUT!!! digirail triggers, mid-step, can change the rails,
|
|
-- although that shouldn't matter - but think about it!
|
|
-- TODO: make it less messy!
|
|
-- TODO: we're only storing the position for reading it back to debug
|
|
-- the 'unloaded' problem, we could lose it when that's fixed
|
|
local getsur = function(name, pos, surrounds)
|
|
surrounds[name] = minetest.get_node(pos)
|
|
surrounds[name.."_pos"] = pos
|
|
end
|
|
surrounds = {}
|
|
getsur("x_prev", {x=pos.x-1,y=pos.y,z=pos.z}, surrounds)
|
|
getsur("x_next", {x=pos.x+1,y=pos.y,z=pos.z}, surrounds)
|
|
getsur("z_prev", {x=pos.x,y=pos.y,z=pos.z-1}, surrounds)
|
|
getsur("z_next", {x=pos.x,y=pos.y,z=pos.z+1}, surrounds)
|
|
getsur("x_prev_above", {x=pos.x-1,y=pos.y+1,z=pos.z}, surrounds)
|
|
getsur("x_next_above", {x=pos.x+1,y=pos.y+1,z=pos.z}, surrounds)
|
|
getsur("z_prev_above", {x=pos.x,y=pos.y+1,z=pos.z-1}, surrounds)
|
|
getsur("z_next_above", {x=pos.x,y=pos.y+1,z=pos.z+1}, surrounds)
|
|
getsur("x_prev_below", {x=pos.x-1,y=pos.y-1,z=pos.z}, surrounds)
|
|
getsur("x_next_below", {x=pos.x+1,y=pos.y-1,z=pos.z}, surrounds)
|
|
getsur("z_prev_below", {x=pos.x,y=pos.y-1,z=pos.z-1}, surrounds)
|
|
getsur("z_next_below", {x=pos.x,y=pos.y-1,z=pos.z+1}, surrounds)
|
|
getsur("below", {x=pos.x,y=pos.y-1,z=pos.z}, surrounds)
|
|
getsur("above", {x=pos.x,y=pos.y+1,z=pos.z}, surrounds)
|
|
|
|
-- We need to know what all the surrounding blocks are to be able to move
|
|
-- properly (e.g. consider a curve or switch on a block boundary) so if
|
|
-- we don't have that information we need to wait.
|
|
for k, nn in pairs(surrounds) do
|
|
if string.sub(k, -4) ~= "_pos" then
|
|
if nn.name == "ignore" then
|
|
return {railtype="unloaded-near", unloadedpos=surrounds[k.."_pos"]}
|
|
end
|
|
end
|
|
end
|
|
|
|
get_railcontrol(current_node, pos, status)
|
|
|
|
local railtype
|
|
|
|
-- Check for slopes. The order of these is the same as the order of
|
|
-- priority used by the rail drawing code (as determined by experimenation)
|
|
if is_rail(surrounds.x_next_above) then
|
|
railtype = "x"
|
|
status.slope=1
|
|
elseif is_rail(surrounds.x_prev_above) then
|
|
railtype = "x"
|
|
status.slope=3
|
|
elseif is_rail(surrounds.z_prev_above) then
|
|
railtype = "z"
|
|
status.slope=2
|
|
elseif is_rail(surrounds.z_next_above) then
|
|
railtype = "z"
|
|
status.slope=0
|
|
end
|
|
|
|
-- Check for a crossing
|
|
if not railtype then
|
|
railtype = check_crossing(direction, surrounds)
|
|
end
|
|
|
|
-- Check for a switch
|
|
if (not railtype) and direction then
|
|
railtype = check_switch(current_node, pos, direction, surrounds)
|
|
end
|
|
|
|
-- Check for a curve
|
|
if not railtype then
|
|
railtype = check_curve(surrounds)
|
|
end
|
|
|
|
-- There can only be straight rails left
|
|
if (not railtype) and (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below) or
|
|
is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
|
railtype = "x"
|
|
end
|
|
|
|
if (not railtype) and (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below) or
|
|
is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) then
|
|
railtype = "z"
|
|
end
|
|
|
|
if not railtype then
|
|
railtype = "inv"
|
|
else
|
|
|
|
-- Check for hoppers
|
|
if is_hopper(surrounds.above) then
|
|
status.hopper = {x=pos.x,y=pos.y+1,z=pos.z}
|
|
end
|
|
|
|
if direction then
|
|
-- Check if a player should be ejected
|
|
if speed > 0 then
|
|
if direction == 1 and is_ejector(surrounds.x_next_above) and is_rail(surrounds.x_next) then
|
|
status.eject = true
|
|
elseif direction == 3 and is_ejector(surrounds.x_prev_above) and is_rail(surrounds.x_prev) then
|
|
status.eject = true
|
|
elseif direction == 0 and is_ejector(surrounds.z_next_above) and is_rail(surrounds.z_next) then
|
|
status.eject = true
|
|
elseif direction == 2 and is_ejector(surrounds.z_prev_above) and is_rail(surrounds.z_prev) then
|
|
status.eject = true
|
|
end
|
|
|
|
-- Check for detectors - below the cart, or to the left
|
|
local detpos
|
|
if is_detector(surrounds.below) then
|
|
detpos = {x=pos.x,y=pos.y-1,z=pos.z}
|
|
else
|
|
if direction == 0 and is_detector(surrounds.x_prev) then
|
|
detpos = {x=pos.x-1,y=pos.y,z=pos.z}
|
|
elseif direction == 1 and is_detector(surrounds.z_next) then
|
|
detpos = {x=pos.x,y=pos.y,z=pos.z+1}
|
|
elseif direction == 2 and is_detector(surrounds.x_next) then
|
|
detpos = {x=pos.x+1,y=pos.y,z=pos.z}
|
|
elseif direction == 1 and is_detector(surrounds.z_prev) then
|
|
detpos = {x=pos.x,y=pos.y,z=pos.z-1}
|
|
end
|
|
end
|
|
if detpos then
|
|
status.detector = detpos
|
|
end
|
|
end
|
|
|
|
if speed == 0 then
|
|
|
|
-- Check if the cart should be grabbed
|
|
if is_grabber(surrounds.x_next_above) then
|
|
status.grab = {x=pos.x+1,y=pos.y+1,z=pos.z}
|
|
elseif is_grabber(surrounds.x_prev_above) then
|
|
status.grab = {x=pos.x-1,y=pos.y+1,z=pos.z}
|
|
elseif is_grabber(surrounds.z_prev_above) then
|
|
status.grab = {x=pos.x,y=pos.y+1,z=pos.z-1}
|
|
elseif is_grabber(surrounds.z_next_above) then
|
|
status.grab = {x=pos.x,y=pos.y+1,z=pos.z+1}
|
|
elseif is_grabber(surrounds.below) then
|
|
status.grab = {x=pos.x,y=pos.y-1,z=pos.z}
|
|
end
|
|
|
|
-- Check for being next to a launcher (only stationary carts)
|
|
check_launcher(surrounds, status)
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
if surrounds.below and surrounds.below.name == "railcarts:autolauncher" then
|
|
status.onautolauncher = true
|
|
end
|
|
|
|
status.railtype = railtype
|
|
return status
|
|
end
|
|
|
|
|
|
--- Check if the given node should cause player ejection.
|
|
-- This is currently any walkable node.
|
|
-- @param node The node to check (can be nil)
|
|
function is_ejector(node)
|
|
if not node then return false end
|
|
nd = minetest.registered_nodes[node.name]
|
|
return nd.walkable
|
|
end
|
|
|
|
--- Check if the given node is a cart detector.
|
|
-- Only unactivated detectors are relevant.
|
|
-- @param node The node to check (can be nil)
|
|
function is_detector(node)
|
|
if not node then return false end
|
|
return node.name == "railcarts:cart_detector"
|
|
end
|
|
|
|
--- Check if the given node is a hopper.
|
|
-- @param node The node to check (can be nil)
|
|
function is_hopper(node)
|
|
if not node then return false end
|
|
return node.name == "railcarts:hopper"
|
|
end
|
|
|
|
--- Check for a launcher adjacent to a position, with a rail in the other
|
|
-- direction.
|
|
-- @param surrounds The surrounding nodes
|
|
-- @param result, into which launch and autolaunch fields are inserted if
|
|
-- necessary
|
|
function check_launcher(surrounds, result)
|
|
|
|
if is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below) then
|
|
if is_launcher(surrounds.x_next) then result.launch = 3 end
|
|
if is_autolauncher(surrounds.x_next) then result.autolaunch = 3 end
|
|
end
|
|
|
|
if is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below) then
|
|
if is_launcher(surrounds.x_prev) then result.launch = 1 end
|
|
if is_autolauncher(surrounds.x_prev) then result.autolaunch = 1 end
|
|
end
|
|
|
|
if is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below) then
|
|
if is_launcher(surrounds.z_next) then result.launch = 2 end
|
|
if is_autolauncher(surrounds.z_next) then result.autolaunch = 2 end
|
|
end
|
|
|
|
if is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below) then
|
|
if is_launcher(surrounds.z_prev) then result.launch = 0 end
|
|
if is_autolauncher(surrounds.z_prev) then result.autolaunch = 0 end
|
|
end
|
|
|
|
end
|
|
|
|
|
|
--- Check for a crossing at the given location.
|
|
-- @param direction The cart direction
|
|
-- @param surrounds The surrounding nodes
|
|
--
|
|
-- @return If there's no corssing at the current position, nil is returned.
|
|
-- Otherwise, "x" or "z" is returned.
|
|
function check_crossing(direction, surrounds)
|
|
|
|
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
|
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
|
(is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
|
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
|
if direction == 1 or direction == 3 then
|
|
return "x"
|
|
else
|
|
return "z"
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
--- Check for a switch at the given location.
|
|
-- @param node The node to check at
|
|
-- @param pos The position of the node
|
|
-- @param direction The cart direction
|
|
-- @param surrounds The surrounding nodes
|
|
--
|
|
-- @return If there's no switch at the current position, nil is returned.
|
|
-- Otherwise, "x" or "z" is returned for a switch which is in a straight
|
|
-- configuration (according to the current direction), and for a curve
|
|
-- one of the "x+", "x-", "z+" or "z-" curve designations, as returned
|
|
-- by check_curve.
|
|
function check_switch(node, pos, direction, surrounds)
|
|
|
|
-- junction : Z-, Z+, X+
|
|
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
|
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
|
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
|
|
|
if node.name == "railcarts:rail_switched" then
|
|
if direction == 0 then
|
|
return "z"
|
|
else
|
|
return "z+"
|
|
end
|
|
else
|
|
if direction == 2 then
|
|
return "z"
|
|
else
|
|
return "x+"
|
|
end
|
|
end
|
|
end
|
|
|
|
-- junction : Z-, Z+, X-
|
|
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
|
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
|
(is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) then
|
|
|
|
if node.name == "railcarts:rail_switched" then
|
|
if direction == 2 then
|
|
return "z"
|
|
else
|
|
return "x-"
|
|
end
|
|
else
|
|
if direction == 0 then
|
|
return "z"
|
|
else
|
|
return "z-"
|
|
end
|
|
end
|
|
end
|
|
|
|
-- junction : X-, X+, Z-
|
|
if (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
|
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) and
|
|
(is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) then
|
|
if node.name == "railcarts:rail_switched" then
|
|
if direction == 1 then
|
|
return "x"
|
|
else
|
|
return "x+"
|
|
end
|
|
else
|
|
if direction == 3 then
|
|
return "x"
|
|
else
|
|
return "x-"
|
|
end
|
|
end
|
|
end
|
|
|
|
-- junction : X-, X+, Z-
|
|
if (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
|
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) and
|
|
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) then
|
|
if node.name == "railcarts:rail_switched" then
|
|
if direction == 3 then
|
|
return "x"
|
|
else
|
|
return "z-"
|
|
end
|
|
else
|
|
if direction == 1 then
|
|
return "x"
|
|
else
|
|
return "z+"
|
|
end
|
|
end
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
|
|
--- Check if the given railtype is a curve
|
|
-- @param railtype A railtype (see get_rail_status)
|
|
-- @return True if it's a curve
|
|
function is_curve(railtype)
|
|
if railtype == "x+" then return true end
|
|
if railtype == "x-" then return true end
|
|
if railtype == "z+" then return true end
|
|
if railtype == "z-" then return true end
|
|
return false
|
|
end
|
|
|
|
--- Check for a curve at the given position.
|
|
-- @param surrounds The surrounding nodes
|
|
-- @return Nil if there is no curve, otherwise one of four curve designations.
|
|
-- "x+" has rails at x+1 and z-1, "x-" has rails at x-1 and z-1, "z+" has rails
|
|
-- at x+1 and z+1, and "z-" has nodes at x-1 and z+1.
|
|
function check_curve(surrounds)
|
|
|
|
if (is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) and
|
|
(is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) then
|
|
return "x+"
|
|
end
|
|
|
|
if (is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) and
|
|
(is_rail(surrounds.z_prev) or is_rail(surrounds.z_prev_below)) then
|
|
return "x-"
|
|
end
|
|
|
|
if (is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) and
|
|
(is_rail(surrounds.x_prev) or is_rail(surrounds.x_prev_below)) then
|
|
return "z-"
|
|
end
|
|
|
|
if (is_rail(surrounds.x_next) or is_rail(surrounds.x_next_below)) and
|
|
(is_rail(surrounds.z_next) or is_rail(surrounds.z_next_below)) then
|
|
return "z+"
|
|
end
|
|
return nil
|
|
end
|
|
|