2021-05-29 16:12:33 +02:00
local modname = minetest.get_current_modname ( )
local S = minetest.get_translator ( modname )
2019-03-07 20:43:39 +01:00
2021-03-30 00:58:45 +02:00
local has_mcl_wip = minetest.get_modpath ( " mcl_wip " )
2017-01-05 15:23:14 +01:00
mcl_minecarts = { }
2024-03-28 08:11:59 +01:00
local mod = mcl_minecarts
2021-05-29 16:12:33 +02:00
mcl_minecarts.modpath = minetest.get_modpath ( modname )
2017-01-05 15:23:14 +01:00
mcl_minecarts.speed_max = 10
2019-01-30 03:36:36 +01:00
mcl_minecarts.check_float_time = 15
2024-02-14 11:48:55 +01:00
local max_step_distance = 0.5
2024-03-13 13:08:12 +01:00
local friction = 0.4
2024-03-23 10:59:08 +01:00
local MINECART_MAX_HP = 4
2017-01-05 15:23:14 +01:00
dofile ( mcl_minecarts.modpath .. " /functions.lua " )
dofile ( mcl_minecarts.modpath .. " /rails.lua " )
2023-03-28 02:51:51 +02:00
local LOGGING_ON = minetest.settings : get_bool ( " mcl_logging_minecarts " , false )
2024-02-14 00:31:48 +01:00
local DEBUG = false
2023-03-28 02:51:51 +02:00
local function mcl_log ( message )
if LOGGING_ON then
mcl_util.mcl_log ( message , " [Minecarts] " , true )
end
end
2024-03-16 10:17:09 +01:00
mcl_minecarts.on_enter_below = function ( pos , cart , next_dir , node_def )
local staticdata = cart._staticdata
if ( node_def.groups . hopper or 0 ) == 0 then return end
local hopper_pulled = mcl_hoppers.pull_from_minecart ( cart , pos , cart._inv_size or 0 )
if DEBUG then print ( " Attempt pull_from_minecart, hopper_pulled= " .. tostring ( hopper_pulled ) ) end
if hopper_pulled and next_dir ~= staticdata.dir then
-- If there was an item pulled by a hopper under the rails force the cart to stay put for 1.5 seconds
-- to allow redstone time to process
if hopper_pulled then
staticdata.delay = 1.5
end
end
end
2024-04-04 09:33:16 +02:00
local function detach_minecart ( self )
local staticdata = self._staticdata
staticdata.connected_at = nil
self.object : set_velocity ( staticdata.dir * staticdata.velocity )
end
local function try_detach_minecart ( self )
local staticdata = self._staticdata
local node = minetest.get_node ( staticdata.connected_at )
if minetest.get_item_group ( node.name , " rail " ) == 0 then
detach_minecart ( self )
end
end
2024-03-16 10:17:09 +01:00
--[[
Array of hooks { { u , v , w } , name }
Actual position is pos + u * dir + v * right + w * up
] ]
local enter_exit_checks = {
{ 0 , 0 , 0 , " " } ,
{ 0 , 0 , 1 , " _above " } ,
{ 0 , 0 , - 1 , " _below " } ,
{ 0 , 1 , 0 , " _side " } ,
{ 0 , - 1 , 0 , " _side " } ,
}
local function handle_cart_enter_exit ( self , pos , next_dir , event )
2024-03-13 11:28:40 +01:00
local staticdata = self._staticdata
2024-03-10 12:20:27 +01:00
2024-03-16 10:17:09 +01:00
local dir = staticdata.dir
local right = vector.new ( dir.z , dir.y , - dir.x )
local up = vector.new ( 0 , 1 , 0 )
for _ , check in ipairs ( enter_exit_checks ) do
local check_pos = pos + dir * check [ 1 ] + right * check [ 2 ] + up * check [ 3 ]
local node = minetest.get_node ( check_pos )
local node_def = minetest.registered_nodes [ node.name ]
2024-03-10 12:20:27 +01:00
2024-03-16 10:17:09 +01:00
-- node-specific hook
2024-03-23 07:13:53 +01:00
local hook_name = " _mcl_minecarts_ " .. event .. check [ 4 ]
local hook = node_def [ hook_name ]
if hook then hook ( check_pos , self , next_dir , pos ) end
2024-03-13 11:28:40 +01:00
2024-03-16 10:17:09 +01:00
-- global minecart hook
hook = mcl_minecarts [ event .. check [ 4 ] ]
if hook then hook ( check_pos , self , next_dir , node_def ) end
2024-03-10 12:20:27 +01:00
end
-- Handle cart-specific behaviors
2024-03-16 10:17:09 +01:00
local hook = self [ " _mcl_minecarts_ " .. event ]
if hook then hook ( self , pos ) end
end
local function handle_cart_enter ( self , pos , next_dir )
handle_cart_enter_exit ( self , pos , next_dir , " on_enter " )
end
local function handle_cart_leave ( self , pos , next_dir )
handle_cart_enter_exit ( self , pos , next_dir , " on_leave " )
2024-03-10 12:20:27 +01:00
end
2024-03-16 10:17:09 +01:00
local function handle_cart_node_watches ( self , dtime )
local staticdata = self._staticdata
local watches = staticdata.node_watches or { }
local new_watches = { }
for _ , node_pos in ipairs ( watches ) do
local node = minetest.get_node ( node_pos )
local node_def = minetest.registered_nodes [ node.name ]
local hook = node_def._mcl_minecarts_node_on_step
if hook and hook ( node_pos , self , dtime ) then
2024-03-23 07:13:53 +01:00
new_watches [ # new_watches + 1 ] = node_pos
2024-03-16 10:17:09 +01:00
end
2024-03-10 12:20:27 +01:00
end
2024-03-16 10:17:09 +01:00
staticdata.node_watches = new_watches
2024-03-10 12:20:27 +01:00
end
local function update_cart_orientation ( self , staticdata )
-- constants
local _2_pi = math.pi * 2
local pi = math.pi
local dir = staticdata.dir
-- Calculate an angle from the x,z direction components
local rot_y = math.atan2 ( dir.x , dir.z ) + ( staticdata.rot_adjust or 0 )
if rot_y < 0 then
rot_y = rot_y + _2_pi
end
-- Check if the rotation is a 180 flip and don't change if so
local rot = self.object : get_rotation ( )
local diff = math.abs ( ( rot_y - ( rot.y + pi ) % _2_pi ) )
if diff < 0.001 or diff > _2_pi - 0.001 then
-- Update rotation adjust and recalculate the rotation
staticdata.rot_adjust = ( ( staticdata.rot_adjust or 0 ) + pi ) % _2_pi
rot.y = math.atan2 ( dir.x , dir.z ) + ( staticdata.rot_adjust or 0 )
else
rot.y = rot_y
end
-- Forward/backwards tilt (pitch)
if dir.y < 0 then
rot.x = - 0.25 * pi
elseif dir.y > 0 then
rot.x = 0.25 * pi
else
rot.x = 0
end
if ( staticdata.rot_adjust or 0 ) < 0.01 then
rot.x = - rot.x
end
2024-03-13 14:07:58 +01:00
if dir.z ~= 0 then
rot.x = - rot.x
end
2024-03-10 12:20:27 +01:00
self.object : set_rotation ( rot )
end
2024-03-13 15:22:23 +01:00
local function direction_away_from_players ( self , staticdata )
local objs = minetest.get_objects_inside_radius ( self.object : get_pos ( ) , 1.1 )
for n = 1 , # objs do
2024-03-23 07:13:53 +01:00
local obj = objs [ n ]
2024-03-13 15:22:23 +01:00
local player_name = obj : get_player_name ( )
if player_name and player_name ~= " " and not ( self._driver and self._driver == player_name ) then
local diff = obj : get_pos ( ) - self.object : get_pos ( )
2024-03-23 10:08:17 +01:00
2024-03-13 15:22:23 +01:00
local length = vector.distance ( vector.new ( 0 , 0 , 0 ) , diff )
local vec = diff / length
local force = vector.dot ( vec , vector.normalize ( staticdata.dir ) )
2024-03-23 10:08:17 +01:00
-- Check if this would push past the end of the track and don't move it it would
-- This prevents an oscillation that would otherwise occur
local dir = staticdata.dir
if force > 0 then
dir = - dir
end
if mcl_minecarts : is_rail ( staticdata.connected_at + dir ) then
if force > 0.5 then
return - length * 4
elseif force < - 0.5 then
return length * 4
end
2024-03-13 15:22:23 +01:00
end
end
end
return 0
end
2024-03-13 11:28:40 +01:00
local function calculate_acceleration ( self , staticdata )
local acceleration = 0
2024-04-04 09:33:16 +02:00
-- Fix up movement data
staticdata.velocity = staticdata.velocity or 0
2024-03-13 11:28:40 +01:00
-- Apply friction if moving
if staticdata.velocity > 0 then
acceleration = - friction
end
2024-03-10 12:20:27 +01:00
local pos = staticdata.connected_at
2024-03-13 11:28:40 +01:00
local node_name = minetest.get_node ( pos ) . name
local node_def = minetest.registered_nodes [ node_name ]
local max_vel = mcl_minecarts.speed_max
2024-03-10 12:20:27 +01:00
2024-03-13 11:28:40 +01:00
if self._go_forward then
2024-03-23 10:59:08 +01:00
acceleration = 4
2024-03-13 11:28:40 +01:00
elseif self._brake then
acceleration = - 1.5
2024-03-23 09:09:37 +01:00
elseif ( staticdata.fueltime or 0 ) > 0 and staticdata.velocity <= 4 then
2024-03-13 11:28:40 +01:00
acceleration = 0.6
elseif staticdata.velocity >= ( node_def._max_acceleration_velocity or max_vel ) then
-- Standard friction
else
2024-03-13 12:51:39 +01:00
if node_def._rail_acceleration then
acceleration = node_def._rail_acceleration * 4
end
2024-03-13 11:28:40 +01:00
end
2024-03-10 12:20:27 +01:00
2024-03-13 11:28:40 +01:00
-- Factor in gravity after everything else
2024-03-13 12:51:39 +01:00
local gravity_strength = 2.45 --friction * 5
2024-03-13 11:28:40 +01:00
if staticdata.dir . y < 0 then
acceleration = gravity_strength - friction
elseif staticdata.dir . y > 0 then
acceleration = - gravity_strength + friction
end
return acceleration
end
2024-03-13 15:22:23 +01:00
local function reverse_direction ( self , staticdata )
-- Complete moving thru this block into the next, reverse direction, and put us back at the same position we were at
local next_dir = - staticdata.dir
staticdata.connected_at = staticdata.connected_at + staticdata.dir
staticdata.distance = 1 - staticdata.distance
-- recalculate direction
local next_dir , _ = mcl_minecarts : get_rail_direction ( staticdata.connected_at , next_dir , nil , nil , staticdata.railtype )
staticdata.dir = next_dir
end
2024-03-13 11:28:40 +01:00
local function do_movement_step ( self , dtime )
local staticdata = self._staticdata
if not staticdata.connected_at then return 0 end
-- Calculate timestep remaiing in this block
local x_0 = staticdata.distance or 0
local remaining_in_block = 1 - x_0
local a = calculate_acceleration ( self , staticdata )
local v_0 = staticdata.velocity
2024-03-13 15:22:23 +01:00
-- Repel minecarts
local away = direction_away_from_players ( self , staticdata )
if away > 0 then
v_0 = away
elseif away < 0 then
reverse_direction ( self , staticdata )
v_0 = - away
end
2024-03-13 11:28:40 +01:00
if DEBUG and ( v_0 > 0 or a ~= 0 ) then
print ( " cart # " .. tostring ( staticdata.cart_id ) ..
" : a= " .. tostring ( a ) ..
" ,v_0= " .. tostring ( v_0 ) ..
" ,x_0= " .. tostring ( x_0 ) ..
" ,timestep= " .. tostring ( timestep ) ..
" ,dir= " .. tostring ( staticdata.dir ) ..
" ,connected_at= " .. tostring ( staticdata.connected_at ) ..
" ,distance= " .. tostring ( staticdata.distance )
)
end
-- Not moving
if a == 0 and v_0 == 0 then return 0 end
-- Movement equation with acceleration: x_1 = x_0 + v_0 * t + 0.5 * a * t*t
local timestep
local stops_in_block = false
local inside = v_0 * v_0 + 2 * a * remaining_in_block
if inside < 0 then
-- Would stop or reverse direction inside this block, calculate time to v_1 = 0
timestep = - v_0 / a
stops_in_block = true
elseif a ~= 0 then
-- Setting x_1 = x_0 + remaining_in_block, and solving for t gives:
timestep = ( math.sqrt ( v_0 * v_0 + 2 * a * remaining_in_block ) - v_0 ) / a
2024-03-10 12:20:27 +01:00
else
2024-03-13 11:28:40 +01:00
timestep = remaining_in_block / v_0
end
-- Truncate timestep to remaining time delta
if timestep > dtime then
timestep = dtime
end
-- Truncate timestep to prevent v_1 from being larger that speed_max
local v_max = mcl_minecarts.speed_max
2024-03-23 08:32:32 +01:00
if ( v_0 ~= v_max ) and ( v_0 + a * timestep > v_max ) then
2024-03-13 11:28:40 +01:00
timestep = ( v_max - v_0 ) / a
end
-- Prevent infinite loops
if timestep <= 0 then return 0 end
-- Calculate v_1 taking v_max into account
local v_1 = v_0 + a * timestep
if v_1 > v_max then
v_1 = v_max
elseif v_1 < friction / 5 then
v_1 = 0
end
-- Calculate x_1
local x_1 = x_0 + timestep * v_0 + 0.5 * a * timestep * timestep
-- Update position and velocity of the minecart
staticdata.velocity = v_1
staticdata.distance = x_1
2024-03-23 10:08:17 +01:00
if DEBUG and ( 1 == 0 ) and ( v_0 > 0 or a ~= 0 ) then
2024-03-23 08:32:32 +01:00
print ( " - cart # " .. tostring ( staticdata.cart_id ) ..
" : a= " .. tostring ( a ) ..
" ,v_0= " .. tostring ( v_0 ) ..
" ,v_1= " .. tostring ( v_1 ) ..
" ,x_0= " .. tostring ( x_0 ) ..
" ,x_1= " .. tostring ( x_1 ) ..
" ,timestep= " .. tostring ( timestep ) ..
" ,dir= " .. tostring ( staticdata.dir ) ..
" ,connected_at= " .. tostring ( staticdata.connected_at ) ..
" ,distance= " .. tostring ( staticdata.distance )
)
end
2024-03-13 11:28:40 +01:00
-- Entity movement
local pos = staticdata.connected_at
-- Handle movement to next block, account for loss of precision in calculations
if x_1 >= 0.99 then
2024-03-10 12:20:27 +01:00
staticdata.distance = 0
-- Anchor at the next node
2024-03-16 10:17:09 +01:00
local old_pos = pos
2024-03-10 12:20:27 +01:00
pos = pos + staticdata.dir
staticdata.connected_at = pos
-- Get the next direction
local next_dir , _ = mcl_minecarts : get_rail_direction ( pos , staticdata.dir , nil , nil , staticdata.railtype )
if DEBUG and next_dir ~= staticdata.dir then
print ( " Changing direction from " .. tostring ( staticdata.dir ) .. " to " .. tostring ( next_dir ) )
end
2024-03-16 10:17:09 +01:00
-- Leave the old node
handle_cart_leave ( self , old_pos , next_dir )
2024-03-13 11:28:40 +01:00
-- Enter the new node
handle_cart_enter ( self , pos , next_dir )
2024-04-04 09:33:16 +02:00
try_detach_minecart ( self )
2024-03-10 12:20:27 +01:00
-- Handle end of track
2024-03-23 07:13:53 +01:00
if next_dir == staticdata.dir * - 1 and next_dir.y == 0 then
2024-03-13 11:28:40 +01:00
if DEBUG then print ( " Stopping cart at end of track at " .. tostring ( pos ) ) end
2024-03-10 12:20:27 +01:00
staticdata.velocity = 0
end
-- Update cart direction
staticdata.dir = next_dir
2024-03-13 11:28:40 +01:00
elseif stops_in_block and v_1 < ( friction / 5 ) and a <= 0 then
-- Handle direction flip due to gravity
if DEBUG then print ( " Gravity flipped direction " ) end
-- Velocity should be zero at this point
staticdata.velocity = 0
2024-03-13 15:22:23 +01:00
reverse_direction ( self , staticdata )
2024-03-13 11:28:40 +01:00
-- Intermediate movement
2024-03-13 15:22:23 +01:00
pos = staticdata.connected_at + staticdata.dir * staticdata.distance
2024-03-13 11:28:40 +01:00
else
-- Intermediate movement
pos = pos + staticdata.dir * staticdata.distance
2024-03-10 12:20:27 +01:00
end
self.object : move_to ( pos )
-- Update cart orientation
update_cart_orientation ( self , staticdata )
2024-03-13 11:28:40 +01:00
-- Debug reporting
if DEBUG and ( v_0 > 0 or v_1 > 0 ) then
print ( " cart # " .. tostring ( staticdata.cart_id ) ..
" : a= " .. tostring ( a ) ..
" ,v_0= " .. tostring ( v_0 ) ..
" ,v_1= " .. tostring ( v_1 ) ..
" ,x_0= " .. tostring ( x_0 ) ..
" ,x_1= " .. tostring ( x_1 ) ..
" ,timestep= " .. tostring ( timestep ) ..
" ,dir= " .. tostring ( staticdata.dir ) ..
" ,pos= " .. tostring ( pos ) ..
" ,connected_at= " .. tostring ( staticdata.connected_at ) ..
" ,distance= " .. tostring ( staticdata.distance )
)
end
2024-03-10 12:20:27 +01:00
2024-03-13 11:28:40 +01:00
-- Report the amount of time processed
return dtime - timestep
2024-03-10 12:20:27 +01:00
end
2024-03-13 11:28:40 +01:00
2024-03-10 12:20:27 +01:00
local function do_movement ( self , dtime )
local staticdata = self._staticdata
-- Allow the carts to be delay for the rest of the world to react before moving again
if ( staticdata.delay or 0 ) > dtime then
staticdata.delay = staticdata.delay - dtime
return
else
staticdata.delay = 0
end
2024-03-13 11:28:40 +01:00
-- Break long movements at block boundaries to make it
-- it impossible to jump across gaps due to server lag
2024-03-10 12:20:27 +01:00
-- causing large timesteps
2024-03-13 11:28:40 +01:00
while dtime > 0 do
2024-03-16 10:17:09 +01:00
local new_dtime = do_movement_step ( self , dtime )
-- Handle node watches here in steps to prevent server lag from changing behavior
handle_cart_node_watches ( self , dtime - new_dtime )
dtime = new_dtime
2024-03-10 12:20:27 +01:00
end
end
2023-03-28 02:51:51 +02:00
2024-04-04 09:33:16 +02:00
local function do_detached_movement ( self , dtime )
local staticdata = self._staticdata
-- Apply physics
if mcl_physics then
mcl_physics.apply_entity_environmental_physics ( self )
else
-- Simple physics
local friction = self.object : get_velocity ( )
friction.y = 0
local accel = vector.new ( 0 , - 9.81 , 0 ) -- gravity
accel = vector.add ( accel , vector.multiply ( friction , - 0.9 ) )
self.object : set_acceleration ( accel )
end
-- Try to reconnect to rail
local pos_r = vector.round ( self.object : get_pos ( ) )
local node = minetest.get_node ( pos_r )
if minetest.get_item_group ( node.name , " rail " ) ~= 0 then
staticdata.connected_at = pos_r
staticdata.railtype = node.name
local freebody_velocity = self.object : get_velocity ( )
2024-04-04 20:20:14 +02:00
staticdata.dir = mod : get_rail_direction ( pos_r , mod.snap_direction ( freebody_velocity ) )
-- Use vector projection to only keep the velocity in the new direction of movement on the rail
-- https://en.wikipedia.org/wiki/Vector_projection
staticdata.velocity = vector.dot ( staticdata.dir , freebody_velocity )
2024-04-04 09:33:16 +02:00
-- Clear freebody movement
self.object : set_velocity ( vector.new ( 0 , 0 , 0 ) )
self.object : set_acceleration ( vector.new ( 0 , 0 , 0 ) )
end
end
2020-01-30 22:42:53 +01:00
local function detach_driver ( self )
if not self._driver then
return
end
2021-03-13 12:30:33 +01:00
mcl_player.player_attached [ self._driver ] = nil
local player = minetest.get_player_by_name ( self._driver )
2020-01-30 22:42:53 +01:00
self._driver = nil
self._start_pos = nil
2021-03-13 12:30:33 +01:00
if player then
player : set_detach ( )
2024-02-13 15:57:45 +01:00
player : set_eye_offset ( vector.new ( 0 , 0 , 0 ) , vector.new ( 0 , 0 , 0 ) )
2021-03-13 12:30:33 +01:00
mcl_player.player_set_animation ( player , " stand " , 30 )
end
2020-01-30 22:42:53 +01:00
end
2020-01-30 23:11:16 +01:00
local function activate_tnt_minecart ( self , timer )
2020-01-30 22:05:18 +01:00
if self._boomtimer then
return
end
2020-01-30 23:11:16 +01:00
if timer then
self._boomtimer = timer
else
self._boomtimer = tnt.BOOMTIMER
end
2020-01-30 22:05:18 +01: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-07 00:55:45 +02:00
minetest.sound_play ( " tnt_ignite " , { pos = self.object : get_pos ( ) , gain = 1.0 , max_hear_distance = 15 } , true )
2020-01-30 22:05:18 +01:00
end
2024-03-13 12:51:39 +01:00
local function activate_normal_minecart ( self )
detach_driver ( self )
2024-03-23 10:59:08 +01:00
-- Detach passenger
2024-03-13 12:51:39 +01:00
if self._passenger then
local mob = self._passenger . object
mob : set_detach ( )
end
end
2020-01-30 22:42:53 +01:00
2023-03-28 02:51:51 +02: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 ( )
2023-06-27 19:06:09 +02:00
if ent and not ent._removed and ent.itemstring and ent.itemstring ~= " " then
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 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
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
if not taken_items then
local items_remaining = current_itemstack : get_count ( )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
-- 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 )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
mcl_log ( " i: " .. tostring ( i ) )
mcl_log ( " Items remaining: " .. items_remaining )
mcl_log ( " Name: " .. tostring ( stack : get_name ( ) ) )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
if current_itemstack : get_name ( ) == stack : get_name ( ) then
mcl_log ( " We have a match. Name: " .. tostring ( stack : get_name ( ) ) )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
local room_for = stack : get_stack_max ( ) - stack : get_count ( )
mcl_log ( " Room for: " .. tostring ( room_for ) )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
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 " )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
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 )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
inv : set_stack ( " main " , i , stack )
items_remaining = 0
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
ent.object : get_luaentity ( ) . itemstring = " "
ent.object : remove ( )
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
taken_items = true
break
end
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
mcl_log ( " Count: " .. tostring ( stack : get_count ( ) ) )
mcl_log ( " stack max: " .. tostring ( stack : get_stack_max ( ) ) )
--mcl_log("Is it empty: " .. stack:to_string())
2023-03-28 02:51:51 +02:00
end
2023-06-27 19:06:09 +02:00
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
2023-03-28 02:51:51 +02:00
end
2023-06-27 19:06:09 +02:00
end
2023-03-28 02:51:51 +02:00
2023-06-27 19:06:09 +02:00
--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 " )
2023-03-28 02:51:51 +02:00
end
end
end
end
return false
end
2017-08-29 02:36:50 +02:00
-- Table for item-to-entity mapping. Keys: itemstring, Values: Corresponding entity ID
local entity_mapping = { }
2017-08-28 14:19:46 +02:00
2024-02-14 00:31:48 +01:00
local function make_staticdata ( railtype , connected_at , dir )
return {
railtype = railtype ,
connected_at = connected_at ,
2024-02-17 00:09:29 +01:00
distance = 0 ,
velocity = 0 ,
2024-02-14 00:31:48 +01:00
dir = vector.new ( dir ) ,
2024-03-13 11:28:40 +01:00
cart_id = math.random ( 1 , 1000000000 ) ,
2024-02-14 00:31:48 +01:00
}
end
2024-02-14 11:48:55 +01:00
local function to_dirstring ( dir )
if dir.x == 0 then
if dir.z == 1 then
return " north "
else
return " south "
end
elseif dir.z == 0 then
if dir.x == 1 then
return " east "
else
return " west "
end
end
end
2024-03-10 12:20:27 +01:00
local function register_entity ( entity_id , def )
assert ( def.drop , " drop is required parameter " )
2024-03-23 07:13:53 +01:00
-- Entity groups
local groups = { minecart = 1 }
for k , v in pairs ( def.groups or { } ) do
groups [ k ] = v
end
2017-08-28 14:19:46 +02:00
local cart = {
2024-02-13 16:33:37 +01:00
initial_properties = {
2024-03-13 15:22:23 +01:00
physical = true ,
2024-02-13 16:33:37 +01:00
collisionbox = { - 10 / 16. , - 0.5 , - 10 / 16 , 10 / 16 , 0.25 , 10 / 16 } ,
visual = " mesh " ,
2024-03-10 12:20:27 +01:00
mesh = def.mesh ,
2024-02-13 16:33:37 +01:00
visual_size = { x = 1 , y = 1 } ,
2024-03-10 12:20:27 +01:00
textures = def.textures ,
2024-02-13 16:33:37 +01:00
} ,
2017-08-28 14:19:46 +02:00
2024-03-23 10:59:08 +01:00
hp_max = MINECART_MAX_HP ,
2024-03-23 07:13:53 +01:00
groups = groups ,
2024-03-10 12:20:27 +01:00
on_rightclick = def.on_rightclick ,
on_activate_by_rail = def.on_activate_by_rail ,
2024-03-23 07:13:53 +01:00
2024-03-10 12:20:27 +01:00
_mcl_minecarts_on_enter = def._mcl_minecarts_on_enter ,
2024-03-16 08:59:39 +01:00
_mcl_minecarts_on_place = def._mcl_minecarts_on_place ,
2024-03-16 10:17:09 +01:00
_mcl_minecarts_on_step = def._mcl_minecarts_on_step ,
2017-08-28 14:35:56 +02:00
2021-03-13 12:30:33 +01:00
_driver = nil , -- player who sits in and controls the minecart (only for minecart!)
2023-03-04 21:14:59 +01:00
_passenger = nil , -- for mobs
2017-08-28 14:19:46 +02:00
_start_pos = nil , -- Used to calculate distance for “On A Rail” achievement
2019-01-30 03:16:59 +01:00
_last_float_check = nil , -- timestamp of last time the cart was checked to be still on a rail
2020-01-30 22:05:18 +01: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 14:19:46 +02:00
_old_pos = nil ,
2024-02-13 15:45:00 +01:00
_staticdata = nil ,
2017-08-28 14:19:46 +02:00
}
function cart : on_activate ( staticdata , dtime_s )
2020-01-30 22:05:18 +01:00
-- Initialize
2019-01-30 02:04:12 +01:00
local data = minetest.deserialize ( staticdata )
if type ( data ) == " table " then
2024-02-13 11:42:38 +01:00
-- Migrate old data
if data._railtype then
data.railtype = data._railtype
data._railtype = nil
end
2024-02-13 15:45:00 +01:00
-- Fix up types
data.dir = vector.new ( data.dir )
2024-02-13 11:42:38 +01:00
2024-03-23 08:09:20 +01:00
-- Make sure all carts have an ID to isolate them
data.cart_id = staticdata.cart_id or math.random ( 1 , 1000000000 )
2024-02-13 11:42:38 +01:00
self._staticdata = data
2019-01-30 02:04:12 +01:00
end
2020-01-30 22:05:18 +01: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 15:23:14 +01:00
end
2024-03-23 10:59:08 +01:00
function cart : on_death ( killer )
2024-02-13 15:45:00 +01:00
local staticdata = self._staticdata
2024-03-23 10:59:08 +01:00
detach_driver ( self )
2024-02-14 00:31:48 +01:00
2024-03-23 10:59:08 +01:00
-- Detach passenger
if self._passenger then
local mob = self._passenger . object
mob : set_detach ( )
2024-02-13 15:45:00 +01:00
end
2024-03-23 10:59:08 +01:00
-- Leave nodes
if staticdata.attached_at then
handle_cart_leave ( self , staticdata.attached_at , staticdata.dir )
else
mcl_log ( " TODO: handle detatched minecart death " )
2021-03-13 12:30:33 +01:00
end
2017-01-05 15:23:14 +01:00
2024-03-23 10:59:08 +01:00
-- Drop items
local drop = def.drop
2024-03-31 13:49:55 +02:00
if not killer or not minetest.is_creative_enabled ( killer : get_player_name ( ) ) then
2024-03-23 10:59:08 +01:00
for d = 1 , # drop do
minetest.add_item ( self.object : get_pos ( ) , drop [ d ] )
2021-03-13 12:30:33 +01:00
end
2024-03-28 07:18:50 +01:00
elseif killer and killer : is_player ( ) then
2024-03-23 10:59:08 +01:00
local inv = killer : get_inventory ( )
for d = 1 , # drop do
if not inv : contains_item ( " main " , drop [ d ] ) then
inv : add_item ( " main " , drop [ d ] )
2021-03-13 12:30:33 +01:00
end
end
2024-02-13 15:45:00 +01:00
end
2017-01-05 15:23:14 +01:00
end
2017-08-28 14:19:46 +02:00
2023-03-20 14:29:25 +01:00
local passenger_attach_position = vector.new ( 0 , - 1.75 , 0 )
2024-03-16 10:17:09 +01:00
function cart : add_node_watch ( pos )
local staticdata = self._staticdata
2024-03-23 07:13:53 +01:00
local watches = staticdata.node_watches or { }
2024-03-16 10:17:09 +01:00
for _ , watch in ipairs ( watches ) do
if watch == pos then return end
end
watches [ # watches + 1 ] = pos
2024-03-23 07:13:53 +01:00
staticdata.node_watches = watches
2024-03-16 10:17:09 +01:00
end
2024-03-16 10:21:39 +01:00
function cart : remove_node_watch ( pos )
2024-03-23 07:13:53 +01:00
local staticdata = self._staticdata
local watches = staticdata.node_watches or { }
2024-03-16 10:21:39 +01:00
local new_watches = { }
for _ , node_pos in ipairs ( watches ) do
2024-03-23 08:09:20 +01:00
if node_pos ~= pos then
2024-03-16 10:21:39 +01:00
new_watches [ # new_watches ] = node_pos
end
end
staticdata.node_watches = new_watches
end
2024-03-16 10:17:09 +01:00
2024-02-13 10:42:07 +01:00
function cart : on_step ( dtime )
2024-02-13 15:45:00 +01:00
local staticdata = self._staticdata
2024-02-14 00:31:48 +01:00
if not staticdata then
staticdata = make_staticdata ( )
self._staticdata = staticdata
end
2024-03-23 08:09:20 +01:00
2024-03-23 10:59:08 +01:00
-- Regen
local hp = self.object : get_hp ( )
if hp < MINECART_MAX_HP then
if ( staticdata.regen_timer or 0 ) > 0.5 then
hp = hp + 1
staticdata.regen_timer = staticdata.regen_timer - 1
end
staticdata.regen_timer = ( staticdata.regen_timer or 0 ) + dtime
self.object : set_hp ( hp )
else
staticdata.regen_timer = nil
end
-- Fix railtype field
2024-04-04 09:33:16 +02:00
local pos = self.object : get_pos ( )
2024-03-23 10:59:08 +01:00
if staticdata.connected_at and not staticdata.railtype then
local node = minetest.get_node ( vector.floor ( pos ) ) . name
staticdata.railtype = minetest.get_item_group ( node , " connect_to_raillike " )
end
2024-03-23 09:09:37 +01:00
-- Cart specific behaviors
local hook = self._mcl_minecarts_on_step
if hook then hook ( self , dtime ) end
2024-03-11 01:34:26 +01:00
if ( staticdata.hopper_delay or 0 ) > 0 then
staticdata.hopper_delay = staticdata.hopper_delay - dtime
end
2024-02-13 10:42:07 +01:00
-- Controls
local ctrl , player = nil , nil
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
end
2024-02-13 15:45:00 +01:00
-- Experimental controls
2024-03-23 10:59:08 +01:00
self._go_forward = ctrl.up
self._brake = ctrl.down
2024-02-13 10:42:07 +01:00
end
-- Give achievement when player reached a distance of 1000 nodes from the start position
if vector.distance ( self._start_pos , pos ) >= 1000 then
awards.unlock ( self._driver , " mcl:onARail " )
end
end
2024-04-04 09:33:16 +02:00
if staticdata.connected_at then
do_movement ( self , dtime )
else
do_detached_movement ( self , dtime )
end
2024-02-13 10:42:07 +01:00
2024-03-23 07:13:53 +01:00
-- TODO: move this into mcl_core:cactus _mcl_minecarts_on_enter_side
2024-02-13 10:42:07 +01:00
-- Drop minecart if it collides with a cactus node
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 )
2024-03-10 12:20:27 +01:00
local drop = def.drop
2024-02-13 10:42:07 +01:00
for d = 1 , # drop do
minetest.add_item ( pos , drop [ d ] )
end
self.object : remove ( )
return
2024-03-24 06:29:44 +01:00
end
end
2017-08-28 14:19:46 +02:00
end
2017-09-05 15:28:04 +02:00
function cart : get_staticdata ( )
2024-02-14 12:47:42 +01:00
return minetest.serialize ( self._staticdata or { } )
2017-09-05 15:28:04 +02:00
end
2017-08-28 14:19:46 +02:00
minetest.register_entity ( entity_id , cart )
2017-03-21 03:58:53 +01:00
end
2017-08-29 02:36:50 +02:00
-- Place a minecart at pointed_thing
2021-05-25 12:52:25 +02:00
function mcl_minecarts . place_minecart ( itemstack , pointed_thing , placer )
2017-08-29 02:36:50 +02: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
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 " )
2024-02-13 15:57:45 +01:00
local cart_dir = mcl_minecarts : get_rail_direction ( railpos , vector.new ( 1 , 0 , 0 ) , nil , nil , railtype )
2024-02-13 15:45:00 +01:00
cart : set_yaw ( minetest.dir_to_yaw ( cart_dir ) )
-- Update static data
2019-01-30 01:58:26 +01:00
local le = cart : get_luaentity ( )
2021-05-29 16:12:33 +02:00
if le then
2024-02-14 00:31:48 +01:00
le._staticdata = make_staticdata ( railtype , railpos , cart_dir )
2019-01-30 01:58:26 +01:00
end
2024-02-13 15:45:00 +01:00
2024-03-28 07:18:50 +01:00
-- Call placer
if le._mcl_minecarts_on_place then
le._mcl_minecarts_on_place ( le , placer )
end
2024-03-11 01:34:26 +01:00
handle_cart_enter ( le , railpos )
2017-08-29 02:36:50 +02:00
2020-07-10 16:08:40 +02:00
local pname = " "
if placer then
pname = placer : get_player_name ( )
end
if not minetest.is_creative_enabled ( pname ) then
2017-08-29 02:36:50 +02:00
itemstack : take_item ( )
end
return itemstack
end
2024-03-11 08:26:51 +01:00
local function dropper_place_minecart ( dropitem , pos )
local node = minetest.get_node ( pos )
local nodedef = minetest.registered_nodes [ node.name ]
-- Don't try to place the minecart if pos isn't a rail
if ( nodedef.groups . rail or 0 ) == 0 then return false end
mcl_minecarts.place_minecart ( dropitem , {
above = pos ,
under = vector.offset ( pos , 0 , - 1 , 0 )
} )
return true
end
2018-02-01 22:45:19 +01:00
2021-05-25 12:52:25 +02:00
local function register_craftitem ( itemstring , entity_id , description , tt_help , longdesc , usagehelp , icon , creative )
2017-08-29 02:36:50 +02:00
entity_mapping [ itemstring ] = entity_id
2017-08-30 21:51:27 +02:00
local groups = { minecart = 1 , transport = 1 }
if creative == false then
groups.not_in_creative_inventory = 1
end
2017-08-28 14:19:46 +02:00
local def = {
stack_max = 1 ,
2024-03-11 08:26:51 +01:00
_mcl_dropper_on_drop = dropper_place_minecart ,
2017-08-28 14:19:46 +02:00
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 16:08:40 +02:00
return mcl_minecarts.place_minecart ( itemstack , pointed_thing , placer )
2017-08-28 14:19:46 +02:00
end ,
2018-02-01 22:45:19 +01: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
2024-02-13 15:57:45 +01:00
local pointed_thing = { under = droppos , above = vector.new ( droppos.x , droppos.y + 1 , droppos.z ) }
2018-02-01 22:45:19 +01:00
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 21:51:27 +02:00
groups = groups ,
2017-08-28 14:19:46 +02:00
}
def.description = description
2020-02-19 04:54:17 +01:00
def._tt_help = tt_help
2017-08-28 15:04:50 +02:00
def._doc_items_longdesc = longdesc
2017-08-28 14:19:46 +02:00
def._doc_items_usagehelp = usagehelp
def.inventory_image = icon
def.wield_image = icon
minetest.register_craftitem ( itemstring , def )
end
2018-02-02 01:21:19 +01: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
] ]
2024-03-10 12:20:27 +01:00
local function register_minecart ( def )
register_entity ( def.entity_id , def )
register_craftitem ( def.itemstring , def.entity_id , def.description , def.tt_help , def.longdesc , def.usagehelp , def.icon , def.creative )
2021-05-29 16:12:33 +02:00
if minetest.get_modpath ( " doc_identifier " ) then
2024-03-28 16:06:26 +01:00
doc.sub . identifier.register_object ( def.entity_id , " craftitems " , def.itemstring )
2017-08-28 15:04:50 +02:00
end
end
-- Minecart
2024-03-10 12:20:27 +01:00
register_minecart ( {
itemstring = " mcl_minecarts:minecart " ,
entity_id = " mcl_minecarts:minecart " ,
description = S ( " Minecart " ) ,
tt_helop = S ( " Vehicle for fast travel on rails " ) ,
long_descp = 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 " ..
S ( " To obtain the minecart, punch it while holding down the sneak key. " ) .. " \n " ..
S ( " If it moves over a powered activator rail, you'll get ejected. " ) ,
mesh = " mcl_minecarts_minecart.b3d " ,
textures = { " mcl_minecarts_minecart.png " } ,
icon = " mcl_minecarts_minecart_normal.png " ,
drop = { " mcl_minecarts:minecart " } ,
on_rightclick = function ( self , clicker )
2021-03-13 12:30:33 +01: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
2024-03-13 13:08:12 +01:00
--detach_driver(self)
2024-03-13 13:26:15 +01:00
elseif not self._driver and not clicker : get_player_control ( ) . sneak then
2021-03-13 12:30:33 +01:00
self._driver = player_name
2019-02-01 06:33:07 +01:00
self._start_pos = self.object : get_pos ( )
2021-03-13 12:30:33 +01:00
mcl_player.player_attached [ player_name ] = true
2024-02-13 15:57:45 +01:00
clicker : set_attach ( self.object , " " , vector.new ( 1 , - 1.75 , - 2 ) , vector.new ( 0 , 0 , 0 ) )
2018-07-09 23:45:54 +02: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 )
2024-02-13 15:57:45 +01:00
player : set_eye_offset ( vector.new ( 0 , - 5.5 , 0 ) , vector.new ( 0 , - 4 , 0 ) )
2021-07-20 16:14:34 +02:00
mcl_title.set ( clicker , " actionbar " , { text = S ( " Sneak to dismount " ) , color = " white " , stay = 60 } )
2018-07-09 23:45:54 +02:00
end
end , name )
2017-08-28 15:04:50 +02:00
end
2024-03-10 12:20:27 +01:00
end ,
2024-03-23 08:09:20 +01:00
on_activate_by_rail = activate_normal_minecart ,
_mcl_minecarts_on_step = function ( self , dtime )
-- Grab mob
if math.random ( 1 , 20 ) > 15 and not self._passenger then
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
self._passenger = entity
mob : set_attach ( self.object , " " , passenger_attach_position , vector.zero ( ) )
break
end
end
end
end
elseif self._passenger then
local passenger_pos = self._passenger . object : get_pos ( )
if not passenger_pos then
self._passenger = nil
end
end
end
2024-03-10 12:20:27 +01:00
} )
2017-08-28 14:19:46 +02:00
2017-08-28 15:04:50 +02:00
-- Minecart with Chest
2024-03-10 12:20:27 +01:00
register_minecart ( {
itemstring = " mcl_minecarts:chest_minecart " ,
entity_id = " mcl_minecarts:chest_minecart " ,
description = S ( " Minecart with Chest " ) ,
tt_help = nil ,
longdesc = nil ,
usagehelp = nil ,
mesh = " mcl_minecarts_minecart_chest.b3d " ,
textures = {
" mcl_chests_normal.png " ,
" mcl_minecarts_minecart.png "
} ,
icon = " mcl_minecarts_minecart_chest.png " ,
drop = { " mcl_minecarts:minecart " , " mcl_chests:chest " } ,
2024-03-23 07:13:53 +01:00
groups = { container = 1 } ,
2024-03-10 12:20:27 +01:00
on_rightclick = nil ,
on_activate_by_rail = nil ,
creative = true
} )
2022-10-15 00:37:29 +02:00
mcl_entity_invs.register_inv ( " mcl_minecarts:chest_minecart " , " Minecart " , 27 , false , true )
2017-08-28 14:19:46 +02:00
2017-08-28 15:04:50 +02:00
-- Minecart with Furnace
2024-03-10 12:20:27 +01:00
register_minecart ( {
itemstring = " mcl_minecarts:furnace_minecart " ,
entity_id = " mcl_minecarts:furnace_minecart " ,
description = S ( " Minecart with Furnace " ) ,
tt_help = nil ,
longdesc = S ( " A minecart with furnace is a vehicle that travels on rails. It can propel itself with fuel. " ) ,
usagehelp = 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. " ) ,
mesh = " mcl_minecarts_minecart_block.b3d " ,
textures = {
2017-08-28 15:04:50 +02:00
" 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 " ,
} ,
2024-03-10 12:20:27 +01:00
icon = " mcl_minecarts_minecart_furnace.png " ,
drop = { " mcl_minecarts:minecart " , " mcl_furnaces:furnace " } ,
on_rightclick = function ( self , clicker )
2024-03-23 08:32:32 +01:00
local staticdata = self._staticdata
2024-03-10 12:20:27 +01:00
-- Feed furnace with coal
2017-08-29 02:36:50 +02:00
if not clicker or not clicker : is_player ( ) then
return
end
local held = clicker : get_wielded_item ( )
if minetest.get_item_group ( held : get_name ( ) , " coal " ) == 1 then
2024-03-23 08:32:32 +01:00
staticdata.fueltime = ( staticdata.fueltime or 0 ) + 180
2017-08-29 02:36:50 +02:00
2024-03-23 09:09:37 +01:00
-- Trucate to 27 minutes (9 uses)
if staticdata.fueltime > 27 * 60 then
staticdata.fuel_time = 27 * 60
end
2020-07-10 16:08:40 +02:00
if not minetest.is_creative_enabled ( clicker : get_player_name ( ) ) then
2017-08-29 02:36:50 +02:00
held : take_item ( )
2020-04-30 18:13:05 +02:00
local index = clicker : get_wield_index ( )
2017-08-29 02:36:50 +02:00
local inv = clicker : get_inventory ( )
inv : set_stack ( " main " , index , held )
end
2020-01-30 20:38:31 +01: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 02:36:50 +02:00
end
2024-03-10 12:20:27 +01:00
end ,
on_activate_by_rail = nil ,
2024-03-23 08:09:20 +01:00
creative = true ,
_mcl_minecarts_on_step = function ( self , dtime )
2024-03-23 08:32:32 +01:00
local staticdata = self._staticdata
2024-03-23 08:09:20 +01:00
-- Update furnace stuff
2024-03-23 08:32:32 +01:00
if ( staticdata.fueltime or 0 ) > 0 then
staticdata.fueltime = ( staticdata.fueltime or dtime ) - dtime
if staticdata.fueltime <= 0 then
2024-03-23 08:09:20 +01:00
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 " ,
} } )
2024-03-23 08:32:32 +01:00
staticdata.fueltime = 0
2024-03-23 08:09:20 +01:00
end
end
end
2024-03-10 12:20:27 +01:00
} )
2024-03-28 07:18:50 +01:00
function table_metadata ( table )
return {
table = table ,
set_string = function ( self , key , value )
2024-04-04 00:07:14 +02:00
--print("set_string("..tostring(key)..", "..tostring(value)..")")
2024-03-28 07:18:50 +01:00
self.table [ key ] = tostring ( value )
end ,
get_string = function ( self , key )
if self.table [ key ] then
return tostring ( self.table [ key ] )
end
end
}
end
2017-08-28 14:19:46 +02:00
2017-08-28 15:04:50 +02:00
-- Minecart with Command Block
2024-03-10 12:20:27 +01:00
register_minecart ( {
itemstring = " mcl_minecarts:command_block_minecart " ,
entity_id = " mcl_minecarts:command_block_minecart " ,
description = S ( " Minecart with Command Block " ) ,
tt_help = nil ,
loncdesc = nil ,
usagehelp = nil ,
mesh = " mcl_minecarts_minecart_block.b3d " ,
textures = {
2017-08-28 15:04:50 +02:00
" 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 " ,
} ,
2024-03-10 12:20:27 +01:00
icon = " mcl_minecarts_minecart_command_block.png " ,
drop = { " mcl_minecarts:minecart " } ,
2024-03-28 07:18:50 +01:00
on_rightclick = function ( self , clicker )
self._staticdata . meta = self._staticdata . meta or { }
local meta = table_metadata ( self._staticdata . meta )
mesecon.commandblock . handle_rightclick ( meta , clicker )
end ,
2024-03-16 08:59:39 +01:00
_mcl_minecarts_on_place = function ( self , placer )
2024-03-28 07:18:50 +01:00
-- Create a fake metadata object that stores into the cart's staticdata
self._staticdata . meta = self._staticdata . meta or { }
local meta = table_metadata ( self._staticdata . meta )
mesecon.commandblock . initialize ( meta )
mesecon.commandblock . place ( meta , placer )
2024-03-16 08:59:39 +01:00
end ,
on_activate_by_rail = function ( self , timer )
2024-03-28 07:18:50 +01:00
self._staticdata . meta = self._staticdata . meta or { }
local meta = table_metadata ( self._staticdata . meta )
mesecon.commandblock . action_on ( meta , self.object : get_pos ( ) )
2024-03-16 08:59:39 +01:00
end ,
2024-03-28 07:18:50 +01:00
creative = true
2024-03-10 12:20:27 +01:00
} )
2017-08-28 15:04:50 +02:00
-- Minecart with Hopper
2024-03-10 12:20:27 +01:00
register_minecart ( {
itemstring = " mcl_minecarts:hopper_minecart " ,
entity_id = " mcl_minecarts:hopper_minecart " ,
description = S ( " Minecart with Hopper " ) ,
tt_help = nil ,
longdesc = nil ,
usagehelp = nil ,
mesh = " mcl_minecarts_minecart_hopper.b3d " ,
textures = {
2017-08-28 15:04:50 +02:00
" mcl_hoppers_hopper_inside.png " ,
" mcl_minecarts_minecart.png " ,
" mcl_hoppers_hopper_outside.png " ,
" mcl_hoppers_hopper_top.png " ,
} ,
2024-03-10 12:20:27 +01:00
icon = " mcl_minecarts_minecart_hopper.png " ,
drop = { " mcl_minecarts:minecart " , " mcl_hoppers:hopper " } ,
2024-03-23 07:13:53 +01:00
groups = { container = 1 } ,
2024-03-10 12:20:27 +01:00
on_rightclick = nil ,
on_activate_by_rail = nil ,
2024-03-23 08:09:20 +01:00
_mcl_minecarts_on_enter = function ( self , pos )
2024-03-11 01:34:26 +01:00
local staticdata = self._staticdata
if ( staticdata.hopper_delay or 0 ) > 0 then
return
end
-- try to pull from containers into our inventory
local inv = mcl_entity_invs.load_inv ( self , 5 )
local above_pos = pos + vector.new ( 0 , 1 , 0 )
2024-03-23 08:09:20 +01:00
mcl_util.hopper_pull_to_inventory ( inv , ' main ' , above_pos , pos )
2024-03-11 01:34:26 +01:00
staticdata.hopper_delay = ( staticdata.hopper_delay or 0 ) + ( 1 / 20 )
2024-03-10 12:20:27 +01:00
end ,
2024-03-23 08:09:20 +01:00
_mcl_minecarts_on_step = function ( self , dtime )
hopper_take_item ( self , dtime )
end ,
2024-03-10 12:20:27 +01:00
creative = true
} )
2022-11-12 18:47:20 +01:00
mcl_entity_invs.register_inv ( " mcl_minecarts:hopper_minecart " , " Hopper Minecart " , 5 , false , true )
2017-08-28 14:19:46 +02:00
2017-08-28 15:04:50 +02:00
-- Minecart with TNT
2024-03-10 12:20:27 +01:00
register_minecart ( {
itemstring = " mcl_minecarts:tnt_minecart " ,
entity_id = " mcl_minecarts:tnt_minecart " ,
description = S ( " Minecart with TNT " ) ,
tt_help = S ( " Vehicle for fast travel on rails " ) .. " \n " .. S ( " Can be ignited by tools or powered activator rail " ) ,
longdesc = S ( " A minecart with TNT is an explosive vehicle that travels on rail. " ) ,
usagehelp = 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 " ..
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. " ) ,
mesh = " mcl_minecarts_minecart_block.b3d " ,
textures = {
2017-08-28 15:04:50 +02:00
" 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 " ,
} ,
2024-03-10 12:20:27 +01:00
icon = " mcl_minecarts_minecart_tnt.png " ,
drop = { " mcl_minecarts:minecart " , " mcl_tnt:tnt " } ,
on_rightclick = function ( self , clicker )
-- Ingite
2020-01-30 22:05:18 +01:00
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 16:08:40 +02:00
if not minetest.is_creative_enabled ( clicker : get_player_name ( ) ) then
2020-01-30 22:05:18 +01:00
held : add_wear ( 65535 / 65 ) -- 65 uses
2020-02-14 21:09:36 +01:00
local index = clicker : get_wield_index ( )
2020-01-30 22:05:18 +01:00
local inv = clicker : get_inventory ( )
inv : set_stack ( " main " , index , held )
end
activate_tnt_minecart ( self )
end
2024-03-10 12:20:27 +01:00
end ,
on_activate_by_rail = activate_tnt_minecart ,
2024-03-23 08:09:20 +01:00
creative = true ,
_mcl_minecarts_on_step = function ( self , dtime )
-- 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 ( )
mcl_explosions.explode ( pos , 4 , { drop_chance = 1.0 } )
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
end ,
2024-03-10 12:20:27 +01:00
} )
2017-08-28 14:19:46 +02:00
2017-01-05 15:23:14 +01:00
minetest.register_craft ( {
output = " mcl_minecarts:minecart " ,
recipe = {
2017-02-11 21:14:40 +01:00
{ " mcl_core:iron_ingot " , " " , " mcl_core:iron_ingot " } ,
{ " mcl_core:iron_ingot " , " mcl_core:iron_ingot " , " mcl_core:iron_ingot " } ,
2017-01-05 15:23:14 +01:00
} ,
} )
2017-08-28 14:19:46 +02:00
minetest.register_craft ( {
2020-01-30 22:31:44 +01:00
output = " mcl_minecarts:tnt_minecart " ,
2017-08-28 14:19:46 +02:00
recipe = {
2020-01-30 22:31:44 +01:00
{ " mcl_tnt:tnt " } ,
2017-08-28 14:19:46 +02:00
{ " mcl_minecarts:minecart " } ,
} ,
} )
2022-09-18 21:32:45 +02:00
minetest.register_craft ( {
2021-05-25 00:43:08 +02:00
output = " mcl_minecarts:furnace_minecart " ,
recipe = {
{ " mcl_furnaces:furnace " } ,
{ " mcl_minecarts:minecart " } ,
} ,
} )
2022-11-12 18:47:20 +01:00
minetest.register_craft ( {
2021-05-25 00:43:08 +02:00
output = " mcl_minecarts:hopper_minecart " ,
recipe = {
{ " mcl_hoppers:hopper " } ,
{ " mcl_minecarts:minecart " } ,
} ,
} )
2022-11-12 18:47:20 +01:00
2021-05-25 00:43:08 +02:00
minetest.register_craft ( {
output = " mcl_minecarts:chest_minecart " ,
recipe = {
{ " mcl_chests:chest " } ,
{ " mcl_minecarts:minecart " } ,
} ,
2022-09-23 05:43:31 +02:00
} )
2021-05-25 00:43:08 +02:00
2021-03-30 00:58:45 +02: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 12:11:59 +01:00
end