mirror of
https://github.com/sys4-fr/server-nalc.git
synced 2024-12-27 11:10:37 +01:00
392af4281f
Hack that makes sure cart doesn't leave rails and hangs in the air. This patch now seems to fix that. (thx for testing with me @fabienwang)
904 lines
30 KiB
Lua
Executable File
904 lines
30 KiB
Lua
Executable File
dofile(minetest.get_modpath("carts").."/functions.lua")
|
|
|
|
local cart = {
|
|
physical = true, -- Set to false to not make carts collide with other entities such as carts or the player.
|
|
collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
|
visual = "mesh",
|
|
mesh = "cart.x",
|
|
visual_size = {x=1, y=1},
|
|
textures = {"cart.png"}, -- Set to "invisible.png" to make the cart invisible, which is sometimes useful for videos and custom maps.
|
|
driver = nil,
|
|
velocity = {x=0, y=0, z=0},
|
|
old_pos = nil,
|
|
old_velocity = nil,
|
|
pre_stop_dir = nil,
|
|
MAX_V = 9.6, -- Limit of the velocity (speed).
|
|
TARGET_TOUR_V = 6, -- Target touring velocity, currently unused.
|
|
railcount = 0,
|
|
ignorekeypos = nil,
|
|
lockyaw = false, -- Set to true to lock camera by default. If set to false, player has to press the "jump" key to lock camera.
|
|
yawtarget = nil,
|
|
YAW_STEP = math.pi/64 -- Larger YAW_STEP makes for slower camera turning.
|
|
}
|
|
|
|
function cart:on_rightclick(clicker)
|
|
if not clicker or not clicker:is_player() then
|
|
return
|
|
end
|
|
if self.driver and clicker == self.driver then
|
|
self.driver = nil
|
|
clicker:set_detach()
|
|
elseif not self.driver then
|
|
self.driver = clicker
|
|
clicker:set_attach(self.object, "", {x=0,y=5,z=0}, {x=0,y=0,z=0})
|
|
end
|
|
end
|
|
|
|
function cart:on_activate(staticdata, dtime_s)
|
|
self.object:set_armor_groups({immortal=1})
|
|
if staticdata then
|
|
local tmp = minetest.deserialize(staticdata)
|
|
if tmp then
|
|
self.velocity = tmp.velocity
|
|
end
|
|
if tmp and tmp.pre_stop_dir then
|
|
self.pre_stop_dir = tmp.pre_stop_dir
|
|
end
|
|
end
|
|
self.old_pos = self.object:getpos()
|
|
self.old_velocity = self.velocity
|
|
end
|
|
|
|
function cart:get_staticdata()
|
|
return minetest.serialize({
|
|
velocity = self.velocity,
|
|
pre_stop_dir = self.pre_stop_dir,
|
|
})
|
|
end
|
|
|
|
-- Remove the cart if holding a tool or accelerate it
|
|
function cart:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
|
if not puncher or not puncher:is_player() then
|
|
return
|
|
end
|
|
|
|
if puncher:get_player_control().sneak then
|
|
minetest.log("action", "carts:cart removed at " .. pos_to_string(vector.round(self.object:getpos())) .. ", " .. self.railcount .. " rails travelled.")
|
|
self.object:remove()
|
|
local inv = puncher:get_inventory()
|
|
if minetest.setting_getbool("creative_mode") then
|
|
if not inv:contains_item("main", "carts:cart") then
|
|
inv:add_item("main", "carts:cart")
|
|
end
|
|
else
|
|
inv:add_item("main", "carts:cart")
|
|
end
|
|
return
|
|
end
|
|
|
|
if puncher == self.driver and (math.abs(self.velocity.x)>1 or math.abs(self.velocity.z)>1) then
|
|
return
|
|
end
|
|
|
|
local d = cart_func:velocity_to_dir(direction)
|
|
if d.x==0 and d.z==0 then
|
|
local fd = minetest.dir_to_facedir(puncher:get_look_dir())
|
|
if fd == 0 then
|
|
d.x = 1
|
|
elseif fd == 1 then
|
|
d.z = -1
|
|
elseif fd == 2 then
|
|
d.x = -1
|
|
elseif fd == 3 then
|
|
d.z = 1
|
|
end
|
|
end
|
|
|
|
local s = self.velocity
|
|
if time_from_last_punch > tool_capabilities.full_punch_interval then
|
|
time_from_last_punch = tool_capabilities.full_punch_interval
|
|
end
|
|
local f = 4*(time_from_last_punch/tool_capabilities.full_punch_interval)
|
|
local v = {x=s.x+d.x*f, y=s.y, z=s.z+d.z*f}
|
|
if math.abs(v.x) < 6 and math.abs(v.z) < 6 then
|
|
self.velocity = v
|
|
else
|
|
if math.abs(self.velocity.x) < 6 and math.abs(v.x) >= 6 then
|
|
self.velocity.x = 6*cart_func:get_sign(self.velocity.x)
|
|
end
|
|
if math.abs(self.velocity.z) < 6 and math.abs(v.z) >= 6 then
|
|
self.velocity.z = 6*cart_func:get_sign(self.velocity.z)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Returns the direction as a unit vector
|
|
function cart:get_rail_direction(pos, dir)
|
|
--minetest.log("action", "get_rail_direction top pos="..pos_to_string(pos).." dir="..pos_to_string(dir))
|
|
local fwd=cart_func.v3:copy(dir)
|
|
fwd.y=0
|
|
local up=cart_func.v3:copy(dir)
|
|
up.y=1
|
|
local down=cart_func.v3:copy(dir)
|
|
down.y=-1
|
|
-- figure out left and right
|
|
local left={x=0,y=0,z=0}
|
|
local right={x=0,y=0,z=0}
|
|
if dir.z ~= 0 and dir.x == 0 then
|
|
left.x=-dir.z --left is opposite sign from z
|
|
right.x=dir.z --right is same sign as z
|
|
elseif dir.x ~= 0 and dir.z == 0 then
|
|
left.z=dir.x --left is same sign as x
|
|
right.z=-dir.x --right is opposite sign from x
|
|
end --left and right
|
|
local leftdown=cart_func.v3:copy(left)
|
|
leftdown.y=-1
|
|
local rightdown=cart_func.v3:copy(right)
|
|
rightdown.y=-1
|
|
--minetest.log("action", " fwd="..pos_to_string(fwd))
|
|
--minetest.log("action", " down="..pos_to_string(down))
|
|
--minetest.log("action", " up="..pos_to_string(up))
|
|
--minetest.log("action", " left="..pos_to_string(left))
|
|
--minetest.log("action", " leftdown="..pos_to_string(leftdown))
|
|
--minetest.log("action", " right="..pos_to_string(right))
|
|
--minetest.log("action", " rightdown="..pos_to_string(rightdown))
|
|
|
|
local ignorekeys=false
|
|
--ignorekeypos stores a position where we changed direction because the
|
|
--player was pressing left or right keys. once we have changed direction,
|
|
--we want to ignore those keys until you have changed to a different rail
|
|
--(otherwise you end up reversing)
|
|
--I use the ignorekeys boolean to make the key logic more readable
|
|
if self.ignorekeypos then --ignorekeypos was set
|
|
if cart_func.v3:equal(self.ignorekeypos,pos) then
|
|
ignorekeys=true --if still at same position, ignore left and right keys
|
|
else
|
|
self.ignorekeypos=nil --if ignorekeypos was set but pos does not match anymore, clear it
|
|
end
|
|
end
|
|
|
|
local ctrl=nil
|
|
if self.driver and not ignorekeys then
|
|
ctrl = self.driver:get_player_control()
|
|
end
|
|
|
|
if ctrl and ctrl.left then --left key pressed, check left first
|
|
if cart_func:check_rail_in_direction(pos,left) then
|
|
self.ignorekeypos=cart_func.v3:copy(pos) --ignore keys until pos changes
|
|
return left
|
|
elseif cart_func:check_rail_in_direction(pos,leftdown) then
|
|
self.ignorekeypos=cart_func.v3:copy(pos)
|
|
return leftdown
|
|
end
|
|
elseif ctrl and ctrl.right then --right key pressed, check right first
|
|
if cart_func:check_rail_in_direction(pos,right) then
|
|
self.ignorekeypos=cart_func.v3:copy(pos)
|
|
return right
|
|
elseif cart_func:check_rail_in_direction(pos,rightdown) then
|
|
self.ignorekeypos=cart_func.v3:copy(pos)
|
|
return rightdown
|
|
end
|
|
end --ctrl.left ctrl.right
|
|
|
|
--now for normal checks
|
|
if cart_func:check_rail_in_direction(pos,fwd) then
|
|
return fwd
|
|
elseif cart_func:check_rail_in_direction(pos,down) then
|
|
return down
|
|
elseif cart_func:check_rail_in_direction(pos,up) then
|
|
return up
|
|
elseif (not ctrl or not ctrl.left) --only check left if we didnt above
|
|
and cart_func:check_rail_in_direction(pos,left) then
|
|
return left
|
|
elseif (not ctrl or not ctrl.left)
|
|
and cart_func:check_rail_in_direction(pos,leftdown) then
|
|
return leftdown
|
|
elseif (not ctrl or not ctrl.right) --only check right if we didnt above
|
|
and cart_func:check_rail_in_direction(pos,right) then
|
|
return right
|
|
elseif (not ctrl or not ctrl.right)
|
|
and cart_func:check_rail_in_direction(pos,rightdown) then
|
|
return rightdown
|
|
else
|
|
return {x=0, y=0, z=0}
|
|
end --normal rail checks
|
|
end --get_rail_direction
|
|
|
|
function cart:calc_rail_direction(pos, vel)
|
|
local velocity = cart_func.v3:copy(vel)
|
|
local p = cart_func.v3:copy(pos)
|
|
if cart_func:is_int(p.x) and cart_func:is_int(p.z) then
|
|
|
|
local dir = cart_func:velocity_to_dir(velocity)
|
|
local dir_old = cart_func.v3:copy(dir)
|
|
|
|
dir = self:get_rail_direction(cart_func.v3:round(p), dir)
|
|
|
|
local v = math.max(math.abs(velocity.x), math.abs(velocity.z))
|
|
velocity = {
|
|
x = v * dir.x,
|
|
y = v * dir.y,
|
|
z = v * dir.z,
|
|
}
|
|
|
|
if cart_func.v3:equal(velocity, {x=0, y=0, z=0}) and not cart_func:is_rail(p) then
|
|
|
|
-- First try this HACK
|
|
-- Move the cart on the rail if above or under it
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=1, z=0})) and vel.y >= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-1, z=0})) and vel.y <= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=-1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
-- Now the HACK gets really dirty
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=2, z=0})) and vel.y >= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-2, z=0})) and vel.y <= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=-1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
-- NOW the hack gets even dirtier
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=3, z=0})) and vel.y >= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-3, z=0})) and vel.y <= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=-1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=4, z=0})) and vel.y >= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
if cart_func:is_rail(cart_func.v3:add(p, {x=0, y=-4, z=0})) and vel.y <= 0 then
|
|
p = cart_func.v3:add(p, {x=0, y=-1, z=0})
|
|
return self:calc_rail_direction(p, vel)
|
|
end
|
|
|
|
return {x=0, y=0, z=0}, p
|
|
end
|
|
|
|
if not cart_func.v3:equal(dir, dir_old) then
|
|
return velocity, cart_func.v3:round(p)
|
|
end
|
|
|
|
end
|
|
return velocity, p
|
|
end
|
|
|
|
-- built in pos_to_string doesn't handle nil.
|
|
function pos_to_string(pos)
|
|
if pos==nil then return "(nil)"
|
|
else return minetest.pos_to_string(pos)
|
|
end --poss==nil
|
|
end --pos_to_string
|
|
|
|
function cart:on_step(dtime)
|
|
|
|
local pos = self.object:getpos()
|
|
local dir = cart_func:velocity_to_dir(self.velocity)
|
|
|
|
-- *!* Debug.
|
|
if self.old_pos then
|
|
local cmp_old=vector.round(self.old_pos)
|
|
local cmp_new=vector.round(pos)
|
|
if cmp_old.x~=cmp_new.x or cmp_old.y~=cmp_new.y or cmp_old.z~=cmp_new.z then
|
|
self.railcount=self.railcount+1
|
|
-- local a = tonumber(minetest.get_meta(pos):get_string("cart_acceleration"))
|
|
-- local railtype=""
|
|
-- if a and a>0 then railtype="power" end
|
|
-- minetest.chat_send_all("-- cart pos="..pos_to_string(vector.round(pos)).." railcount="..self.railcount.." vel="..pos_to_string(self.velocity).." "..railtype) --*!*debug
|
|
end
|
|
end
|
|
-- *!* Debug.
|
|
|
|
local ctrl=nil
|
|
if self.driver then
|
|
ctrl = self.driver:get_player_control()
|
|
if ctrl and ctrl.jump and not self.lockyaw then
|
|
self.lockyaw=true
|
|
self.yawtarget=self.object:getyaw()
|
|
-- minetest.chat_send_player(self.driver:get_player_name(),"Player view locked to cart, sneak to unlock.")
|
|
elseif ctrl and ctrl.sneak and self.lockyaw then
|
|
self.lockyaw=false
|
|
-- minetest.chat_send_player(self.driver:get_player_name(),"Player view unlocked from cart, jump to lock.")
|
|
end
|
|
end -- Check lockyaw if self.driver.
|
|
|
|
if not cart_func.v3:equal(self.velocity, {x=0,y=0,z=0}) then
|
|
self.pre_stop_dir = cart_func:velocity_to_dir(self.velocity)
|
|
end
|
|
|
|
-- Stop the cart if the velocity is nearly 0, only if on a flat railway.
|
|
if dir.y == 0 then
|
|
if math.abs(self.velocity.x) < 0.1 and math.abs(self.velocity.z) < 0.1 then
|
|
-- Start the cart if powered from mesecons.
|
|
local a = tonumber(minetest.get_meta(pos):get_string("cart_acceleration"))
|
|
if a and a ~= 0 then
|
|
if self.pre_stop_dir and cart_func.v3:equal(self:get_rail_direction(self.object:getpos(), self.pre_stop_dir), self.pre_stop_dir) then
|
|
self.velocity = {
|
|
x = self.pre_stop_dir.x * 0.2,
|
|
y = self.pre_stop_dir.y * 0.2,
|
|
z = self.pre_stop_dir.z * 0.2,
|
|
}
|
|
self.old_velocity = self.velocity
|
|
return
|
|
end
|
|
for _,y in ipairs({0,-1,1}) do
|
|
for _,z in ipairs({1,-1}) do
|
|
if cart_func.v3:equal(self:get_rail_direction(self.object:getpos(), {x=0, y=y, z=z}), {x=0, y=y, z=z}) then
|
|
self.velocity = {
|
|
x = 0,
|
|
y = 0.2*y,
|
|
z = 0.2*z,
|
|
}
|
|
self.old_velocity = self.velocity
|
|
return
|
|
end
|
|
end
|
|
for _,x in ipairs({1,-1}) do
|
|
if cart_func.v3:equal(self:get_rail_direction(self.object:getpos(), {x=x, y=y, z=0}), {x=x, y=y, z=0}) then
|
|
self.velocity = {
|
|
x = 0.2*x,
|
|
y = 0.2*y,
|
|
z = 0,
|
|
}
|
|
self.old_velocity = self.velocity
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
self.velocity = {x=0, y=0, z=0}
|
|
self.object:setvelocity(self.velocity)
|
|
self.old_velocity = self.velocity
|
|
self.old_pos = self.object:getpos()
|
|
return
|
|
end
|
|
end
|
|
|
|
--
|
|
-- Set the new moving direction
|
|
--
|
|
|
|
-- Recalcualte the rails that are passed since the last server step
|
|
local old_dir = cart_func:velocity_to_dir(self.old_velocity)
|
|
if old_dir.x ~= 0 then
|
|
local sign = cart_func:get_sign(pos.x-self.old_pos.x)
|
|
while true do
|
|
if sign ~= cart_func:get_sign(pos.x-self.old_pos.x) or pos.x == self.old_pos.x then
|
|
break
|
|
end
|
|
self.old_pos.x = self.old_pos.x + cart_func:get_sign(pos.x-self.old_pos.x)*0.1
|
|
self.old_pos.y = self.old_pos.y + cart_func:get_sign(pos.x-self.old_pos.x)*0.1*old_dir.y
|
|
self.old_velocity, self.old_pos = self:calc_rail_direction(self.old_pos, self.old_velocity)
|
|
old_dir = cart_func:velocity_to_dir(self.old_velocity)
|
|
if not cart_func.v3:equal(cart_func:velocity_to_dir(self.old_velocity), dir) then
|
|
self.velocity = self.old_velocity
|
|
pos = self.old_pos
|
|
self.object:setpos(self.old_pos)
|
|
break
|
|
end
|
|
end
|
|
elseif old_dir.z ~= 0 then
|
|
local sign = cart_func:get_sign(pos.z-self.old_pos.z)
|
|
while true do
|
|
if sign ~= cart_func:get_sign(pos.z-self.old_pos.z) or pos.z == self.old_pos.z then
|
|
break
|
|
end
|
|
self.old_pos.z = self.old_pos.z + cart_func:get_sign(pos.z-self.old_pos.z)*0.1
|
|
self.old_pos.y = self.old_pos.y + cart_func:get_sign(pos.z-self.old_pos.z)*0.1*old_dir.y
|
|
self.old_velocity, self.old_pos = self:calc_rail_direction(self.old_pos, self.old_velocity)
|
|
old_dir = cart_func:velocity_to_dir(self.old_velocity)
|
|
if not cart_func.v3:equal(cart_func:velocity_to_dir(self.old_velocity), dir) then
|
|
self.velocity = self.old_velocity
|
|
pos = self.old_pos
|
|
self.object:setpos(self.old_pos)
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Calculate the new step
|
|
self.velocity, pos = self:calc_rail_direction(pos, self.velocity)
|
|
self.object:setpos(pos)
|
|
dir = cart_func:velocity_to_dir(self.velocity)
|
|
|
|
-- Accelerate or decelerate the cart according to the pitch and acceleration of the rail node
|
|
local a = tonumber(minetest.get_meta(pos):get_string("cart_acceleration"))
|
|
if not a then
|
|
a = 0
|
|
end
|
|
local t = tonumber(minetest.get_meta(pos):get_string("cart_touring_velocity"))
|
|
if not t then t=0 end
|
|
if t>0 then
|
|
local vx=math.abs(self.velocity.x)
|
|
local vy=math.abs(self.velocity.y)
|
|
local vz=math.abs(self.velocity.z)
|
|
-- make v the largest of the 3 velocities
|
|
local v=vx
|
|
if vy>v then v=vy end
|
|
if vz>v then v=vz end
|
|
--
|
|
local diff=0
|
|
local acelordecl=0
|
|
if v>t then
|
|
diff=v-t
|
|
acelordecl=-1
|
|
elseif v<t then
|
|
diff=t-v
|
|
acelordecl=1
|
|
end --v>t
|
|
--minetest.log("action", " on_step t1 v="..v.." t="..t.." diff="..diff.." a="..a.." acelordecl="..acelordecl)
|
|
--adjust for grav
|
|
if self.velocity.y<0 then --going downhill so grav will acel by extra 0.13
|
|
--if we are decel then add an extra 0.13 to how much we need to decel
|
|
--if we are accel then subtract an extra 0.13 from how much we need to acel
|
|
diff=diff-(0.13*acelordecl)
|
|
elseif self.velocity.y>0 then --going uphill grav will decl by extra 0.10
|
|
--if we are decel then subtract 0.1 from how much we need to decel
|
|
--if we are acel then add 0.1 to how much we need to acel
|
|
diff=diff+(0.1*acelordecl)
|
|
end -- self.velocity.y<0
|
|
--so now diff is the difference between cart velocity (after this turns grav)
|
|
--and our target touring velocity
|
|
--minetest.log("action", "*!* on_step t2 grav v="..v.." diff="..diff.." a="..a)
|
|
if diff<a then --we dont want to over acel or decel
|
|
a=diff
|
|
elseif diff>a*4 then
|
|
a=a*2 --if big difference, play catchup fast!
|
|
elseif diff>a*3 then
|
|
a=a*1.5 --if big difference, play catchup fast!
|
|
end --diff<a
|
|
a=a*acelordecl
|
|
end -- if t>0
|
|
|
|
-- Check if down arrow is being pressed (handbrake).
|
|
if self.driver then
|
|
local ctrl = self.driver:get_player_control()
|
|
if ctrl and ctrl.down then
|
|
a=a-0.1 -- Same as uphill.
|
|
|
|
end -- If handbrake.
|
|
end -- If self.driver.
|
|
|
|
if self.velocity.y < 0 then
|
|
self.velocity = {
|
|
x = self.velocity.x + (a+0.13)*cart_func:get_sign(self.velocity.x),
|
|
y = self.velocity.y + (a+0.13)*cart_func:get_sign(self.velocity.y),
|
|
z = self.velocity.z + (a+0.13)*cart_func:get_sign(self.velocity.z),
|
|
}
|
|
elseif self.velocity.y > 0 then
|
|
self.velocity = {
|
|
x = self.velocity.x + (a-0.1)*cart_func:get_sign(self.velocity.x),
|
|
y = self.velocity.y + (a-0.1)*cart_func:get_sign(self.velocity.y),
|
|
z = self.velocity.z + (a-0.1)*cart_func:get_sign(self.velocity.z),
|
|
}
|
|
else
|
|
self.velocity = {
|
|
x = self.velocity.x + (a-0.03)*cart_func:get_sign(self.velocity.x),
|
|
y = self.velocity.y + (a-0.03)*cart_func:get_sign(self.velocity.y),
|
|
z = self.velocity.z + (a-0.03)*cart_func:get_sign(self.velocity.z),
|
|
}
|
|
|
|
-- Place the cart exactly on top of the rail
|
|
if cart_func:is_rail(cart_func.v3:round(pos)) then
|
|
self.object:setpos({x=pos.x, y=math.floor(pos.y+0.5), z=pos.z})
|
|
pos = self.object:getpos()
|
|
end
|
|
end
|
|
|
|
-- Dont switch moving direction
|
|
-- Only if on flat railway
|
|
if dir.y == 0 then
|
|
if cart_func:get_sign(dir.x) ~= cart_func:get_sign(self.velocity.x) then
|
|
self.velocity.x = 0
|
|
end
|
|
if cart_func:get_sign(dir.y) ~= cart_func:get_sign(self.velocity.y) then
|
|
self.velocity.y = 0
|
|
end
|
|
if cart_func:get_sign(dir.z) ~= cart_func:get_sign(self.velocity.z) then
|
|
self.velocity.z = 0
|
|
end
|
|
end
|
|
|
|
-- Allow only one moving direction (multiply the other one with 0)
|
|
dir = cart_func:velocity_to_dir(self.velocity)
|
|
self.velocity = {
|
|
x = math.abs(self.velocity.x) * dir.x,
|
|
y = self.velocity.y,
|
|
z = math.abs(self.velocity.z) * dir.z,
|
|
}
|
|
|
|
|
|
-- Move cart exactly on the rail
|
|
if dir.x ~= 0 and not cart_func:is_int(pos.z) then
|
|
pos.z = math.floor(0.5+pos.z)
|
|
self.object:setpos(pos)
|
|
elseif dir.z ~= 0 and not cart_func:is_int(pos.x) then
|
|
pos.x = math.floor(0.5+pos.x)
|
|
self.object:setpos(pos)
|
|
end
|
|
|
|
-- Limit the velocity.
|
|
if math.abs(self.velocity.x) > self.MAX_V then
|
|
self.velocity.x = self.MAX_V*cart_func:get_sign(self.velocity.x)
|
|
end
|
|
if math.abs(self.velocity.y) > self.MAX_V then
|
|
self.velocity.y = self.MAX_V*cart_func:get_sign(self.velocity.y)
|
|
end
|
|
if math.abs(self.velocity.z) > self.MAX_V then
|
|
self.velocity.z = self.MAX_V*cart_func:get_sign(self.velocity.z)
|
|
end
|
|
|
|
self.object:setvelocity(self.velocity)
|
|
|
|
self.old_pos = self.object:getpos()
|
|
self.old_velocity = cart_func.v3:copy(self.velocity)
|
|
|
|
local oldyaw=self.object:getyaw()
|
|
if dir.x < 0 then
|
|
self.object:setyaw(math.pi/2)
|
|
elseif dir.x > 0 then
|
|
self.object:setyaw(3*math.pi/2)
|
|
elseif dir.z < 0 then
|
|
self.object:setyaw(math.pi)
|
|
elseif dir.z > 0 then
|
|
self.object:setyaw(0)
|
|
end
|
|
|
|
local newyaw=self.object:getyaw()
|
|
-- Now if driver and lockyaw, change drivers direction.
|
|
if self.driver and self.lockyaw then
|
|
if oldyaw~=newyaw then
|
|
self.yawtarget=newyaw -- Set new target.
|
|
-- minetest.log("action", "--Cart yawtarget set "..self.yawtarget)
|
|
end
|
|
if self.driver:get_look_yaw() == nil then
|
|
return
|
|
end
|
|
local playeryaw=self.driver:get_look_yaw()-1.57
|
|
if playeryaw<0 then playeryaw=playeryaw+(math.pi*2) end
|
|
if self.yawtarget and playeryaw ~= self.yawtarget then
|
|
local diff = self.yawtarget - playeryaw
|
|
if diff>math.pi then
|
|
diff=diff-(2*math.pi)
|
|
elseif diff<(-math.pi) then
|
|
diff=diff+(2*math.pi)
|
|
end
|
|
local yawdir=cart_func:get_sign(diff)
|
|
local step=self.YAW_STEP
|
|
if math.abs(diff)<=self.YAW_STEP then
|
|
step=diff
|
|
self.yawtarget=nil
|
|
end
|
|
local setyaw=playeryaw+(step*yawdir)
|
|
self.driver:set_look_yaw(setyaw)
|
|
end -- Move yaw.
|
|
end -- lockyaw set.
|
|
|
|
if dir.y == -1 then
|
|
self.object:set_animation({x=1, y=1}, 1, 0)
|
|
elseif dir.y == 1 then
|
|
self.object:set_animation({x=2, y=2}, 1, 0)
|
|
else
|
|
self.object:set_animation({x=0, y=0}, 1, 0)
|
|
end
|
|
|
|
end
|
|
|
|
minetest.register_entity("carts:cart", cart)
|
|
|
|
minetest.register_craftitem("carts:cart", {
|
|
description = "Cart",
|
|
inventory_image = minetest.inventorycube("cart_top.png", "cart_side.png", "cart_side.png"),
|
|
wield_image = "cart_side.png",
|
|
on_place = function(itemstack, placer, pointed_thing)
|
|
if not pointed_thing.type == "node" then
|
|
return
|
|
end
|
|
if cart_func:is_rail(pointed_thing.under) then
|
|
local obj=minetest.add_entity(pointed_thing.under, "carts:cart")
|
|
minetest.log("action", "carts:cart placed at " .. pos_to_string(vector.round(obj:getpos())) .. ".")
|
|
-- minetest.chat_send_player(placer:get_player_name(),"Right click to ride, left click to push, sneak + left click to put back in inventory, jump to lock view to cart, sneak to unlock view from cart, move left and right to switch tracks, move backwards to apply handbrake.")
|
|
if not minetest.setting_getbool("creative_mode") then
|
|
itemstack:take_item()
|
|
end
|
|
return itemstack
|
|
elseif cart_func:is_rail(pointed_thing.above) then
|
|
minetest.add_entity(pointed_thing.above, "carts:cart")
|
|
if not minetest.setting_getbool("creative_mode") then
|
|
itemstack:take_item()
|
|
end
|
|
return itemstack
|
|
end
|
|
end,
|
|
})
|
|
|
|
minetest.register_craft({
|
|
output = "carts:cart",
|
|
recipe = {
|
|
{"default:steel_ingot", "", "default:steel_ingot"},
|
|
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
|
|
},
|
|
})
|
|
|
|
--
|
|
-- Mesecon support
|
|
--
|
|
|
|
minetest.register_node(":default:rail", {
|
|
description = "Rail",
|
|
drawtype = "raillike",
|
|
tiles = {"default_rail.png", "default_rail_curved.png", "default_rail_t_junction.png", "default_rail_crossing.png"},
|
|
inventory_image = "default_rail.png",
|
|
wield_image = "default_rail.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
groups = {bendy = 2, snappy = 1, dig_immediate = 2, rail = 1, connect_to_raillike = 1},
|
|
mesecons = {
|
|
effector = {
|
|
action_off = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0.5")
|
|
minetest.get_meta(pos):set_string("cart_touring_velocity", cart:get_staticdata().velocity)
|
|
end,
|
|
|
|
action_on = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0")
|
|
end,
|
|
},
|
|
},
|
|
})
|
|
|
|
minetest.register_node("carts:rail_copper", {
|
|
description = "Copper Rail",
|
|
drawtype = "raillike",
|
|
tiles = {"carts_rail_copper.png", "carts_rail_copper_curved.png", "carts_rail_copper_t_junction.png", "carts_rail_copper_crossing.png"},
|
|
inventory_image = "carts_rail_copper.png",
|
|
wield_image = "carts_rail_copper.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
groups = {bendy = 2, snappy = 1, dig_immediate = 2, rail = 1, connect_to_raillike = 1},
|
|
})
|
|
|
|
minetest.register_node("carts:rail_invisible", {
|
|
description = "Invisible Rail",
|
|
stack_max = 10000,
|
|
range = 12,
|
|
drawtype = "raillike",
|
|
tiles = {"invisible.png", "invisible.png", "invisible.png", "invisible.png"},
|
|
inventory_image = "default_rail.png^default_steel_ingot.png",
|
|
wield_image = "default_rail.png^default_steel_ingot.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
drop = "",
|
|
groups = {unbreakable = 1, rail = 1, connect_to_raillike = 1, not_in_creative_inventory = 1},
|
|
})
|
|
|
|
minetest.register_node("carts:rail_power", {
|
|
description = "Powered Rail",
|
|
drawtype = "raillike",
|
|
tiles = {"carts_rail_pwr.png", "carts_rail_curved_pwr.png", "carts_rail_t_junction_pwr.png", "carts_rail_crossing_pwr.png"},
|
|
inventory_image = "carts_rail_pwr.png",
|
|
wield_image = "carts_rail_pwr.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
groups = {bendy = 2, snappy = 1, dig_immediate = 2, rail = 1, connect_to_raillike = 1},
|
|
|
|
after_place_node = function(pos, placer, itemstack)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0.5")
|
|
minetest.get_meta(pos):set_string("cart_touring_velocity", cart:get_staticdata().velocity)
|
|
end,
|
|
|
|
mesecons = {
|
|
effector = {
|
|
action_off = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0.5")
|
|
end,
|
|
|
|
action_on = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0")
|
|
end,
|
|
},
|
|
},
|
|
})
|
|
|
|
minetest.register_node("carts:rail_power_invisible", {
|
|
description = "Invisible Powered Rail",
|
|
stack_max = 10000,
|
|
range = 12,
|
|
drawtype = "raillike",
|
|
tiles = {"invisible.png", "invisible.png", "invisible.png", "invisible.png"},
|
|
inventory_image = "default_rail.png^default_mese_crystal_fragment.png",
|
|
wield_image = "default_rail.png^default_mese_crystal_fragment.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
drop = "",
|
|
groups = {unbreakable = 1, rail = 1, connect_to_raillike = 1, not_in_creative_inventory = 1},
|
|
|
|
after_place_node = function(pos, placer, itemstack)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "10")
|
|
minetest.get_meta(pos):set_string("cart_touring_velocity", cart:get_staticdata().velocity)
|
|
end,
|
|
})
|
|
|
|
minetest.register_node("carts:rail_brake", {
|
|
description = "Brake Rail",
|
|
drawtype = "raillike",
|
|
tiles = {"carts_rail_brk.png", "carts_rail_curved_brk.png", "carts_rail_t_junction_brk.png", "carts_rail_crossing_brk.png"},
|
|
inventory_image = "carts_rail_brk.png",
|
|
wield_image = "carts_rail_brk.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
groups = {bendy = 2, snappy = 1, dig_immediate = 2, rail = 1, connect_to_raillike = 1},
|
|
|
|
after_place_node = function(pos, placer, itemstack)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "-0.2")
|
|
minetest.get_meta(pos):set_string("cart_touring_velocity", cart.TARGET_TOUR_V)
|
|
end,
|
|
|
|
mesecons = {
|
|
effector = {
|
|
action_off = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "-0.2")
|
|
end,
|
|
|
|
action_on = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0")
|
|
end,
|
|
},
|
|
},
|
|
})
|
|
|
|
minetest.register_node("carts:rail_brake_invisible", {
|
|
description = "Invisible Brake Rail",
|
|
stack_max = 10000,
|
|
range = 12,
|
|
drawtype = "raillike",
|
|
tiles = {"invisible.png", "invisible.png", "invisible.png", "invisible.png"},
|
|
inventory_image = "default_rail.png^default_coal_lump.png",
|
|
wield_image = "default_rail.png^default_coal_lump.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
drop = "",
|
|
groups = {bendy = 2, snappy = 1, dig_immediate = 2, rail = 1, connect_to_raillike = 1, not_in_creative_inventory = 1},
|
|
|
|
after_place_node = function(pos, placer, itemstack)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "-10")
|
|
minetest.get_meta(pos):set_string("cart_touring_velocity", cart:get_staticdata().velocity)
|
|
end,
|
|
})
|
|
|
|
--[[
|
|
minetest.register_node("carts:rail_tour", {
|
|
description = "Touring Rail",
|
|
drawtype = "raillike",
|
|
tiles = {"carts_rail_tour.png", "carts_rail_curved_tour.png", "carts_rail_t_junction_tour.png", "carts_rail_crossing_tour.png"},
|
|
inventory_image = "carts_rail_tour.png",
|
|
wield_image = "carts_rail_tour.png",
|
|
paramtype = "light",
|
|
sunlight_propagates = true,
|
|
is_ground_content = true,
|
|
walkable = false,
|
|
selection_box = {
|
|
type = "fixed",
|
|
fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
|
|
},
|
|
groups = {bendy = 2, snappy = 1, dig_immediate = 2, rail = 1, connect_to_raillike = 1},
|
|
|
|
after_place_node = function(pos, placer, itemstack)
|
|
if not mesecon then
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0.5")
|
|
minetest.get_meta(pos):set_string("cart_touring_velocity", cart.TARGET_TOUR_V)
|
|
end
|
|
end,
|
|
|
|
mesecons = {
|
|
effector = {
|
|
action_on = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0.5")
|
|
end,
|
|
|
|
action_off = function(pos, node)
|
|
minetest.get_meta(pos):set_string("cart_acceleration", "0")
|
|
end,
|
|
},
|
|
},
|
|
})
|
|
--]]
|
|
|
|
minetest.register_craft({
|
|
output = "carts:rail_copper 16",
|
|
recipe = {
|
|
{"default:copper_ingot", "group:stick", "default:copper_ingot"},
|
|
{"default:copper_ingot", "group:stick", "default:copper_ingot"},
|
|
{"default:copper_ingot", "group:stick", "default:copper_ingot"},
|
|
}
|
|
})
|
|
|
|
minetest.register_craft({
|
|
type = "shapeless",
|
|
output = "carts:rail_power",
|
|
recipe = {"group:rail", "default:mese_crystal_fragment"},
|
|
})
|
|
|
|
minetest.register_craft({
|
|
type = "shapeless",
|
|
output = "carts:rail_brake",
|
|
recipe = {"group:rail", "default:coal_lump"},
|
|
})
|
|
|
|
minetest.register_craft({
|
|
type = "shapeless",
|
|
output = "carts:rail_tour",
|
|
recipe = {"group:rail", "default:clay_lump"},
|
|
})
|
|
|
|
minetest.register_alias("carts:powerrail", "carts:rail_power")
|
|
minetest.register_alias("carts:power_rail", "carts:rail_power")
|
|
minetest.register_alias("carts:brakerail", "carts:rail_brake")
|
|
minetest.register_alias("carts:brake_rail", "carts:rail_power")
|
|
minetest.register_alias("carts:tourrail", "carts:rail_tour")
|
|
|
|
if minetest.setting_getbool("log_mods") then
|
|
minetest.log("action", "Carbone: [carts] loaded.")
|
|
end
|
|
|
|
-- temporary
|
|
minetest.register_abm({
|
|
nodes = {"group:rail"},
|
|
func = function(pos)
|
|
local name = minetest.get_node(pos).name
|
|
minetest.remove_node(pos)
|
|
minetest.place_node(pos,{name = name})
|
|
end
|
|
})
|