836 lines
30 KiB
Lua
836 lines
30 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.skipnextstep then
|
|
self.skipnextstep = false
|
|
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
|
|
|
|
-- 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.
|
|
-- Theoretically, the speed could increase during the first slice and
|
|
-- make the movement too large in the second, but I'm not worrying about
|
|
-- that for now. TODO
|
|
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
|
|
end
|
|
if dtime >= 0 and not nomore then
|
|
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
|
|
self.skipnextstep = true
|
|
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 v3equal(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 within
|
|
-- low enough that the cart doesn't >= 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!)
|
|
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 = v3posround(state.pos)
|
|
state.direction = self.direction
|
|
state.speed = self.speed
|
|
state.railstatus = get_railstatus(state.pos, state.direction, state.speed)
|
|
-- 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 "..postostr(state.pos))
|
|
self.wait = 1
|
|
end
|
|
return no_more_slices
|
|
end
|
|
|
|
-- Eject a player if necessary
|
|
if (state.railstatus.eject or 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
|
|
|
|
-- 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 "..postostr(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
|
|
|
|
-- Set speed/direction from launcher
|
|
if state.railstatus.launch then
|
|
cartbase.setdirection(self, state, state.railstatus.launch)
|
|
state.speed = LAUNCH_CART_SPEED
|
|
elseif state.railstatus.autolaunch and self.linkedplayer ~= nil then
|
|
cartbase.setdirection(self, state, state.railstatus.autolaunch)
|
|
state.speed = LAUNCH_CART_SPEED
|
|
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 "..postostr(hpos).." waited 10 minutes for more cargo - launching")
|
|
else
|
|
if toinv:is_empty("main") then
|
|
self.loadwait = 0
|
|
dbg.v3("Hopper and cart at "..postostr(hpos).." are empty, waiting.")
|
|
else
|
|
dbg.v3("Hopper/cart at "..postostr(hpos).." is empty, waiting (for "..math.floor(self.loadwait).."s)")
|
|
end
|
|
self.wait = 5
|
|
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 "..postostr(hpos))
|
|
-- Counts as a move, for the purposes of remaining
|
|
-- active
|
|
self.lastmove = 0
|
|
self.wait = 5
|
|
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 "..postostr(hpos).." is full - launching")
|
|
else
|
|
dbg.v3("Cart at "..postostr(hpos).." is full")
|
|
self.wait = 30
|
|
end
|
|
else
|
|
dbg.v3("Hopper at "..postostr(hpos).." is full, can't"..desc)
|
|
self.wait = 30
|
|
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 "..postostr(self.pos).." "..postostr(v3posround(self.pos)))
|
|
state.speed = 0
|
|
end
|
|
|
|
-- Move the cart
|
|
if state.speed > 0 then
|
|
|
|
-- 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 "..
|
|
postostr(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
|
|
|
|
local movedist = state.speed * 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
|
|
local 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
|
|
movein = true
|
|
end
|
|
|
|
dbg.v3("Cart on "..state.railstatus.railtype.." at "..postostr(state.pos)..
|
|
", dir:"..state.direction.." speed:"..state.speed..
|
|
" next rail:"..state.nextrailstatus.railtype..
|
|
" "..(drop and "(drop)" or "")..
|
|
" "..(movein and "(movein)" or "(moveout)"))
|
|
|
|
state.pos[axis] = state.pos[axis] + movedist * xz[axis]
|
|
if 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)
|
|
dbg.v2("Triggered cart detector at "..postostr(newpos))
|
|
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 "..postostr(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
|
|
|
|
local newnodepos = v3posround(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
|
|
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 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 and direction are relevant)
|
|
--@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) 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 = v3posround(state.pos)
|
|
nextpos.x = nextpos.x + xz.x
|
|
nextpos.y = nextpos.y + nexty
|
|
nextpos.z = nextpos.z + xz.z
|
|
dbg.v3("Checking nextpos "..postostr(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}
|
|
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.skipnextstep = false
|
|
self.lastmove = 0
|
|
self.dtime_debt = 0
|
|
|
|
-- These are parameters for the boring cart only...
|
|
self.dig = false
|
|
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.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.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
|
|
|