railcarts/cartbase.lua
2015-04-20 22:32:09 +01:00

888 lines
32 KiB
Lua

local dbg
if moddebug then dbg=moddebug.dbg("railcarts") else dbg={v1=function() end,v2=function() end,v3=function() end} end
cartbase = {}
--- Set a new direction
-- This updates the passed in state, but also sets the cart's orientation and
-- modifies the linked player's view.
-- @param state The current cart state, which will be updated.
-- Only the direction component is relevant.
-- If nil, the player's direction is set directly rather
-- than amended (e.g. boarding cart)
-- @param newdir The new direction
function cartbase.setdirection(self, state, newdir)
self.object:setyaw(direction_to_yaw(newdir))
if self.linkedplayer then
if state then
local dd = newdir - state.direction
self.addplayeryaw = self.addplayeryaw - dd * (math.pi / 2)
else
self.linkedplayer:set_look_yaw(direction_to_yaw(newdir))
end
end
if state then
state.direction = newdir
else
self.direction = newdir
end
end
--- Determine if the cart is empty
-- Empty means it can be picked up, which is not the case if there's
-- someone riding in it, or cargo on board, etc.
-- @return True if it's empty
function cartbase.is_empty(self)
if self.linkedplayer ~= nil then return false end
if self.carttype == "cargo" then
if not self.inventory:is_empty("main") then return false end
elseif self.carttype == "boring" then
if not self.inventory:is_empty("main") then return false end
if not self.inventory:is_empty("materials") then return false end
end
return true
end
--- Handle step processing for a cart
--@param self The cart
--@param dtime Time since last call, in seconds
function cartbase.on_step_handler(self, dtime)
if self.skipsteps > 0 then
dbg.v3("Skipping step")
self.skipsteps = self.skipsteps -1
self.dtime_debt = self.dtime_debt + dtime
return
end
-- No need to do any processing in a wait state.
self.loadwait = self.loadwait + dtime
if self.wait > 0 then
self.wait = self.wait - dtime
if self.wait >= 0 then return end
-- Note, adding the negative remainder - we now process just the
-- portion of the step left after the wait finished.
dtime = dtime + self.wait
self.wait = 0
end
-- Add on 'dtime debt' (caused by us skipping slices on a previous
-- step)
dtime = dtime + self.dtime_debt
self.dtime_debt = 0
self.lastmove = self.lastmove + dtime
local oldpos
if self.pos then
oldpos = {x=self.pos.x, y=self.pos.y, z=self.pos.z}
end
if self.pos and self.speed > 0 then
dbg.v3("New step, dtime "..dtime.." at "..minetest.pos_to_string(self.pos))
end
-- Keep track of this over all step slices and do it once, otherwise
-- it goes wrong if it happens twice (i.e. very fast over two close
-- together curves)
self.addplayeryaw = 0
-- Maximum length of time processed in a singleslice. This is based on
-- the maximum cart speed and ensures it doesn't move more than half (or
-- exactly!) a node length in one slice call.
local max_update_time = 1
if self.speed > 0 then
max_update_time = 0.45 / self.speed
end
-- Do as many slices as we need to to keep the maximum distance moved
-- below the threshold
local nomore = false
while dtime >= max_update_time and not nomore do
nomore = cartbase.update(self, max_update_time)
dtime = dtime - max_update_time
-- We need to recalculate this, because the speed could have changed
-- during the previous slice!
if self.speed > 0 then
max_update_time = 0.45 / self.speed
else
max_update_time = 1
end
end
if dtime > 0 and not nomore then
nomore = cartbase.update(self, dtime)
dtime = 0
end
if nomore then
-- TODO - seeing if skipping an extra step resolves the mesecons
-- delay which occasionally causes rails to not be switched
-- in time
-- TODO - in fact, the switching happens at the *end* of the *next*
-- step, so we have to skip two!!
self.skipsteps = 2
end
self.dtime_debt = dtime
if self.addplayeryaw ~= 0 and self.linkedplayer then
local curyaw = self.linkedplayer:get_look_yaw()
if curyaw then
-- Correct for api insanity
curyaw = curyaw - math.pi / 2
curyaw = curyaw + self.addplayeryaw
local c = math.pi * 2
while curyaw > c do curyaw = curyaw - c end
while curyaw < 0 do curyaw = curyaw + c end
self.linkedplayer:set_look_yaw(curyaw)
end
end
-- Call setpos if the position has changed
if not oldpos or not vector.equals(self.pos, oldpos) then
self.object:setpos(self.pos)
end
end
--- Handle update for a cart.
-- @param self The cart
-- @param dtime Time since last call, in seconds, which should always be
-- low enough that the cart doesn't move >= 0.5 nodes.
-- @return True if something happened which means no further step slices should
-- be processed during this step. (Currently, this is specifically only
-- when a digiline message is sent, because a common usage of that is
-- to pass information to a luacontroller, which then uses mesecons to
-- switch a rail. This will not necessarily have happened until the next
-- server step - and hopefully by then!)
--
-- Note that although some processing is done before the cart is moved, NOTHING
-- should set the speed (and in particular, increase it) before the movement,
-- because this method is called with a dtime calculated to be safe for the
-- speed at the time of calling.
function cartbase.update(self, dtime)
local no_more_slices = false
-- A little hack to stop double-grabbing. After self.object:remove() this
-- handler can still get called, because of our step-slicing.
if self.dead then return no_more_slices end
local state = {}
if not self.pos then
self.pos = self.object:getpos()
end
state.pos = self.pos
-- state.nodepos is the rounded node position, i.e. the centre of the node box
state.nodepos = vector.round(state.pos)
state.direction = self.direction
state.speed = self.speed
state.railstatus = get_railstatus(state.pos, state.direction, state.speed)
dbg.v3("Railstatus at "..minetest.pos_to_string(state.pos)..", dir="..self.direction..", speed="..self.speed.." : "..dump(state.railstatus))
-- Stop here if we're on an unloaded block. Hopefully it will load and we can just carry on.
if is_unloaded(state.railstatus.railtype) then
if self.speed == 0 then
-- This can happen at speed 0, because we're inactive. Could have
-- been placed by an active autolauncher, near the boundary with
-- an inactive block, for example.
self.wait = 2
else
-- It shouldn't ever happen when moving though, so long as we have
-- autonomous support.
dbg.v2("Waiting for current block load ("..state.railstatus.railtype..") at "..minetest.pos_to_string(state.pos))
self.wait = 1
end
return no_more_slices
end
-- Grab the cart into something's inventory if necessary
-- (only allowed when there is no passenger or cargo)
if state.railstatus.grab and cartbase.is_empty(self) then
dbg.v1("Node "..minetest.pos_to_string(state.railstatus.grab).." grabbing "..self.carttype.." cart")
local meta = minetest.get_meta(state.railstatus.grab)
local inv = meta:get_inventory()
if self.name == "railcarts:cart_ent" then
inv:add_item("main", ItemStack("railcarts:cart"))
elseif self.name == "railcarts:boring_cart_ent" then
inv:add_item("main", ItemStack("railcarts:boring_cart"))
else
inv:add_item("main", ItemStack("railcarts:cargo_cart"))
end
self.object:remove()
self.dead = true
return no_more_slices
end
-- Handle loading/unloading from/to a hopper
if (self.carttype == "cargo" or self.carttype == "boring") and state.railstatus.hopper and self.speed == 0 then
local hpos = state.railstatus.hopper
local meta = minetest.get_meta(hpos)
if meta then
local frominv = meta:get_inventory()
local toinv = self.inventory
local desc = "load"
-- For now at least, loading is the default, and unloading happens
-- when there is an autolauncher below.
if state.railstatus.onautolauncher then
local t = frominv
frominv = toinv
toinv = t
desc = "unload"
end
if frominv:is_empty("main") then
if self.loadwait > 600 and state.railstatus.autolaunch and desc == "load" and not toinv:is_empty("main") then
cartbase.setdirection(self, state, state.railstatus.autolaunch)
state.speed = LAUNCH_CART_SPEED
dbg.v3("Cart at "..minetest.pos_to_string(hpos).." waited 10 minutes for more cargo - launching")
else
if toinv:is_empty("main") then
self.loadwait = 0
dbg.v3("Hopper and cart at "..minetest.pos_to_string(hpos).." are empty, waiting.")
else
dbg.v3("Hopper/cart at "..minetest.pos_to_string(hpos).." is empty, waiting (for "..math.floor(self.loadwait).."s)")
end
self.wait = 1
end
else
self.loadwait = 0
for i, stack in ipairs(frominv:get_list("main")) do
if stack:get_name() ~= "" then
if toinv:room_for_item("main", stack) then
-- Important to overwrite the slot here, not remove_item,
-- otherwise if there are two items the same with metadata
-- the wrong one can be removed.
frominv:set_stack("main", i, ItemStack(nil))
toinv:add_item("main", stack)
dbg.v1("Cart "..desc.."ed "..stack:get_count().." "..stack:get_name().." at "..minetest.pos_to_string(hpos))
-- Counts as a move, for the purposes of remaining
-- active
self.lastmove = 0
self.wait = 1
break
else
if desc == "load" then
if state.railstatus.autolaunch then
cartbase.setdirection(self, state, state.railstatus.autolaunch)
state.speed = LAUNCH_CART_SPEED
dbg.v3("Cart at "..minetest.pos_to_string(hpos).." is full - launching")
else
dbg.v3("Cart at "..minetest.pos_to_string(hpos).." is full")
self.wait = 1
end
else
dbg.v3("Hopper at "..minetest.pos_to_string(hpos).." is full, can't"..desc)
self.wait = 1
end
end
break
end
end
end
end
else
self.loadwait = 0
end
-- For safety - should never happen...
if state.railstatus.railtype == "inv" and state.speed ~= 0 then
dbg.v1("Stopping cart, it's off the rails at "..minetest.pos_to_string(self.pos).." "..minetest.pos_to_string(vector.round(self.pos)))
state.speed = 0
end
if state.speed > 0 then
-- Move the cart
local movedist = state.speed * dtime
dbg.v3("Cart moving "..movedist.." at speed "..state.speed.." and dtime "..dtime)
local xz = direction_to_xz(state.direction)
local axis, oaxis
if xz.x ~= 0 then
axis = "x"
oaxis = "z"
else
axis = "z"
oaxis = "x"
end
state.movein = false
if (xz[axis] > 0 and state.pos[axis] < state.nodepos[axis]) or
(xz[axis] < 0 and state.pos[axis] > state.nodepos[axis]) then
state.movein = true
end
-- Get the status of the rails at our next location
cartbase.get_next_railstatus(self, state, false)
local drop = false
if state.nextrailstatus.railtype == "inv" then
-- If there's no next rail, maybe there's a drop (a.k.a a downward slope)
cartbase.get_next_railstatus(self, state, true)
if state.nextrailstatus.railtype ~= "inv" then drop = true end
end
-- Stop here if we're entering an unloaded block. Hopefully it will load
-- and we can just carry on
if is_unloaded(state.nextrailstatus.railtype) then
dbg.v2("Waiting for next block load ("..state.nextrailstatus.railtype..") at "..
minetest.pos_to_string(state.nextrailstatus.unloadedpos)..
" : block = "..
math.floor(state.nextrailstatus.unloadedpos.x / 16)..","..
math.floor(state.nextrailstatus.unloadedpos.y / 16)..","..
math.floor(state.nextrailstatus.unloadedpos.z / 16))
return no_more_slices
end
dbg.v3("Cart on "..state.railstatus.railtype.." at "..minetest.pos_to_string(state.pos)..
", dir:"..state.direction.." speed:"..state.speed..
" next rail:"..state.nextrailstatus.railtype..
" "..(drop and "(drop)" or "")..
" "..(state.movein and "(movein)" or "(moveout)"))
state.pos[axis] = state.pos[axis] + movedist * xz[axis]
if state.movein then
if (xz[axis] > 0 and state.pos[axis] >= state.nodepos[axis]) or
(xz[axis] < 0 and state.pos[axis] <= state.nodepos[axis]) then
-- We were moving in to the centre, but now we've moved past it
local past = math.abs(state.pos[axis] - state.nodepos[axis])
-- Trigger a detector?
if state.railstatus.detector then
local detpos = state.railstatus.detector
minetest.add_node(detpos, {name="railcarts:cart_detector_on"})
mesecon:receptor_on(detpos, mesecon.rules.default)
no_more_slices = true
dbg.v2("Triggered cart detector at "..minetest.pos_to_string(detpos))
end
-- Various control things. These are applied only at the centre
-- of the node such that they only occur once.
local con = state.railstatus.control
if con then
if con == "maxspeed" then
dbg.v2("Max speed on control rail")
state.speed = MAXIMUM_CART_SPEED
elseif con == "speedup" then
dbg.v2("Accelerating on control rail")
state.speed = state.speed + 1
elseif con == "slowdown" then
dbg.v2("Decelerating on control rail")
state.speed = state.speed - 1
elseif con == "stop" then
dbg.v2("Stopping on control rail")
state.speed = 0
elseif con == "reverse" then
dbg.v2("Reversing on control rail")
cartbase.setdirection(self, state,
direction_reverse(state.direction))
-- Move back to prevent re-trigger on the way back
state.pos[axis] = state.pos[axis] - past
elseif string.sub(con, 0, 6) == "speed " then
local newspeed = tonumber(string.sub(con, 7))
if newspeed then
state.speed = newspeed
dbg.v2("Setting speed to "..newspeed.." on control rail")
else
dbg.v1("Invalid speed from control rail")
end
elseif string.sub(con, 0, 4) == "tag " then
self.tag = string.sub(con, 5)
dbg.v2("Cart tagged '"..self.tag.."'")
elseif con == "none" then
-- doesn't do anything
else
dbg.v1("Invalid control rail setting")
end
-- Cap speed appropriately
if state.speed < 0 then state.speed = 0 end
if state.speed > MAXIMUM_CART_SPEED then state.speed = MAXIMUM_CART_SPEED end
end
-- Send digiline signal if required
if digiline and state.railstatus.digiline then
local dpos = state.railstatus.digiline
local channel = minetest.get_meta(dpos):get_string("channel")
if channel and channel ~= "" then
msg = {}
msg.tag = self.tag
msg.speed = self.speed
msg.direction = self.direction
if self.linkedplayer then
msg.passenger = self.linkedplayer:get_player_name()
else
msg.passenger = nil
end
msg.carttype = self.carttype
dbg.v2("Sending digiline message on channel "..channel)
digiline:receptor_send(dpos, digiline.rules.default, channel, msg)
no_more_slices = true
end
end
if state.nextrailstatus.railtype == "inv" then
-- End of rails, so stop the cart (or do a custom action)
state.pos[axis] = state.nodepos[axis]
state.speed = 0
if self.on_end_of_rails then
if self.on_end_of_rails(self, state, axis, oaxis, xz) then
self.wait = 2
state.speed = 1
-- Back up a bit so we come back here for the next action
state.pos[axis] = state.pos[axis] - (0.4 * xz[axis])
end
else
dbg.v2("Cart reached end of rails and stopped at "..minetest.pos_to_string(state.pos))
end
elseif is_curve(state.railstatus.railtype) then
state.pos[axis] = state.nodepos[axis]
local newdir
if state.railstatus.railtype == "x+" then
if axis == "x" then
newdir = 2
past = -past
else
newdir = 1
end
elseif state.railstatus.railtype == "x-" then
if axis == "x" then
newdir = 2
past = -past
else
newdir = 3
past = -past
end
elseif state.railstatus.railtype == "z+" then
if axis == "x" then
newdir = 0
else
newdir = 1
end
else
if axis == "x" then
newdir = 0
else
newdir = 3
past = -past
end
end
cartbase.setdirection(self, state, newdir)
state.pos[oaxis] = state.pos[oaxis] + past
dbg.v2("Cart turned a corner")
end
end
else
--Moving out
-- Eject a player if necessary - only when moving out, to avoid
-- false ejections when the cart will change direction on a
-- switch
if state.railstatus.eject and self.linkedplayer ~= nil then
dbg.v1("Ejecting player from cart")
self.linkedplayer:set_detach()
self.linkedplayer:setpos(state.pos)
self.linkedplayer = nil
end
local newnodepos = vector.round(state.pos)
if state.nodepos[axis] ~= newnodepos[axis] then
dbg.v3("New nodepos "..newnodepos[axis]..", old "..state.nodepos[axis])
-- We've arrived in the new node
if drop then
state.pos.y = state.pos.y - 1
elseif state.direction == state.railstatus.slope then
state.pos.y = state.pos.y + 1
end
end
end
else
-- speed == 0
dbg.v3("Cart stationary at "..minetest.pos_to_string(state.pos))
if state.railstatus.onautolauncher and self.linkedplayer ~= nil then
dbg.v1("Ejecting player from cart")
self.linkedplayer:set_detach()
self.linkedplayer:setpos(state.pos)
self.linkedplayer = nil
end
-- Set speed/direction from launcher
if state.railstatus.launch then
dbg.v3("Launch cart at "..minetest.pos_to_string(state.pos)..
", direction="..state.railstatus.launch)
cartbase.setdirection(self, state, state.railstatus.launch)
state.speed = LAUNCH_CART_SPEED
elseif state.railstatus.autolaunch and self.linkedplayer ~= nil then
dbg.v3("Autolaunch cart at "..minetest.pos_to_string(state.pos)..
", direction="..state.railstatus.autolaunch)
cartbase.setdirection(self, state, state.railstatus.autolaunch)
state.speed = LAUNCH_CART_SPEED
end
end
-- Set ourselves autonomous if necessary (requires minetest patch)
if state.speed > 0 then
self.lastmove = 0
end
local autonomous = 0
if self.lastmove < 10 or state.railstatus.hopper then
autonomous = 1
end
self.object:set_autonomous(autonomous)
-- Write things back. Ultimately the position will get 'broken' when core
-- turns it from a double to a float. We don't care, because we use our
-- own internally stored position as the reference.
self.pos = state.pos
self.speed = state.speed
self.direction = state.direction
return no_more_slices
end
--- Get the status of the rails at the next node
--@param state The current state, which will have a next_railstatus
-- field added. (And only railstatus, pos, speed, movein and
-- direction are relevant)
--
-- If movein is true, the track is used to determine the
-- outbound direction (e.g. curves, switches)
-- If movein is false, the current direction is used instead.
-- This is to ensure that movement isn't messed up by the
-- track switching underneath the cart.
--
--@param below True to look below instead of at the same level
function cartbase.get_next_railstatus(self, state, below)
if state.speed == 0 then
state.nextrailstatus = {railtype="inv"}
return
end
local xz, nexty
if below then
nexty = -1
else
if state.railstatus.slope then
if state.railstatus.slope == state.direction then
nexty = 1
else
nexty = 0
end
else
nexty = 0
end
end
if is_curve(state.railstatus.railtype) and state.movein then
if state.railstatus.railtype == "x+" then
if state.direction == 0 or state.direction == 1 then
xz = {x=1,z=0}
else
xz = {x=0,z=-1}
end
elseif state.railstatus.railtype == "x-" then
if state.direction == 0 or state.direction == 3 then
xz = {x=-1,z=0}
else
xz = {x=0,z=-1}
end
elseif state.railstatus.railtype == "z+" then
if state.direction == 2 or state.direction == 1 then
xz = {x=1,z=0}
else
xz = {x=0,z=1}
end
else
if state.direction == 2 or state.direction == 3 then
xz = {x=-1,z=0}
else
xz = {x=0,z=1}
end
end
else
xz = direction_to_xz(state.direction)
end
nextpos = vector.round(state.pos)
nextpos.x = nextpos.x + xz.x
nextpos.y = nextpos.y + nexty
nextpos.z = nextpos.z + xz.z
dbg.v3("Checking nextpos "..minetest.pos_to_string(nextpos))
state.nextrailstatus = get_railstatus(nextpos)
end
--- Handle cart being punched by a player
--
function cartbase.punch_move(self, own_pos, hitterpos)
self.wait = 0
local railstatus = get_railstatus(self.pos)
-- Only when on rails...
if railstatus.railtype == "inv" then return end
local xd = own_pos.x - hitterpos.x
local zd = own_pos.z - hitterpos.z
dbg.v2("Player punched cart with xd="..xd..",zd="..zd)
local newdir
if railstatus.railtype == "x" then
if xd < 0 then
newdir = 3
else
newdir = 1
end
elseif railstatus.railtype == "z" then
if zd < 0 then
newdir = 2
else
newdir = 0
end
elseif railstatus.railtype == "x+" then
if math.abs(zd) > math.abs(xd) and zd < 0 then
newdir = 2
elseif math.abs(xd) > math.abs(zd) and xd > 0 then
newdir = 1
end
end
-- TODO - the rest of the curves!
if newdir then
-- Punching a moving cart in the same direction speeds it up
if self.speed ~= 0 then
if newdir == self.direction then
self.speed = self.speed + 1
if self.speed > MAXIMUM_CART_SPEED then
self.speed = MAXIMUM_CART_SPEED
end
else
-- otherwise it just stops...
self.speed = 0
end
return
end
state = {direction=newdir, speed=1, railstatus=railstatus, pos=self.pos, movein=true}
cartbase.get_next_railstatus(self, state, false)
if state.nextrailstatus.railtype == "inv" then
-- check below, so we can push down a hill
cartbase.get_next_railstatus(self, state, true)
end
if state.nextrailstatus.railtype ~= "inv" and
not is_unloaded(state.nextrailstatus.railtype) then
cartbase.setdirection(self, nil, newdir)
self.speed = 1
end
end
end
--- Handler for on_punch
-- @@param hitter The player that hit it
function cartbase.on_punch_handler(self, hitter)
if hitter:get_player_control().sneak and cartbase.is_empty(self) then
hitter:get_inventory():add_item("main", self.getitem)
self.object:remove()
else
local own_pos = self.object:getpos()
local hitterpos = hitter:getpos()
cartbase.punch_move(self,own_pos,hitterpos)
end
return true
end
--- Handler for on_activate
-- @param staticdata Saved data if restoring a deactivated cart
function cartbase.on_activate_handler(self, staticdata)
self.direction = 0
self.speed = 0
self.pos = nil
self.tag = nil
self.wait = 0
self.loadwait = 0
self.skipsteps = 0
self.lastmove = 0
self.dtime_debt = 0
-- These are parameters for the boring cart only...
self.dig = false
self.safedig = true
self.bridge = false
self.lay = false
self.digslope = 2
self.object:set_armor_groups(self.groups)
-- Set up detached inventories if required
if self.carttype == "cargo" or self.carttype == "boring" then
-- Unique name needed for detached inventory
-- TODO - is there a better way of getting this unique ID?
local uid
for k, v in pairs(minetest.luaentities) do
if v == self then
uid = k
break
end
end
if not uid then
dbg.v1("WARNING - unique ID was not generated - cart inventories may be shared")
uuid = tostring(math.random())
end
self.inventoryname = uid
dbg.v3("Creating new cart, inventory name is "..self.inventoryname)
self.inventory = minetest.create_detached_inventory(self.inventoryname, nil)
self.inventory:set_size("main",12)
if self.carttype == "boring" then
self.inventory:set_size("materials",12)
end
end
local restored = minetest.deserialize(staticdata)
if restored ~= nil then
if restored.pos then
self.pos = restored.pos
else
self.pos = self.object:getpos()
end
if restored.speed then
self.speed = restored.speed
end
if restored.direction then
self.direction = restored.direction
end
if restored.tag then
self.tag = restored.tag
end
if restored.wait then
self.wait = restored.wait
end
if restored.loadwait then
self.loadwait = restored.loadwait
end
if restored.dig then
self.dig = restored.dig
end
if restored.safedig then
self.safedig = restored.safedig
end
if restored.bridge then
self.bridge = restored.bridge
end
if restored.lay then
self.lay = restored.lay
end
if restored.digslope then
self.digslope = restored.digslope
end
local inv_main
if restored.stacks then
-- TODO legacy support, can remove in a bit
inv_main = restored.stacks
end
inv_main = restored.inv_main
if inv_main then
for i=1,#inv_main,1 do
self.inventory:set_stack("main",i,inv_main[i])
end
end
local inv_materials = restored.inv_materials
if inv_materials then
for i=1,#inv_materials,1 do
self.inventory:set_stack("materials",i,inv_materials[i])
end
end
end
self.object:set_armor_groups(self.groups)
end
--- Get static data for saving state on deactivation
function cartbase.get_staticdata_handler(self)
local tostore = {}
tostore.pos = self.pos
tostore.speed = self.speed
tostore.direction = self.direction
tostore.tag = self.tag
tostore.wait = self.wait
tostore.loadwait = self.loadwait
if self.carttype == "cargo" or self.carttype == "boring" then
tostore.inv_main = cartbase.get_inv(self, "main")
end
if self.carttype == "boring" then
tostore.inv_materials = cartbase.get_inv(self, "materials")
tostore.dig = self.dig
tostore.safedig = self.safedig
tostore.bridge = self.bridge
tostore.lay = self.lay
tostore.digslope = self.digslope
end
return minetest.serialize(tostore)
end
function cartbase.get_inv(self, name)
local st = {}
local list = self.inventory:get_list(name)
for i=1,#list,1 do
table.insert(st,list[i]:to_string())
end
return st
end
function cartbase.place_cart(item, pointed_thing, ent)
if pointed_thing.type == "node" then
local pos = pointed_thing.above
pos.y = pos.y - 1
local railtype = get_railstatus(pos).railtype
if railtype ~= "inv" then
local obj = minetest.add_entity(pos, ent)
if obj then
if railtype == "x" then
obj:setyaw(0)
elseif railtype == "z" then
obj:setyaw(0 + math.pi/2)
end
item:take_item()
end
end
end
return item
end