2017-01-05 16:23:14 +02:00
mcl_minecarts = { }
mcl_minecarts.modpath = minetest.get_modpath ( " mcl_minecarts " )
mcl_minecarts.speed_max = 10
2017-01-24 03:31:49 +02:00
local vector_floor = function ( v )
2017-01-05 16:23:14 +02:00
return {
x = math.floor ( v.x ) ,
y = math.floor ( v.y ) ,
z = math.floor ( v.z )
}
end
dofile ( mcl_minecarts.modpath .. " /functions.lua " )
dofile ( mcl_minecarts.modpath .. " /rails.lua " )
mcl_minecarts.cart = {
physical = false ,
2017-08-15 14:20:11 +03:00
collisionbox = { - 10 / 16. , - 0.5 , - 10 / 16 , 10 / 16 , 0.25 , 10 / 16 } ,
2017-01-05 16:23:14 +02:00
visual = " mesh " ,
2017-08-15 14:20:11 +03:00
mesh = " mcl_minecarts_minecart.b3d " ,
2017-01-05 16:23:14 +02:00
visual_size = { x = 1 , y = 1 } ,
2017-08-15 14:20:11 +03:00
textures = { " mcl_minecarts_minecart.png " } ,
2017-01-05 16:23:14 +02:00
2017-01-05 16:40:41 +02:00
_driver = nil ,
_punched = false , -- used to re-send _velocity and position
_velocity = { x = 0 , y = 0 , z = 0 } , -- only used on punch
2017-03-06 01:22:41 +02:00
_start_pos = nil , -- Used to calculate distance for “On A Rail” achievement
2017-01-05 16:40:41 +02:00
_old_dir = { x = 0 , y = 0 , z = 0 } ,
_old_pos = nil ,
_old_switch = 0 ,
_railtype = nil ,
2017-01-05 16:23:14 +02:00
}
function mcl_minecarts . cart : on_rightclick ( clicker )
if not clicker or not clicker : is_player ( ) then
return
end
local player_name = clicker : get_player_name ( )
2017-01-05 16:40:41 +02:00
if self._driver and player_name == self._driver then
self._driver = nil
2017-03-06 01:22:41 +02:00
self._start_pos = nil
2017-01-05 16:23:14 +02:00
clicker : set_detach ( )
2017-01-05 16:40:41 +02:00
elseif not self._driver then
self._driver = player_name
2017-03-06 01:22:41 +02:00
self._start_pos = self.object : getpos ( )
2017-02-11 19:05:50 +02:00
mcl_player.player_attached [ player_name ] = true
2017-01-05 16:23:14 +02:00
clicker : set_attach ( self.object , " " , { x = 0 , y = 3 , z = 0 } , { x = 0 , y = 0 , z = 0 } )
end
end
function mcl_minecarts . cart : on_activate ( staticdata , dtime_s )
self.object : set_armor_groups ( { immortal = 1 } )
end
function mcl_minecarts . cart : on_punch ( puncher , time_from_last_punch , tool_capabilities , direction )
local pos = self.object : getpos ( )
2017-01-05 16:40:41 +02:00
if not self._railtype then
2017-01-24 03:31:49 +02:00
local node = minetest.get_node ( vector_floor ( pos ) ) . name
2017-01-05 16:40:41 +02:00
self._railtype = minetest.get_item_group ( node , " connect_to_raillike " )
2017-01-05 16:23:14 +02:00
end
if not puncher or not puncher : is_player ( ) then
2017-01-05 16:40:41 +02:00
local cart_dir = mcl_minecarts : get_rail_direction ( pos , { x = 1 , y = 0 , z = 0 } , nil , nil , self._railtype )
2017-01-05 16:23:14 +02:00
if vector.equals ( cart_dir , { x = 0 , y = 0 , z = 0 } ) then
return
end
2017-01-05 16:40:41 +02:00
self._velocity = vector.multiply ( cart_dir , 3 )
self._old_pos = nil
self._punched = true
2017-01-05 16:23:14 +02:00
return
end
if puncher : get_player_control ( ) . sneak then
2017-01-05 16:40:41 +02:00
if self._driver then
if self._old_pos then
self.object : setpos ( self._old_pos )
2017-01-05 16:23:14 +02:00
end
2017-02-11 19:05:50 +02:00
mcl_player.player_attached [ self._driver ] = nil
2017-01-05 16:40:41 +02:00
local player = minetest.get_player_by_name ( self._driver )
2017-01-05 16:23:14 +02:00
if player then
player : set_detach ( )
end
end
2017-08-15 03:51:40 +03:00
if not minetest.settings : get_bool ( " creative_mode " ) then
minetest.add_item ( self.object : getpos ( ) , " mcl_minecarts:minecart " )
end
2017-01-05 16:23:14 +02:00
self.object : remove ( )
return
end
local vel = self.object : getvelocity ( )
2017-01-05 16:40:41 +02:00
if puncher : get_player_name ( ) == self._driver then
2017-01-05 16:23:14 +02:00
if math.abs ( vel.x + vel.z ) > 7 then
return
end
end
local punch_dir = mcl_minecarts : velocity_to_dir ( puncher : get_look_dir ( ) )
punch_dir.y = 0
2017-01-05 16:40:41 +02:00
local cart_dir = mcl_minecarts : get_rail_direction ( pos , punch_dir , nil , nil , self._railtype )
2017-01-05 16:23:14 +02:00
if vector.equals ( cart_dir , { x = 0 , y = 0 , z = 0 } ) then
return
end
time_from_last_punch = math.min ( time_from_last_punch , tool_capabilities.full_punch_interval )
local f = 3 * ( time_from_last_punch / tool_capabilities.full_punch_interval )
2017-01-05 16:40:41 +02:00
self._velocity = vector.multiply ( cart_dir , f )
self._old_pos = nil
self._punched = true
2017-01-05 16:23:14 +02:00
end
function mcl_minecarts . cart : on_step ( dtime )
local vel = self.object : getvelocity ( )
local update = { }
2017-01-05 16:40:41 +02:00
if self._punched then
vel = vector.add ( vel , self._velocity )
2017-01-05 16:23:14 +02:00
self.object : setvelocity ( vel )
2017-01-05 16:40:41 +02:00
self._old_dir . y = 0
2017-01-05 16:23:14 +02:00
elseif vector.equals ( vel , { x = 0 , y = 0 , z = 0 } ) then
return
end
local dir , last_switch = nil , nil
local pos = self.object : getpos ( )
2017-01-05 16:40:41 +02:00
if self._old_pos and not self._punched then
2017-01-24 03:31:49 +02:00
local flo_pos = vector_floor ( pos )
local flo_old = vector_floor ( self._old_pos )
2017-01-05 16:23:14 +02:00
if vector.equals ( flo_pos , flo_old ) then
return
end
end
local ctrl , player = nil , nil
2017-01-05 16:40:41 +02:00
if self._driver then
player = minetest.get_player_by_name ( self._driver )
2017-01-05 16:23:14 +02:00
if player then
ctrl = player : get_player_control ( )
end
end
2017-01-05 16:40:41 +02:00
if self._old_pos then
local diff = vector.subtract ( self._old_pos , pos )
2017-01-05 16:23:14 +02:00
for _ , v in ipairs ( { " x " , " y " , " z " } ) do
if math.abs ( diff [ v ] ) > 1.1 then
2017-01-05 16:40:41 +02:00
local expected_pos = vector.add ( self._old_pos , self._old_dir )
dir , last_switch = mcl_minecarts : get_rail_direction ( pos , self._old_dir , ctrl , self._old_switch , self._railtype )
2017-01-05 16:23:14 +02:00
if vector.equals ( dir , { x = 0 , y = 0 , z = 0 } ) then
dir = false
pos = vector.new ( expected_pos )
update.pos = true
end
break
end
end
end
if vel.y == 0 then
for _ , v in ipairs ( { " x " , " z " } ) do
if vel [ v ] ~= 0 and math.abs ( vel [ v ] ) < 0.9 then
vel [ v ] = 0
update.vel = true
end
end
end
local cart_dir = mcl_minecarts : velocity_to_dir ( vel )
local max_vel = mcl_minecarts.speed_max
if not dir then
2017-01-05 16:40:41 +02:00
dir , last_switch = mcl_minecarts : get_rail_direction ( pos , cart_dir , ctrl , self._old_switch , self._railtype )
2017-01-05 16:23:14 +02:00
end
local new_acc = { x = 0 , y = 0 , z = 0 }
if vector.equals ( dir , { x = 0 , y = 0 , z = 0 } ) then
vel = { x = 0 , y = 0 , z = 0 }
update.vel = true
else
-- If the direction changed
2017-01-05 16:40:41 +02:00
if dir.x ~= 0 and self._old_dir . z ~= 0 then
2017-01-05 16:23:14 +02:00
vel.x = dir.x * math.abs ( vel.z )
vel.z = 0
pos.z = math.floor ( pos.z + 0.5 )
update.pos = true
end
2017-01-05 16:40:41 +02:00
if dir.z ~= 0 and self._old_dir . x ~= 0 then
2017-01-05 16:23:14 +02:00
vel.z = dir.z * math.abs ( vel.x )
vel.x = 0
pos.x = math.floor ( pos.x + 0.5 )
update.pos = true
end
-- Up, down?
2017-01-05 16:40:41 +02:00
if dir.y ~= self._old_dir . y then
2017-01-05 16:23:14 +02:00
vel.y = dir.y * math.abs ( vel.x + vel.z )
pos = vector.round ( pos )
update.pos = true
end
-- Slow down or speed up..
local acc = dir.y * - 1.8
local speed_mod = tonumber ( minetest.get_meta ( pos ) : get_string ( " cart_acceleration " ) )
if speed_mod and speed_mod ~= 0 then
if speed_mod > 0 then
for _ , v in ipairs ( { " x " , " y " , " z " } ) do
if math.abs ( vel [ v ] ) >= max_vel then
speed_mod = 0
break
end
end
end
acc = acc + ( speed_mod * 8 )
else
acc = acc - 0.4
end
new_acc = vector.multiply ( dir , acc )
end
self.object : setacceleration ( new_acc )
2017-01-05 16:40:41 +02:00
self._old_pos = vector.new ( pos )
self._old_dir = vector.new ( dir )
self._old_switch = last_switch
2017-01-05 16:23:14 +02:00
-- Limits
for _ , v in ipairs ( { " x " , " y " , " z " } ) do
if math.abs ( vel [ v ] ) > max_vel then
vel [ v ] = mcl_minecarts : get_sign ( vel [ v ] ) * max_vel
update.vel = true
end
end
2017-01-05 16:40:41 +02:00
if self._punched then
self._punched = false
2017-01-05 16:23:14 +02:00
end
2017-03-06 01:22:41 +02:00
-- Give achievement when player reached a distance of 1000 nodes from the start position
if self._driver and ( vector.distance ( self._start_pos , pos ) >= 1000 ) then
awards.unlock ( self._driver , " mcl:onARail " )
end
2017-01-05 16:23:14 +02:00
if not ( update.vel or update.pos ) then
return
end
local yaw = 0
if dir.x < 0 then
yaw = 0.5
elseif dir.x > 0 then
yaw = 1.5
elseif dir.z < 0 then
yaw = 1
end
2017-08-15 14:53:05 +03:00
if update.pos then
self.object : setyaw ( yaw * math.pi )
end
2017-01-05 16:23:14 +02:00
local anim = { x = 0 , y = 0 }
if dir.y == - 1 then
anim = { x = 1 , y = 1 }
elseif dir.y == 1 then
anim = { x = 2 , y = 2 }
end
self.object : set_animation ( anim , 1 , 0 )
self.object : setvelocity ( vel )
if update.pos then
self.object : setpos ( pos )
end
update = nil
end
minetest.register_entity ( " mcl_minecarts:minecart " , mcl_minecarts.cart )
minetest.register_craftitem ( " mcl_minecarts:minecart " , {
description = " Minecart " ,
2017-03-11 22:12:31 +02:00
_doc_items_longdesc = " Minecarts can be used for a quick transportion on rails. " .. " \n " ..
" Minecarts only ride on rails and always follow the tracks. At a T-junction with no straight way ahead, they turn left. The speed is affected by the rail type. " ,
_doc_items_usagehelp = " You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving. " .. " \n " ..
" To obtain the minecart, punch it while holding down the sneak key. " ,
2017-02-17 02:49:16 +02:00
inventory_image = " mcl_minecarts_minecart_normal.png " ,
wield_image = " mcl_minecarts_minecart_normal.png " ,
2017-01-17 00:34:40 +02:00
stack_max = 1 ,
2017-01-05 16:23:14 +02:00
on_place = function ( itemstack , placer , pointed_thing )
if not pointed_thing.type == " node " then
return
end
2017-03-02 16:44:31 +02:00
-- Call on_rightclick if the pointed node defines it
local node = minetest.get_node ( pointed_thing.under )
if placer and not placer : get_player_control ( ) . sneak then
if minetest.registered_nodes [ node.name ] and minetest.registered_nodes [ node.name ] . on_rightclick then
return minetest.registered_nodes [ node.name ] . on_rightclick ( pointed_thing.under , node , placer , itemstack ) or itemstack
end
end
2017-08-15 14:53:05 +03:00
local railpos
2017-01-05 16:23:14 +02:00
if mcl_minecarts : is_rail ( pointed_thing.under ) then
2017-08-15 14:53:05 +03:00
railpos = pointed_thing.under
2017-01-05 16:23:14 +02:00
elseif mcl_minecarts : is_rail ( pointed_thing.above ) then
2017-08-15 14:53:05 +03:00
railpos = pointed_thing.under
else
return
end
local cart = minetest.add_entity ( railpos , " mcl_minecarts:minecart " )
local railtype = minetest.get_item_group ( node.name , " connect_to_raillike " )
local cart_dir = mcl_minecarts : get_rail_direction ( railpos , { x = 1 , y = 0 , z = 0 } , nil , nil , railtype )
cart : setyaw ( minetest.dir_to_yaw ( cart_dir ) )
2017-01-05 16:23:14 +02:00
2017-08-15 03:51:40 +03:00
if not minetest.settings : get_bool ( " creative_mode " ) then
itemstack : take_item ( )
end
2017-01-05 16:23:14 +02:00
return itemstack
end ,
2017-01-20 05:54:09 +02:00
groups = { minecart = 1 , transport = 1 } ,
2017-01-05 16:23:14 +02:00
} )
2017-03-21 04:58:53 +02:00
if minetest.get_modpath ( " doc_identifier " ) ~= nil then
doc.sub . identifier.register_object ( " mcl_minecarts:minecart " , " craftitems " , " mcl_minecarts:minecart " )
end
2017-01-05 16:23:14 +02:00
minetest.register_craft ( {
output = " mcl_minecarts:minecart " ,
recipe = {
2017-02-11 22:14:40 +02:00
{ " mcl_core:iron_ingot " , " " , " mcl_core:iron_ingot " } ,
{ " mcl_core:iron_ingot " , " mcl_core:iron_ingot " , " mcl_core:iron_ingot " } ,
2017-01-05 16:23:14 +02:00
} ,
} )