2021-05-29 14:12:33 +00:00
local modname = minetest.get_current_modname ( )
local S = minetest.get_translator ( modname )
2019-03-07 19:43:39 +00:00
2021-03-29 22:58:45 +00:00
local has_mcl_wip = minetest.get_modpath ( " mcl_wip " )
2017-01-05 14:23:14 +00:00
mcl_minecarts = { }
2021-05-29 14:12:33 +00:00
mcl_minecarts.modpath = minetest.get_modpath ( modname )
2017-01-05 14:23:14 +00:00
mcl_minecarts.speed_max = 10
2019-01-30 02:36:36 +00:00
mcl_minecarts.check_float_time = 15
2017-01-05 14:23:14 +00:00
dofile ( mcl_minecarts.modpath .. " /functions.lua " )
dofile ( mcl_minecarts.modpath .. " /rails.lua " )
2023-03-28 00:51:51 +00:00
local LOGGING_ON = minetest.settings : get_bool ( " mcl_logging_minecarts " , false )
local function mcl_log ( message )
if LOGGING_ON then
mcl_util.mcl_log ( message , " [Minecarts] " , true )
end
end
2020-01-30 21:42:53 +00:00
local function detach_driver ( self )
if not self._driver then
return
end
2021-03-13 11:30:33 +00:00
mcl_player.player_attached [ self._driver ] = nil
local player = minetest.get_player_by_name ( self._driver )
2020-01-30 21:42:53 +00:00
self._driver = nil
self._start_pos = nil
2021-03-13 11:30:33 +00:00
if player then
player : set_detach ( )
player : set_eye_offset ( { x = 0 , y = 0 , z = 0 } , { x = 0 , y = 0 , z = 0 } )
mcl_player.player_set_animation ( player , " stand " , 30 )
end
2020-01-30 21:42:53 +00:00
end
2020-01-30 22:11:16 +00:00
local function activate_tnt_minecart ( self , timer )
2020-01-30 21:05:18 +00:00
if self._boomtimer then
return
end
self.object : set_armor_groups ( { immortal = 1 } )
2020-01-30 22:11:16 +00:00
if timer then
self._boomtimer = timer
else
self._boomtimer = tnt.BOOMTIMER
end
2020-01-30 21:05:18 +00:00
self.object : set_properties ( { textures = {
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_minecarts_minecart.png " ,
} } )
self._blinktimer = tnt.BLINKTIMER
2020-04-06 22:55:45 +00:00
minetest.sound_play ( " tnt_ignite " , { pos = self.object : get_pos ( ) , gain = 1.0 , max_hear_distance = 15 } , true )
2020-01-30 21:05:18 +00:00
end
2020-01-30 21:42:53 +00:00
local activate_normal_minecart = detach_driver
2023-03-28 00:51:51 +00:00
local function hopper_take_item ( self , dtime )
local pos = self.object : get_pos ( )
if not pos then return end
if not self or self.name ~= " mcl_minecarts:hopper_minecart " then return end
if mcl_util.check_dtime_timer ( self , dtime , " hoppermc_take " , 0.15 ) then
--minetest.log("The check timer was triggered: " .. dump(pos) .. ", name:" .. self.name)
else
--minetest.log("The check timer was not triggered")
return
end
--mcl_log("self.itemstring: ".. self.itemstring)
local above_pos = vector.offset ( pos , 0 , 0.9 , 0 )
--mcl_log("self.itemstring: ".. minetest.pos_to_string(above_pos))
local objs = minetest.get_objects_inside_radius ( above_pos , 1.25 )
if objs then
mcl_log ( " there is an itemstring. Number of objs: " .. # objs )
for k , v in pairs ( objs ) do
local ent = v : get_luaentity ( )
if ent._removed or not ent.itemstring or ent.itemstring == " " then
--minetest.log("Ignore this item")
break
end
-- Don't forget actual hoppers
local taken_items = false
mcl_log ( " ent.name: " .. tostring ( ent.name ) )
mcl_log ( " ent pos: " .. tostring ( ent.object : get_pos ( ) ) )
local inv = mcl_entity_invs.load_inv ( self , 5 )
if not inv then
mcl_log ( " No inv " )
return false
end
local current_itemstack = ItemStack ( ent.itemstring )
mcl_log ( " inv. size: " .. self._inv_size )
if inv : room_for_item ( " main " , current_itemstack ) then
mcl_log ( " Room " )
inv : add_item ( " main " , current_itemstack )
ent.object : get_luaentity ( ) . itemstring = " "
ent.object : remove ( )
taken_items = true
else
mcl_log ( " no Room " )
end
if not taken_items then
local items_remaining = current_itemstack : get_count ( )
-- This will take part of a floating item stack if no slot can hold the full amount
for i = 1 , self._inv_size , 1 do
local stack = inv : get_stack ( " main " , i )
mcl_log ( " i: " .. tostring ( i ) )
mcl_log ( " Items remaining: " .. items_remaining )
mcl_log ( " Name: " .. tostring ( stack : get_name ( ) ) )
if current_itemstack : get_name ( ) == stack : get_name ( ) then
mcl_log ( " We have a match. Name: " .. tostring ( stack : get_name ( ) ) )
local room_for = stack : get_stack_max ( ) - stack : get_count ( )
mcl_log ( " Room for: " .. tostring ( room_for ) )
if room_for == 0 then
-- Do nothing
mcl_log ( " No room " )
elseif room_for < items_remaining then
mcl_log ( " We have more items remaining than space " )
items_remaining = items_remaining - room_for
stack : set_count ( stack : get_stack_max ( ) )
inv : set_stack ( " main " , i , stack )
taken_items = true
else
local new_stack_size = stack : get_count ( ) + items_remaining
stack : set_count ( new_stack_size )
mcl_log ( " We have more than enough space. Now holds: " .. new_stack_size )
inv : set_stack ( " main " , i , stack )
items_remaining = 0
ent.object : get_luaentity ( ) . itemstring = " "
ent.object : remove ( )
taken_items = true
break
end
mcl_log ( " Count: " .. tostring ( stack : get_count ( ) ) )
mcl_log ( " stack max: " .. tostring ( stack : get_stack_max ( ) ) )
--mcl_log("Is it empty: " .. stack:to_string())
end
if i == self._inv_size and taken_items then
mcl_log ( " We are on last item and still have items left. Set final stack size: " .. items_remaining )
current_itemstack : set_count ( items_remaining )
--mcl_log("Itemstack2: " .. current_itemstack:to_string())
ent.itemstring = current_itemstack : to_string ( )
end
end
end
--Add in, and delete
if taken_items then
mcl_log ( " Saving " )
mcl_entity_invs.save_inv ( ent )
return taken_items
else
mcl_log ( " No need to save " )
end
end
end
return false
end
2017-08-29 00:36:50 +00:00
-- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID
local entity_mapping = { }
2017-08-28 12:19:46 +00:00
2020-01-30 21:05:18 +00:00
local function register_entity ( entity_id , mesh , textures , drop , on_rightclick , on_activate_by_rail )
2017-08-28 12:19:46 +00:00
local cart = {
physical = false ,
collisionbox = { - 10 / 16. , - 0.5 , - 10 / 16 , 10 / 16 , 0.25 , 10 / 16 } ,
visual = " mesh " ,
mesh = mesh ,
visual_size = { x = 1 , y = 1 } ,
textures = textures ,
2017-08-28 12:35:56 +00:00
on_rightclick = on_rightclick ,
2021-03-13 11:30:33 +00:00
_driver = nil , -- player who sits in and controls the minecart (only for minecart!)
2023-03-04 20:14:59 +00:00
_passenger = nil , -- for mobs
2017-08-28 12:19:46 +00:00
_punched = false , -- used to re-send _velocity and position
_velocity = { x = 0 , y = 0 , z = 0 } , -- only used on punch
_start_pos = nil , -- Used to calculate distance for “On A Rail” achievement
2019-01-30 02:16:59 +00:00
_last_float_check = nil , -- timestamp of last time the cart was checked to be still on a rail
2020-01-30 19:38:31 +00:00
_fueltime = nil , -- how many seconds worth of fuel is left. Only used by minecart with furnace
2020-01-30 21:05:18 +00:00
_boomtimer = nil , -- how many seconds are left before exploding
_blinktimer = nil , -- how many seconds are left before TNT blinking
_blink = false , -- is TNT blink texture active?
2017-08-28 12:19:46 +00:00
_old_dir = { x = 0 , y = 0 , z = 0 } ,
_old_pos = nil ,
2017-08-28 14:59:10 +00:00
_old_vel = { x = 0 , y = 0 , z = 0 } ,
2017-08-28 12:19:46 +00:00
_old_switch = 0 ,
_railtype = nil ,
}
function cart : on_activate ( staticdata , dtime_s )
2020-01-30 21:05:18 +00:00
-- Initialize
2019-01-30 01:04:12 +00:00
local data = minetest.deserialize ( staticdata )
if type ( data ) == " table " then
self._railtype = data._railtype
2023-03-20 13:16:26 +00:00
self._passenger = data._passenger
2019-01-30 01:04:12 +00:00
end
2017-08-28 12:19:46 +00:00
self.object : set_armor_groups ( { immortal = 1 } )
2020-01-30 21:05:18 +00:00
-- Activate cart if on activator rail
if self.on_activate_by_rail then
local pos = self.object : get_pos ( )
local node = minetest.get_node ( vector.floor ( pos ) )
if node.name == " mcl_minecarts:activator_rail_on " then
self : on_activate_by_rail ( )
end
end
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
function cart : on_punch ( puncher , time_from_last_punch , tool_capabilities , direction )
2021-02-28 21:43:00 +00:00
local pos = self.object : get_pos ( )
2021-03-13 11:30:33 +00:00
if not self._railtype then
local node = minetest.get_node ( vector.floor ( pos ) ) . name
self._railtype = minetest.get_item_group ( node , " connect_to_raillike " )
end
2017-01-05 14:23:14 +00:00
2021-03-13 11:30:33 +00:00
if not puncher or not puncher : is_player ( ) then
local cart_dir = mcl_minecarts : get_rail_direction ( pos , { x = 1 , y = 0 , z = 0 } , nil , nil , self._railtype )
if vector.equals ( cart_dir , { x = 0 , y = 0 , z = 0 } ) then
return
end
self._velocity = vector.multiply ( cart_dir , 3 )
self._old_pos = nil
self._punched = true
return
2017-01-05 14:23:14 +00:00
end
2021-03-13 11:30:33 +00:00
-- Punch+sneak: Pick up minecart (unless TNT was ignited)
if puncher : get_player_control ( ) . sneak and not self._boomtimer then
if self._driver then
if self._old_pos then
self.object : set_pos ( self._old_pos )
end
detach_driver ( self )
end
-- Disable detector rail
local rou_pos = vector.round ( pos )
local node = minetest.get_node ( rou_pos )
if node.name == " mcl_minecarts:detector_rail_on " then
local newnode = { name = " mcl_minecarts:detector_rail " , param2 = node.param2 }
minetest.swap_node ( rou_pos , newnode )
mesecon.receptor_off ( rou_pos )
2017-08-28 23:28:32 +00:00
end
2021-03-13 11:30:33 +00:00
-- Drop items and remove cart entity
if not minetest.is_creative_enabled ( puncher : get_player_name ( ) ) then
for d = 1 , # drop do
minetest.add_item ( self.object : get_pos ( ) , drop [ d ] )
end
elseif puncher and puncher : is_player ( ) then
local inv = puncher : get_inventory ( )
for d = 1 , # drop do
if not inv : contains_item ( " main " , drop [ d ] ) then
inv : add_item ( " main " , drop [ d ] )
end
2019-02-06 20:56:52 +00:00
end
2017-01-05 14:23:14 +00:00
end
2021-03-13 11:30:33 +00:00
self.object : remove ( )
return
end
local vel = self.object : get_velocity ( )
if puncher : get_player_name ( ) == self._driver then
if math.abs ( vel.x + vel.z ) > 7 then
return
end
2017-08-15 00:51:40 +00:00
end
2017-08-28 12:19:46 +00:00
2021-03-13 11:30:33 +00:00
local punch_dir = mcl_minecarts : velocity_to_dir ( puncher : get_look_dir ( ) )
punch_dir.y = 0
local cart_dir = mcl_minecarts : get_rail_direction ( pos , punch_dir , nil , nil , self._railtype )
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 )
self._velocity = vector.multiply ( cart_dir , f )
self._old_pos = nil
self._punched = true
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
2020-01-30 21:05:18 +00:00
cart.on_activate_by_rail = on_activate_by_rail
2023-03-20 13:29:25 +00:00
local passenger_attach_position = vector.new ( 0 , - 1.75 , 0 )
2017-08-28 12:19:46 +00:00
function cart : on_step ( dtime )
2023-03-28 00:51:51 +00:00
hopper_take_item ( self , dtime )
2021-02-25 20:26:27 +00:00
local ctrl , player = nil , nil
2021-03-13 11:30:33 +00:00
if self._driver then
player = minetest.get_player_by_name ( self._driver )
if player then
ctrl = player : get_player_control ( )
-- player detach
if ctrl.sneak then
detach_driver ( self )
return
2021-02-25 20:26:27 +00:00
end
end
end
2021-03-13 11:30:33 +00:00
local vel = self.object : get_velocity ( )
local update = { }
2019-01-30 02:16:59 +00:00
if self._last_float_check == nil then
self._last_float_check = 0
else
self._last_float_check = self._last_float_check + dtime
end
2021-10-24 19:36:45 +00:00
local pos , rou_pos , node = self.object : get_pos ( )
local r = 0.6
for _ , node_pos in pairs ( { { r , 0 } , { 0 , r } , { - r , 0 } , { 0 , - r } } ) do
if minetest.get_node ( vector.offset ( pos , node_pos [ 1 ] , 0 , node_pos [ 2 ] ) ) . name == " mcl_core:cactus " then
detach_driver ( self )
for d = 1 , # drop do
minetest.add_item ( pos , drop [ d ] )
end
self.object : remove ( )
return
end
end
2023-03-04 20:14:59 +00:00
-- Grab mob
2023-03-19 22:15:05 +00:00
if math.random ( 1 , 20 ) > 15 and not self._passenger then
2023-03-04 20:14:59 +00:00
if self.name == " mcl_minecarts:minecart " then
local mobsnear = minetest.get_objects_inside_radius ( self.object : get_pos ( ) , 1.3 )
for n = 1 , # mobsnear do
local mob = mobsnear [ n ]
if mob then
local entity = mob : get_luaentity ( )
if entity and entity.is_mob then
2023-03-19 22:15:05 +00:00
self._passenger = entity
2023-03-20 13:29:25 +00:00
mob : set_attach ( self.object , " " , passenger_attach_position , vector.zero ( ) )
2023-03-04 20:14:59 +00:00
break
end
end
end
end
elseif self._passenger then
2023-05-07 20:11:48 +00:00
local passenger_pos = self._passenger . object : get_pos ( )
if not passenger_pos then
self._passenger = nil
2023-03-04 20:14:59 +00:00
end
end
2019-01-30 02:16:59 +00:00
-- Drop minecart if it isn't on a rail anymore
if self._last_float_check >= mcl_minecarts.check_float_time then
2021-03-13 11:30:33 +00:00
pos = self.object : get_pos ( )
rou_pos = vector.round ( pos )
node = minetest.get_node ( rou_pos )
local g = minetest.get_item_group ( node.name , " connect_to_raillike " )
2021-05-29 14:12:33 +00:00
if g ~= self._railtype and self._railtype then
2019-01-30 02:16:59 +00:00
-- Detach driver
2021-02-25 20:26:27 +00:00
if player then
2019-01-30 02:16:59 +00:00
if self._old_pos then
2019-03-06 03:38:57 +00:00
self.object : set_pos ( self._old_pos )
2019-01-30 02:16:59 +00:00
end
mcl_player.player_attached [ self._driver ] = nil
2021-02-25 20:26:27 +00:00
player : set_detach ( )
player : set_eye_offset ( { x = 0 , y = 0 , z = 0 } , { x = 0 , y = 0 , z = 0 } )
2019-01-30 02:16:59 +00:00
end
2020-01-30 21:05:18 +00:00
-- Explode if already ignited
if self._boomtimer then
self.object : remove ( )
2020-04-17 18:36:39 +00:00
mcl_explosions.explode ( pos , 4 , { drop_chance = 1.0 } )
2020-01-30 21:05:18 +00:00
return
end
2023-03-28 00:51:51 +00:00
-- Do not drop minecart. It goes off the rails too frequently, and anyone using them for farms won't
-- notice and lose their iron and not bother. Not cool until fixed.
2019-01-30 02:16:59 +00:00
end
self._last_float_check = 0
end
2020-01-30 21:05:18 +00:00
-- Update furnace stuff
2020-01-30 19:38:31 +00:00
if self._fueltime and self._fueltime > 0 then
self._fueltime = self._fueltime - dtime
if self._fueltime <= 0 then
self.object : set_properties ( { textures =
{
" default_furnace_top.png " ,
" default_furnace_top.png " ,
" default_furnace_front.png " ,
" default_furnace_side.png " ,
" default_furnace_side.png " ,
" default_furnace_side.png " ,
" mcl_minecarts_minecart.png " ,
} } )
self._fueltime = 0
end
end
local has_fuel = self._fueltime and self._fueltime > 0
2020-01-30 21:05:18 +00:00
-- Update TNT stuff
if self._boomtimer then
-- Explode
self._boomtimer = self._boomtimer - dtime
local pos = self.object : get_pos ( )
if self._boomtimer <= 0 then
self.object : remove ( )
2020-04-17 18:36:39 +00:00
mcl_explosions.explode ( pos , 4 , { drop_chance = 1.0 } )
2020-01-30 21:05:18 +00:00
return
else
tnt.smoke_step ( pos )
end
end
if self._blinktimer then
self._blinktimer = self._blinktimer - dtime
if self._blinktimer <= 0 then
self._blink = not self._blink
if self._blink then
self.object : set_properties ( { textures =
{
" default_tnt_top.png " ,
" default_tnt_bottom.png " ,
" default_tnt_side.png " ,
" default_tnt_side.png " ,
" default_tnt_side.png " ,
" default_tnt_side.png " ,
" mcl_minecarts_minecart.png " ,
} } )
else
self.object : set_properties ( { textures =
{
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_tnt_blink.png " ,
" mcl_minecarts_minecart.png " ,
} } )
end
self._blinktimer = tnt.BLINKTIMER
end
end
2021-03-13 11:30:33 +00:00
if self._punched then
2017-08-28 12:19:46 +00:00
vel = vector.add ( vel , self._velocity )
2019-03-06 03:38:57 +00:00
self.object : set_velocity ( vel )
2017-08-28 12:19:46 +00:00
self._old_dir . y = 0
2020-01-30 19:38:31 +00:00
elseif vector.equals ( vel , { x = 0 , y = 0 , z = 0 } ) and ( not has_fuel ) then
2017-01-05 14:23:14 +00:00
return
end
2017-08-28 12:19:46 +00:00
local dir , last_switch = nil , nil
2019-01-30 02:16:59 +00:00
if not pos then
2019-02-01 05:33:07 +00:00
pos = self.object : get_pos ( )
2019-01-30 02:16:59 +00:00
end
2017-08-28 12:19:46 +00:00
if self._old_pos and not self._punched then
2017-08-28 23:28:32 +00:00
local flo_pos = vector.floor ( pos )
local flo_old = vector.floor ( self._old_pos )
2020-01-30 19:38:31 +00:00
if vector.equals ( flo_pos , flo_old ) and ( not has_fuel ) then
2017-08-28 12:19:46 +00:00
return
2017-08-28 23:28:32 +00:00
-- Prevent querying the same node over and over again
end
2019-01-30 02:16:59 +00:00
if not rou_pos then
rou_pos = vector.round ( pos )
end
2020-05-16 18:13:22 +00:00
local rou_old = vector.round ( self._old_pos )
2019-01-30 02:16:59 +00:00
if not node then
node = minetest.get_node ( rou_pos )
end
2017-08-28 23:28:32 +00:00
local node_old = minetest.get_node ( rou_old )
2019-01-30 02:16:59 +00:00
-- Update detector rails
2017-08-28 23:28:32 +00:00
if node.name == " mcl_minecarts:detector_rail " then
local newnode = { name = " mcl_minecarts:detector_rail_on " , param2 = node.param2 }
minetest.swap_node ( rou_pos , newnode )
mesecon.receptor_on ( rou_pos )
end
if node_old.name == " mcl_minecarts:detector_rail_on " then
local newnode = { name = " mcl_minecarts:detector_rail " , param2 = node_old.param2 }
minetest.swap_node ( rou_old , newnode )
mesecon.receptor_off ( rou_old )
2017-01-05 14:23:14 +00:00
end
2020-01-30 21:05:18 +00:00
-- Activate minecart if on activator rail
if node_old.name == " mcl_minecarts:activator_rail_on " and self.on_activate_by_rail then
self : on_activate_by_rail ( )
end
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
2017-08-28 14:59:10 +00:00
-- Stop cart if velocity vector flips
if self._old_vel and self._old_vel . y == 0 and
( self._old_vel . x * vel.x < 0 or self._old_vel . z * vel.z < 0 ) then
self._old_vel = { x = 0 , y = 0 , z = 0 }
self._old_pos = pos
2019-03-06 03:38:57 +00:00
self.object : set_velocity ( vector.new ( ) )
self.object : set_acceleration ( vector.new ( ) )
2017-08-28 14:59:10 +00:00
return
end
self._old_vel = vector.new ( vel )
2017-08-28 12:19:46 +00:00
if self._old_pos then
local diff = vector.subtract ( self._old_pos , pos )
for _ , v in ipairs ( { " x " , " y " , " z " } ) do
if math.abs ( diff [ v ] ) > 1.1 then
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 )
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
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
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
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
local cart_dir = mcl_minecarts : velocity_to_dir ( vel )
local max_vel = mcl_minecarts.speed_max
if not dir then
dir , last_switch = mcl_minecarts : get_rail_direction ( pos , cart_dir , ctrl , self._old_switch , self._railtype )
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
local new_acc = { x = 0 , y = 0 , z = 0 }
2020-01-30 19:38:31 +00:00
if vector.equals ( dir , { x = 0 , y = 0 , z = 0 } ) and not has_fuel then
2017-08-28 12:19:46 +00:00
vel = { x = 0 , y = 0 , z = 0 }
update.vel = true
else
-- If the direction changed
if dir.x ~= 0 and self._old_dir . z ~= 0 then
vel.x = dir.x * math.abs ( vel.z )
vel.z = 0
pos.z = math.floor ( pos.z + 0.5 )
update.pos = true
end
if dir.z ~= 0 and self._old_dir . x ~= 0 then
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?
if dir.y ~= self._old_dir . y then
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
2020-03-17 20:00:12 +00:00
local friction = 0.4
2022-03-09 11:11:59 +00:00
local ndef = minetest.registered_nodes [ minetest.get_node ( pos ) . name ]
local speed_mod = ndef and ndef._rail_acceleration
2020-03-17 20:00:12 +00:00
acc = acc - friction
2020-01-30 19:38:31 +00:00
if has_fuel then
2020-03-17 20:00:12 +00:00
acc = acc + 0.6
end
if speed_mod and speed_mod ~= 0 then
acc = acc + speed_mod + friction
2017-01-05 14:23:14 +00:00
end
2017-08-28 12:19:46 +00:00
new_acc = vector.multiply ( dir , acc )
2017-01-05 14:23:14 +00:00
end
2019-03-06 03:38:57 +00:00
self.object : set_acceleration ( new_acc )
2017-08-28 12:19:46 +00:00
self._old_pos = vector.new ( pos )
self._old_dir = vector.new ( dir )
self._old_switch = last_switch
2017-08-15 12:02:21 +00:00
2017-08-28 12:19:46 +00: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
2017-08-28 14:59:10 +00:00
new_acc [ v ] = 0
2017-08-28 12:19:46 +00:00
update.vel = true
end
2017-08-15 12:02:21 +00:00
end
2017-01-05 14:23:14 +00:00
2017-08-28 12:19:46 +00: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 " )
2017-01-05 14:23:14 +00:00
end
2017-03-02 14:44:31 +00:00
2017-08-28 23:28:32 +00:00
2017-08-28 12:19:46 +00:00
if update.pos or self._punched then
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
2017-03-02 14:44:31 +00:00
end
2019-03-06 03:38:57 +00:00
self.object : set_yaw ( yaw * math.pi )
2017-03-02 14:44:31 +00:00
end
2017-08-28 12:19:46 +00:00
if self._punched then
self._punched = false
end
if not ( update.vel or update.pos ) then
2017-08-15 11:53:05 +00:00
return
end
2017-08-28 12:19:46 +00: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 }
2017-08-15 00:51:40 +00:00
end
2017-08-28 12:19:46 +00:00
self.object : set_animation ( anim , 1 , 0 )
2017-01-05 14:23:14 +00:00
2019-03-06 03:38:57 +00:00
self.object : set_velocity ( vel )
2017-08-28 12:19:46 +00:00
if update.pos then
2019-03-06 03:38:57 +00:00
self.object : set_pos ( pos )
2017-08-28 12:19:46 +00:00
end
end
2017-09-05 13:28:04 +00:00
function cart : get_staticdata ( )
2019-01-30 01:04:12 +00:00
return minetest.serialize ( { _railtype = self._railtype } )
2017-09-05 13:28:04 +00:00
end
2017-08-28 12:19:46 +00:00
minetest.register_entity ( entity_id , cart )
2017-03-21 02:58:53 +00:00
end
2017-08-29 00:36:50 +00:00
-- Place a minecart at pointed_thing
2021-05-25 10:52:25 +00:00
function mcl_minecarts . place_minecart ( itemstack , pointed_thing , placer )
2017-08-29 00:36:50 +00:00
if not pointed_thing.type == " node " then
return
end
local railpos , node
if mcl_minecarts : is_rail ( pointed_thing.under ) then
railpos = pointed_thing.under
node = minetest.get_node ( pointed_thing.under )
elseif mcl_minecarts : is_rail ( pointed_thing.above ) then
railpos = pointed_thing.above
node = minetest.get_node ( pointed_thing.above )
else
return
end
-- Activate detector rail
if node.name == " mcl_minecarts:detector_rail " then
local newnode = { name = " mcl_minecarts:detector_rail_on " , param2 = node.param2 }
minetest.swap_node ( railpos , newnode )
mesecon.receptor_on ( railpos )
end
local entity_id = entity_mapping [ itemstack : get_name ( ) ]
local cart = minetest.add_entity ( railpos , entity_id )
local railtype = minetest.get_item_group ( node.name , " connect_to_raillike " )
2019-01-30 00:58:26 +00:00
local le = cart : get_luaentity ( )
2021-05-29 14:12:33 +00:00
if le then
2019-01-30 00:58:26 +00:00
le._railtype = railtype
end
2017-08-29 00:36:50 +00:00
local cart_dir = mcl_minecarts : get_rail_direction ( railpos , { x = 1 , y = 0 , z = 0 } , nil , nil , railtype )
2019-03-06 03:38:57 +00:00
cart : set_yaw ( minetest.dir_to_yaw ( cart_dir ) )
2017-08-29 00:36:50 +00:00
2020-07-10 14:08:40 +00:00
local pname = " "
if placer then
pname = placer : get_player_name ( )
end
if not minetest.is_creative_enabled ( pname ) then
2017-08-29 00:36:50 +00:00
itemstack : take_item ( )
end
return itemstack
end
2018-02-01 21:45:19 +00:00
2021-05-25 10:52:25 +00:00
local function register_craftitem ( itemstring , entity_id , description , tt_help , longdesc , usagehelp , icon , creative )
2017-08-29 00:36:50 +00:00
entity_mapping [ itemstring ] = entity_id
2017-08-30 19:51:27 +00:00
local groups = { minecart = 1 , transport = 1 }
if creative == false then
groups.not_in_creative_inventory = 1
end
2017-08-28 12:19:46 +00:00
local def = {
stack_max = 1 ,
on_place = function ( itemstack , placer , pointed_thing )
if not pointed_thing.type == " node " then
return
end
-- 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
2020-07-10 14:08:40 +00:00
return mcl_minecarts.place_minecart ( itemstack , pointed_thing , placer )
2017-08-28 12:19:46 +00:00
end ,
2018-02-01 21:45:19 +00:00
_on_dispense = function ( stack , pos , droppos , dropnode , dropdir )
-- Place minecart as entity on rail. If there's no rail, just drop it.
local placed
if minetest.get_item_group ( dropnode.name , " rail " ) ~= 0 then
-- FIXME: This places minecarts even if the spot is already occupied
local pointed_thing = { under = droppos , above = { x = droppos.x , y = droppos.y + 1 , z = droppos.z } }
placed = mcl_minecarts.place_minecart ( stack , pointed_thing )
end
if placed == nil then
-- Drop item
minetest.add_item ( droppos , stack )
end
end ,
2017-08-30 19:51:27 +00:00
groups = groups ,
2017-08-28 12:19:46 +00:00
}
def.description = description
2020-02-19 03:54:17 +00:00
def._tt_help = tt_help
2017-08-28 13:04:50 +00:00
def._doc_items_longdesc = longdesc
2017-08-28 12:19:46 +00:00
def._doc_items_usagehelp = usagehelp
def.inventory_image = icon
def.wield_image = icon
minetest.register_craftitem ( itemstring , def )
end
2018-02-02 00:21:19 +00:00
--[[
Register a minecart
* itemstring : Itemstring of minecart item
* entity_id : ID of minecart entity
* description : Item name / description
* longdesc : Long help text
* usagehelp : Usage help text
* mesh : Minecart mesh
* textures : Minecart textures table
* icon : Item icon
* drop : Dropped items after destroying minecart
* on_rightclick : Called after rightclick
* on_activate_by_rail : Called when above activator rail
* creative : If false , don ' t show in Creative Inventory
] ]
2020-02-19 03:54:17 +00:00
local function register_minecart ( itemstring , entity_id , description , tt_help , longdesc , usagehelp , mesh , textures , icon , drop , on_rightclick , on_activate_by_rail , creative )
2020-01-30 21:05:18 +00:00
register_entity ( entity_id , mesh , textures , drop , on_rightclick , on_activate_by_rail )
2020-02-19 03:54:17 +00:00
register_craftitem ( itemstring , entity_id , description , tt_help , longdesc , usagehelp , icon , creative )
2021-05-29 14:12:33 +00:00
if minetest.get_modpath ( " doc_identifier " ) then
2017-08-28 13:04:50 +00:00
doc.sub . identifier.register_object ( entity_id , " craftitems " , itemstring )
end
end
-- Minecart
register_minecart (
2017-08-28 12:19:46 +00:00
" mcl_minecarts:minecart " ,
" mcl_minecarts:minecart " ,
2019-03-07 19:43:39 +00:00
S ( " Minecart " ) ,
2020-02-19 03:54:17 +00:00
S ( " Vehicle for fast travel on rails " ) ,
2019-03-07 19:43:39 +00:00
S ( " Minecarts can be used for a quick transportion on rails. " ) .. " \n " ..
S ( " 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. " ) ,
S ( " You can place the minecart on rails. Right-click it to enter it. Punch it to get it moving. " ) .. " \n " ..
2020-01-30 22:11:16 +00:00
S ( " To obtain the minecart, punch it while holding down the sneak key. " ) .. " \n " ..
2020-01-30 21:42:53 +00:00
S ( " If it moves over a powered activator rail, you'll get ejected. " ) ,
2017-08-28 13:04:50 +00:00
" mcl_minecarts_minecart.b3d " ,
{ " mcl_minecarts_minecart.png " } ,
" mcl_minecarts_minecart_normal.png " ,
{ " mcl_minecarts:minecart " } ,
function ( self , clicker )
2021-03-13 11:30:33 +00:00
local name = clicker : get_player_name ( )
if not clicker or not clicker : is_player ( ) then
return
end
local player_name = clicker : get_player_name ( )
if self._driver and player_name == self._driver then
2020-01-30 21:42:53 +00:00
detach_driver ( self )
2021-03-13 11:30:33 +00:00
elseif not self._driver then
self._driver = player_name
2019-02-01 05:33:07 +00:00
self._start_pos = self.object : get_pos ( )
2021-03-13 11:30:33 +00:00
mcl_player.player_attached [ player_name ] = true
2019-03-06 04:11:49 +00:00
clicker : set_attach ( self.object , " " , { x = 0 , y =- 1.75 , z =- 2 } , { x = 0 , y = 0 , z = 0 } )
2018-07-09 21:45:54 +00:00
mcl_player.player_attached [ name ] = true
minetest.after ( 0.2 , function ( name )
local player = minetest.get_player_by_name ( name )
if player then
mcl_player.player_set_animation ( player , " sit " , 30 )
player : set_eye_offset ( { x = 0 , y =- 5.5 , z = 0 } , { x = 0 , y =- 4 , z = 0 } )
2021-07-20 14:14:34 +00:00
mcl_title.set ( clicker , " actionbar " , { text = S ( " Sneak to dismount " ) , color = " white " , stay = 60 } )
2018-07-09 21:45:54 +00:00
end
end , name )
2017-08-28 13:04:50 +00:00
end
2020-01-30 21:42:53 +00:00
end , activate_normal_minecart
2017-08-28 12:19:46 +00:00
)
2017-08-28 13:04:50 +00:00
-- Minecart with Chest
register_minecart (
2017-08-28 12:19:46 +00:00
" mcl_minecarts:chest_minecart " ,
" mcl_minecarts:chest_minecart " ,
2019-03-07 19:43:39 +00:00
S ( " Minecart with Chest " ) ,
2020-02-19 03:54:17 +00:00
nil , nil , nil ,
2017-08-28 13:04:50 +00:00
" mcl_minecarts_minecart_chest.b3d " ,
{ " mcl_chests_normal.png " , " mcl_minecarts_minecart.png " } ,
" mcl_minecarts_minecart_chest.png " ,
2018-05-07 23:40:31 +00:00
{ " mcl_minecarts:minecart " , " mcl_chests:chest " } ,
2022-09-23 03:43:31 +00:00
nil , nil , true )
2022-10-14 22:37:29 +00:00
mcl_entity_invs.register_inv ( " mcl_minecarts:chest_minecart " , " Minecart " , 27 , false , true )
2017-08-28 12:19:46 +00:00
2017-08-28 13:04:50 +00:00
-- Minecart with Furnace
register_minecart (
2017-08-28 12:19:46 +00:00
" mcl_minecarts:furnace_minecart " ,
" mcl_minecarts:furnace_minecart " ,
2019-03-07 19:43:39 +00:00
S ( " Minecart with Furnace " ) ,
2020-02-19 03:54:17 +00:00
nil ,
2020-01-30 21:31:44 +00:00
S ( " A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel. " ) ,
S ( " Place it on rails. If you give it some coal, the furnace will start burning for a long time and the minecart will be able to move itself. Punch it to get it moving. " ) .. " \n " ..
S ( " To obtain the minecart and furnace, punch them while holding down the sneak key. " ) ,
2017-08-28 13:04:50 +00:00
" mcl_minecarts_minecart_block.b3d " ,
{
" default_furnace_top.png " ,
" default_furnace_top.png " ,
" default_furnace_front.png " ,
" default_furnace_side.png " ,
" default_furnace_side.png " ,
" default_furnace_side.png " ,
" mcl_minecarts_minecart.png " ,
} ,
" mcl_minecarts_minecart_furnace.png " ,
2017-08-29 00:36:50 +00:00
{ " mcl_minecarts:minecart " , " mcl_furnaces:furnace " } ,
-- Feed furnace with coal
function ( self , clicker )
if not clicker or not clicker : is_player ( ) then
return
end
if not self._fueltime then
self._fueltime = 0
end
local held = clicker : get_wielded_item ( )
if minetest.get_item_group ( held : get_name ( ) , " coal " ) == 1 then
self._fueltime = self._fueltime + 180
2020-07-10 14:08:40 +00:00
if not minetest.is_creative_enabled ( clicker : get_player_name ( ) ) then
2017-08-29 00:36:50 +00:00
held : take_item ( )
2020-04-30 16:13:05 +00:00
local index = clicker : get_wield_index ( )
2017-08-29 00:36:50 +00:00
local inv = clicker : get_inventory ( )
inv : set_stack ( " main " , index , held )
end
2020-01-30 19:38:31 +00:00
self.object : set_properties ( { textures =
{
" default_furnace_top.png " ,
" default_furnace_top.png " ,
" default_furnace_front_active.png " ,
" default_furnace_side.png " ,
" default_furnace_side.png " ,
" default_furnace_side.png " ,
" mcl_minecarts_minecart.png " ,
} } )
2017-08-29 00:36:50 +00:00
end
2022-09-18 19:32:45 +00:00
end , nil , true
2017-08-28 12:19:46 +00:00
)
2017-08-28 13:04:50 +00:00
-- Minecart with Command Block
register_minecart (
2017-08-28 12:19:46 +00:00
" mcl_minecarts:command_block_minecart " ,
" mcl_minecarts:command_block_minecart " ,
2019-03-07 19:43:39 +00:00
S ( " Minecart with Command Block " ) ,
2020-02-19 03:54:17 +00:00
nil , nil , nil ,
2017-08-28 13:04:50 +00:00
" mcl_minecarts_minecart_block.b3d " ,
{
" jeija_commandblock_off.png^[verticalframe:2:0 " ,
" jeija_commandblock_off.png^[verticalframe:2:0 " ,
" jeija_commandblock_off.png^[verticalframe:2:0 " ,
" jeija_commandblock_off.png^[verticalframe:2:0 " ,
" jeija_commandblock_off.png^[verticalframe:2:0 " ,
" jeija_commandblock_off.png^[verticalframe:2:0 " ,
" mcl_minecarts_minecart.png " ,
} ,
" mcl_minecarts_minecart_command_block.png " ,
2017-08-30 19:51:27 +00:00
{ " mcl_minecarts:minecart " } ,
2018-05-07 23:40:31 +00:00
nil , nil , false
2017-08-28 13:04:50 +00:00
)
-- Minecart with Hopper
register_minecart (
" mcl_minecarts:hopper_minecart " ,
" mcl_minecarts:hopper_minecart " ,
2019-03-07 19:43:39 +00:00
S ( " Minecart with Hopper " ) ,
2020-02-19 03:54:17 +00:00
nil , nil , nil ,
2017-08-28 13:04:50 +00:00
" mcl_minecarts_minecart_hopper.b3d " ,
{
" mcl_hoppers_hopper_inside.png " ,
" mcl_minecarts_minecart.png " ,
" mcl_hoppers_hopper_outside.png " ,
" mcl_hoppers_hopper_top.png " ,
} ,
" mcl_minecarts_minecart_hopper.png " ,
2018-05-07 23:40:31 +00:00
{ " mcl_minecarts:minecart " , " mcl_hoppers:hopper " } ,
2022-11-12 17:47:20 +00:00
nil , nil , true
2017-08-28 12:19:46 +00:00
)
2022-11-12 17:47:20 +00:00
mcl_entity_invs.register_inv ( " mcl_minecarts:hopper_minecart " , " Hopper Minecart " , 5 , false , true )
2017-08-28 12:19:46 +00:00
2017-08-28 13:04:50 +00:00
-- Minecart with TNT
register_minecart (
" mcl_minecarts:tnt_minecart " ,
" mcl_minecarts:tnt_minecart " ,
2019-03-07 19:43:39 +00:00
S ( " Minecart with TNT " ) ,
2020-02-19 03:54:17 +00:00
S ( " Vehicle for fast travel on rails " ) .. " \n " .. S ( " Can be ignited by tools or powered activator rail " ) ,
2020-01-30 21:31:44 +00:00
S ( " A minecart with TNT is an explosive vehicle that travels on rail. " ) ,
2020-02-05 02:11:07 +00:00
S ( " Place it on rails. Punch it to move it. The TNT is ignited with a flint and steel or when the minecart is on an powered activator rail. " ) .. " \n " ..
2020-01-30 21:31:44 +00:00
S ( " To obtain the minecart and TNT, punch them while holding down the sneak key. You can't do this if the TNT was ignited. " ) ,
2017-08-28 13:04:50 +00:00
" mcl_minecarts_minecart_block.b3d " ,
{
" default_tnt_top.png " ,
" default_tnt_bottom.png " ,
" default_tnt_side.png " ,
" default_tnt_side.png " ,
" default_tnt_side.png " ,
" default_tnt_side.png " ,
" mcl_minecarts_minecart.png " ,
} ,
" mcl_minecarts_minecart_tnt.png " ,
2018-05-07 23:40:31 +00:00
{ " mcl_minecarts:minecart " , " mcl_tnt:tnt " } ,
2020-01-30 21:05:18 +00:00
-- Ingite
function ( self , clicker )
if not clicker or not clicker : is_player ( ) then
return
end
if self._boomtimer then
return
end
local held = clicker : get_wielded_item ( )
if held : get_name ( ) == " mcl_fire:flint_and_steel " then
2020-07-10 14:08:40 +00:00
if not minetest.is_creative_enabled ( clicker : get_player_name ( ) ) then
2020-01-30 21:05:18 +00:00
held : add_wear ( 65535 / 65 ) -- 65 uses
2020-02-14 20:09:36 +00:00
local index = clicker : get_wield_index ( )
2020-01-30 21:05:18 +00:00
local inv = clicker : get_inventory ( )
inv : set_stack ( " main " , index , held )
end
activate_tnt_minecart ( self )
end
2020-01-30 21:31:44 +00:00
end , activate_tnt_minecart )
2017-08-28 12:19:46 +00:00
2017-01-05 14:23:14 +00:00
minetest.register_craft ( {
output = " mcl_minecarts:minecart " ,
recipe = {
2017-02-11 20:14:40 +00:00
{ " mcl_core:iron_ingot " , " " , " mcl_core:iron_ingot " } ,
{ " mcl_core:iron_ingot " , " mcl_core:iron_ingot " , " mcl_core:iron_ingot " } ,
2017-01-05 14:23:14 +00:00
} ,
} )
2017-08-28 12:19:46 +00:00
minetest.register_craft ( {
2020-01-30 21:31:44 +00:00
output = " mcl_minecarts:tnt_minecart " ,
2017-08-28 12:19:46 +00:00
recipe = {
2020-01-30 21:31:44 +00:00
{ " mcl_tnt:tnt " } ,
2017-08-28 12:19:46 +00:00
{ " mcl_minecarts:minecart " } ,
} ,
} )
2022-09-18 19:32:45 +00:00
minetest.register_craft ( {
2021-05-24 22:43:08 +00:00
output = " mcl_minecarts:furnace_minecart " ,
recipe = {
{ " mcl_furnaces:furnace " } ,
{ " mcl_minecarts:minecart " } ,
} ,
} )
2022-11-12 17:47:20 +00:00
minetest.register_craft ( {
2021-05-24 22:43:08 +00:00
output = " mcl_minecarts:hopper_minecart " ,
recipe = {
{ " mcl_hoppers:hopper " } ,
{ " mcl_minecarts:minecart " } ,
} ,
} )
2022-11-12 17:47:20 +00:00
2021-05-24 22:43:08 +00:00
minetest.register_craft ( {
output = " mcl_minecarts:chest_minecart " ,
recipe = {
{ " mcl_chests:chest " } ,
{ " mcl_minecarts:minecart " } ,
} ,
2022-09-23 03:43:31 +00:00
} )
2021-05-24 22:43:08 +00:00
2021-03-29 22:58:45 +00:00
if has_mcl_wip then
mcl_wip.register_wip_item ( " mcl_minecarts:chest_minecart " )
mcl_wip.register_wip_item ( " mcl_minecarts:furnace_minecart " )
mcl_wip.register_wip_item ( " mcl_minecarts:command_block_minecart " )
2022-03-09 11:11:59 +00:00
end