2016-05-29 11:27:30 -07:00
--trainlogic.lua
--controls train entities stuff about connecting/disconnecting/colliding trains and other things
2019-03-09 02:29:22 -08:00
local setting_overrun_mode = minetest.settings : get ( " advtrains_overrun_mode " )
2016-05-29 11:27:30 -07:00
2016-08-28 11:59:54 -07:00
local benchmark = false
local bm = { }
local bmlt = 0
local bmsteps = 0
local bmstepint = 200
2017-01-04 12:34:18 -08:00
atprintbm = function ( action , ta )
2016-08-28 11:59:54 -07:00
if not benchmark then return end
local t = ( os.clock ( ) - ta ) * 1000
if not bm [ action ] then
bm [ action ] = t
else
bm [ action ] = bm [ action ] + t
end
bmlt = bmlt + t
end
function endstep ( )
if not benchmark then return end
bmsteps = bmsteps - 1
if bmsteps <= 0 then
bmsteps = bmstepint
for key , value in pairs ( bm ) do
minetest.chat_send_all ( key .. " " .. ( value / bmstepint ) .. " ms avg. " )
end
minetest.chat_send_all ( " Total time consumed by all advtrains actions per step: " .. ( bmlt / bmstepint ) .. " ms avg. " )
bm = { }
bmlt = 0
end
end
2016-06-02 05:42:01 -07:00
2018-01-07 11:41:48 -08:00
--acceleration for lever modes (trainhud.lua), per wagon
local t_accel_all = {
[ 0 ] = - 10 ,
[ 1 ] = - 3 ,
2020-04-23 00:44:17 -07:00
[ 11 ] = - 2 , -- calculation base for LZB
2018-01-07 11:41:48 -08:00
[ 2 ] = - 0.5 ,
2018-01-09 12:30:56 -08:00
[ 4 ] = 0.5 ,
2018-01-07 11:41:48 -08:00
}
--acceleration per engine
local t_accel_eng = {
[ 0 ] = 0 ,
[ 1 ] = 0 ,
2020-04-23 00:44:17 -07:00
[ 11 ] = 0 ,
2018-01-07 11:41:48 -08:00
[ 2 ] = 0 ,
2018-01-09 12:30:56 -08:00
[ 4 ] = 1.5 ,
2018-01-07 11:41:48 -08:00
}
2016-05-29 11:27:30 -07:00
2020-04-23 00:44:17 -07:00
local VLEVER_EMERG = 0
local VLEVER_BRAKE = 1
local VLEVER_LZBCALC = 11
local VLEVER_ROLL = 2
local VLEVER_HOLD = 3
local VLEVER_ACCEL = 4
2018-07-21 07:43:37 -07:00
tp_player_tmr = 0
2017-04-29 10:44:43 -07:00
advtrains.mainloop_trainlogic = function ( dtime )
2017-02-08 15:11:28 -08:00
--build a table of all players indexed by pts. used by damage and door system.
advtrains.playersbypts = { }
for _ , player in pairs ( minetest.get_connected_players ( ) ) do
if not advtrains.player_to_train_mapping [ player : get_player_name ( ) ] then
--players in train are not subject to damage
local ptspos = minetest.pos_to_string ( vector.round ( player : getpos ( ) ) )
advtrains.playersbypts [ ptspos ] = player
end
end
2018-07-21 07:43:37 -07:00
if tp_player_tmr <= 0 then
-- teleport players to their train every 2 seconds
for _ , player in pairs ( minetest.get_connected_players ( ) ) do
advtrains.tp_player_to_train ( player )
end
tp_player_tmr = 2
else
tp_player_tmr = tp_player_tmr - dtime
end
2016-05-29 11:27:30 -07:00
--regular train step
2018-04-23 06:51:50 -07:00
--[[ structure:
1. make trains calculate their occupation windows when needed ( a )
2. when occupation tells us so , restore the occupation tables ( a )
4. make trains move and update their new occupation windows and write changes
to occupation tables ( b )
5. make trains do other stuff ( c )
] ] --
2016-06-02 05:42:01 -07:00
local t = os.clock ( )
2018-04-23 06:51:50 -07:00
2016-05-29 11:27:30 -07:00
for k , v in pairs ( advtrains.trains ) do
2018-05-29 03:27:02 -07:00
advtrains.atprint_context_tid = k
2018-05-17 03:30:30 -07:00
advtrains.train_ensure_init ( k , v )
2016-05-29 11:27:30 -07:00
end
2018-04-23 06:51:50 -07:00
2019-01-22 03:26:31 -08:00
advtrains.lock_path_inval = true
2017-01-27 14:43:01 -08:00
for k , v in pairs ( advtrains.trains ) do
2018-05-29 03:27:02 -07:00
advtrains.atprint_context_tid = k
2018-04-25 07:38:12 -07:00
advtrains.train_step_b ( k , v , dtime )
2018-04-23 06:51:50 -07:00
end
for k , v in pairs ( advtrains.trains ) do
2018-05-29 03:27:02 -07:00
advtrains.atprint_context_tid = k
2018-04-25 07:38:12 -07:00
advtrains.train_step_c ( k , v , dtime )
2016-11-10 11:24:47 -08:00
end
2019-01-22 03:26:31 -08:00
advtrains.lock_path_inval = false
2017-05-03 07:31:13 -07:00
advtrains.atprint_context_tid = nil
2018-04-25 07:38:12 -07:00
2017-01-04 12:34:18 -08:00
atprintbm ( " trainsteps " , t )
2016-08-28 11:59:54 -07:00
endstep ( )
2017-04-29 10:13:15 -07:00
end
2016-05-29 11:27:30 -07:00
2018-07-21 07:43:37 -07:00
function advtrains . tp_player_to_train ( player )
local pname = player : get_player_name ( )
local id = advtrains.player_to_train_mapping [ pname ]
if id then
local train = advtrains.trains [ id ]
if not train then advtrains.player_to_train_mapping [ pname ] = nil return end
--set the player to the train position.
--minetest will emerge the area and load the objects, which then will call reattach_all().
--because player is in mapping, it will not be subject to dying.
2018-12-16 11:39:55 -08:00
player : setpos ( train.last_pos )
2018-07-21 07:43:37 -07:00
end
end
2019-12-03 12:35:42 -08:00
minetest.register_on_joinplayer ( function ( player )
advtrains.hud [ player : get_player_name ( ) ] = nil
advtrains.hhud [ player : get_player_name ( ) ] = nil
2018-07-21 07:43:37 -07:00
--independent of this, cause all wagons of the train which are loaded to reattach their players
--needed because already loaded wagons won't call reattach_all()
for _ , wagon in pairs ( minetest.luaentities ) do
if wagon.is_wagon and wagon.initialized and wagon.train_id == id then
wagon : reattach_all ( )
2017-02-08 15:11:28 -08:00
end
end
end )
2018-07-21 07:43:37 -07:00
2017-02-08 15:11:28 -08:00
minetest.register_on_dieplayer ( function ( player )
2017-04-29 10:44:43 -07:00
local pname = player : get_player_name ( )
local id = advtrains.player_to_train_mapping [ pname ]
if id then
local train = advtrains.trains [ id ]
if not train then advtrains.player_to_train_mapping [ pname ] = nil return end
for _ , wagon in pairs ( minetest.luaentities ) do
if wagon.is_wagon and wagon.initialized and wagon.train_id == id then
--when player dies, detach him from the train
--call get_off_plr on every wagon since we don't know which one he's on.
wagon : get_off_plr ( pname )
end
2017-02-08 15:11:28 -08:00
end
end
end )
2018-04-23 06:51:50 -07:00
2017-01-27 14:43:01 -08:00
--[[
2018-04-23 06:51:50 -07:00
Zone diagram of a train ( copy from occupation.lua ! ) :
| ___ | | ___ | --> Direction of travel
oo oo + oo oo
=|=======|===|===========|===|=======|===================|========|===
| SafetyB | CpB | Train | CpF | SafetyF | Brake | Aware |
[ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ]
This mapping from indices in occwindows to zone ids is contained in WINDOW_ZONE_IDS
2018-05-17 02:16:04 -07:00
The occupation system has been abandoned . The constants will still be used
to determine the couple distance
( because of the reverse lookup , the couple system simplifies a lot ... )
2018-04-23 06:51:50 -07:00
] ] --
-- unless otherwise stated, in meters.
local SAFETY_ZONE = 10
local COUPLE_ZONE = 2 --value in index positions!
local BRAKE_SPACE = 10
local AWARE_ZONE = 10
local WINDOW_ZONE_IDS = {
2 , -- 1 - SafetyB
4 , -- 2 - CpB
1 , -- 3 - Train
5 , -- 4 - CpF
3 , -- 5 - SafetyF
6 , -- 6 - Brake
7 , -- 7 - Aware
}
-- If a variable does not exist in the table, it is assigned the default value
local function assertdef ( tbl , var , def )
if not tbl [ var ] then
tbl [ var ] = def
2016-12-22 09:55:10 -08:00
end
2018-04-23 06:51:50 -07:00
end
2018-10-10 12:49:52 -07:00
function advtrains . get_acceleration ( train , lever )
local acc_all = t_accel_all [ lever ]
2020-04-23 00:44:17 -07:00
if not acc_all then return 0 end
2018-10-10 12:49:52 -07:00
local acc_eng = t_accel_eng [ lever ]
local nwagons = # train.trainparts
2019-01-21 13:41:57 -08:00
if nwagons == 0 then
-- empty train! avoid division through zero
return - 1
end
2018-10-10 12:49:52 -07:00
local acc = acc_all + ( acc_eng * train.locomotives_in_train ) / nwagons
return acc
end
2018-04-23 06:51:50 -07:00
2018-05-17 02:16:04 -07:00
-- Small local util function to recalculate train's end index
local function recalc_end_index ( train )
train.end_index = advtrains.path_get_index_by_offset ( train , train.index , - train.trainlen )
end
-- Occupation Callback system
-- see occupation.lua
2018-06-29 07:16:55 -07:00
-- signature is advtrains.te_register_on_<?>(function(id, train) ... end)
2018-05-17 02:16:04 -07:00
2018-05-17 12:37:01 -07:00
local function mkcallback ( name )
local callt = { }
advtrains [ " te_register_on_ " .. name ] = function ( func )
assertt ( func , " function " )
table.insert ( callt , func )
end
2020-04-23 00:44:17 -07:00
return callt , function ( id , train , param1 , param2 , param3 )
2018-05-17 12:37:01 -07:00
for _ , f in ipairs ( callt ) do
2020-04-23 00:44:17 -07:00
f ( id , train , param1 , param2 , param3 )
2018-05-17 12:37:01 -07:00
end
2018-05-17 02:16:04 -07:00
end
2018-04-23 06:51:50 -07:00
end
2018-05-17 12:37:01 -07:00
local callbacks_new_path , run_callbacks_new_path = mkcallback ( " new_path " )
2020-04-23 00:44:17 -07:00
local callbacks_invahead
callbacks_invahead , advtrains.run_callbacks_invahead = mkcallback ( " invalidate_ahead " ) -- (id, train, start_idx)
2018-05-17 12:37:01 -07:00
local callbacks_update , run_callbacks_update = mkcallback ( " update " )
local callbacks_create , run_callbacks_create = mkcallback ( " create " )
local callbacks_remove , run_callbacks_remove = mkcallback ( " remove " )
2018-04-23 06:51:50 -07:00
2018-05-17 03:30:30 -07:00
-- train_ensure_init: responsible for creating a state that we can work on, after one of the following events has happened:
2018-04-23 06:51:50 -07:00
-- - the train's path got cleared
2018-05-17 02:16:04 -07:00
-- - save files were loaded
2018-04-23 06:51:50 -07:00
-- Additionally, this gets called outside the step cycle to initialize and/or remove a train, then occ_write_mode is set.
2018-05-17 03:30:30 -07:00
function advtrains . train_ensure_init ( id , train )
2018-12-10 13:57:16 -08:00
if not train then
atwarn ( " train_ensure_init: Called with id = " , id , " but a nil train! " )
atwarn ( debug.traceback ( ) )
return nil
end
2018-04-23 06:51:50 -07:00
train.dirty = true
2018-10-29 13:19:49 -07:00
if train.no_step then return nil end
2018-04-23 06:51:50 -07:00
assertdef ( train , " velocity " , 0 )
2018-10-10 12:49:52 -07:00
--assertdef(train, "tarvelocity", 0)
2018-04-23 06:51:50 -07:00
assertdef ( train , " acceleration " , 0 )
2018-05-17 02:16:04 -07:00
assertdef ( train , " id " , id )
2018-10-10 12:49:52 -07:00
assertdef ( train , " ctrl " , { } )
2018-04-23 06:51:50 -07:00
if not train.drives_on or not train.max_speed then
advtrains.update_trainpart_properties ( id )
2016-11-24 11:25:07 -08:00
end
2018-04-23 06:51:50 -07:00
--restore path
2017-05-03 07:31:13 -07:00
if not train.path then
2017-01-27 14:43:01 -08:00
if not train.last_pos then
2019-01-22 08:14:32 -08:00
atlog ( " Train " , id , " : Restoring path failed, no last_pos set! Train will be disabled. You can try to fix the issue in the save file. " )
2018-04-23 06:51:50 -07:00
train.no_step = true
2018-10-29 13:19:49 -07:00
return nil
2016-11-10 11:24:47 -08:00
end
2018-04-23 06:51:50 -07:00
if not train.last_connid then
2018-04-25 07:38:12 -07:00
atwarn ( " Train " , id , " : Restoring path: no last_connid set! Will assume 1 " )
2018-10-29 15:42:47 -07:00
train.last_connid = 1
2018-10-29 15:51:41 -07:00
--[[
Why this fix was necessary :
Issue : Migration problems on Grand Theft Auto Minetest
1. Run of this code , warning printed .
2. path_create failed with result == nil ( there was an unloaded node , wait_for_path set )
3. in consequence , the supposed call to path_setrestore does not happen
4. train.last_connid is still unset
5. next step , warning is printed again
Result : log flood .
] ]
2016-11-10 11:24:47 -08:00
end
2017-01-27 14:43:01 -08:00
2018-04-25 07:38:12 -07:00
local result = advtrains.path_create ( train , train.last_pos , train.last_connid or 1 , train.last_frac or 0 )
2017-01-27 14:43:01 -08:00
2018-04-23 06:51:50 -07:00
if result == false then
2019-01-22 08:14:32 -08:00
atlog ( " Train " , id , " : Restoring path failed, node at " , train.last_pos , " is gone! Train will be disabled. You can try to place a rail at this position and restart the server. " )
2018-04-23 06:51:50 -07:00
train.no_step = true
2018-10-29 13:19:49 -07:00
return nil
2018-04-23 06:51:50 -07:00
elseif result == nil then
if not train.wait_for_path then
2019-01-22 08:14:32 -08:00
atlog ( " Train " , id , " : Can't initialize: Waiting for the (yet unloaded) node at " , train.last_pos , " to be loaded. " )
2018-04-23 06:51:50 -07:00
end
train.wait_for_path = true
2018-10-29 13:19:49 -07:00
return false
2016-11-10 11:24:47 -08:00
end
2018-04-23 06:51:50 -07:00
-- by now, we should have a working initial path
2018-04-25 07:38:12 -07:00
train.wait_for_path = false
2018-05-17 02:16:04 -07:00
2018-04-23 06:51:50 -07:00
advtrains.update_trainpart_properties ( id )
2018-05-17 02:16:04 -07:00
recalc_end_index ( train )
2018-05-29 03:27:02 -07:00
--atdebug("Train",id,": Successfully restored path at",train.last_pos," connid",train.last_connid," frac",train.last_frac)
2018-05-17 02:16:04 -07:00
-- run on_new_path callbacks
run_callbacks_new_path ( id , train )
2016-12-31 06:16:19 -08:00
end
2018-04-23 06:51:50 -07:00
train.dirty = false -- TODO einbauen!
2018-10-29 13:19:49 -07:00
return true
2018-04-23 06:51:50 -07:00
end
2020-04-23 00:44:17 -07:00
local function v_target_apply ( v_targets , lever , vel )
v_targets [ lever ] = v_targets [ lever ] and math.min ( v_targets [ lever ] , vel ) or vel
end
2018-04-25 07:38:12 -07:00
function advtrains . train_step_b ( id , train , dtime )
2019-01-21 14:40:09 -08:00
if train.no_step or train.wait_for_path or not train.path then return end
2017-01-27 14:43:01 -08:00
2018-04-23 06:51:50 -07:00
-- in this code, we check variables such as path_trk_? and path_dist. We need to ensure that the path is known for the whole 'Train' zone
2018-04-25 07:38:12 -07:00
advtrains.path_get ( train , atfloor ( train.index + 2 ) )
advtrains.path_get ( train , atfloor ( train.end_index - 1 ) )
2016-05-29 12:33:09 -07:00
2020-04-23 00:44:17 -07:00
--[[ again, new velocity control:
There are two heterogenous means of control :
-> set a fixed acceleration and ignore speed ( user )
-> steer towards a target speed , distance doesn ' t matter
-> needs to specify the maximum acceleration / deceleration values they are willing to accelerate / brake with
-> Reach a target speed after a certain distance ( LZB , handled specially )
] ] --
2017-01-27 14:43:01 -08:00
--- 3. handle velocity influences ---
2020-04-23 00:44:17 -07:00
-- Variables for "desired velocities" of various parts of the code
local v_targets = { } --Table keys: VLEVER_*
2017-01-27 14:43:01 -08:00
local train_moves = ( train.velocity ~= 0 )
2016-05-29 12:33:09 -07:00
2016-05-29 11:27:30 -07:00
if train.recently_collided_with_env then
2016-08-28 12:58:13 -07:00
if not train_moves then
2018-01-07 10:00:43 -08:00
train.recently_collided_with_env = nil --reset status when stopped
2016-05-29 11:27:30 -07:00
end
2020-04-23 00:44:17 -07:00
v_target_apply ( v_targets , VLEVER_EMERG , 0 )
2016-05-29 11:27:30 -07:00
end
if train.locomotives_in_train == 0 then
2020-04-23 00:44:17 -07:00
v_target_apply ( v_targets , VLEVER_ROLL , 0 )
2016-05-29 11:27:30 -07:00
end
2017-01-04 03:02:00 -08:00
2017-05-03 07:31:13 -07:00
--- 3a. this can be useful for debugs/warnings and is used for check_trainpartload ---
2018-04-25 07:38:12 -07:00
local t_info , train_pos = sid ( id ) , advtrains.path_get ( train , atfloor ( train.index ) )
2017-05-03 07:31:13 -07:00
if train_pos then
t_info = t_info .. " @ " .. minetest.pos_to_string ( train_pos )
2017-05-15 04:37:06 -07:00
--atprint("train_pos:",train_pos)
2017-05-03 07:31:13 -07:00
end
2017-01-27 14:43:01 -08:00
--apply off-track handling:
2018-04-23 06:51:50 -07:00
local front_off_track = train.index > train.path_trk_f
local back_off_track = train.end_index < train.path_trk_b
2018-07-04 05:04:41 -07:00
train.off_track = front_off_track or back_off_track
2017-05-03 07:31:13 -07:00
2020-04-23 00:44:17 -07:00
if back_off_track then
v_target_apply ( v_targets , VLEVER_EMERG , 1 )
else
if front_off_track then
v_target_apply ( v_targets , VLEVER_EMERG , 0 )
end
2017-02-05 08:22:13 -08:00
end
2017-01-27 14:43:01 -08:00
2018-10-10 12:49:52 -07:00
2018-01-07 11:41:48 -08:00
--interpret ATC command and apply auto-lever control when not actively controlled
2020-04-23 00:44:17 -07:00
local v0 = train.velocity
2018-10-10 12:49:52 -07:00
if train.ctrl . user then
advtrains.atc . train_reset_command ( train )
2018-01-07 11:41:48 -08:00
else
2018-05-17 02:16:04 -07:00
local braketar = train.atc_brake_target
local emerg = false -- atc_brake_target==-1 means emergency brake (BB command)
if braketar == - 1 then
braketar = 0
emerg = true
end
2020-04-23 00:44:17 -07:00
if braketar and braketar >= v0 then
2018-01-07 11:41:48 -08:00
train.atc_brake_target = nil
2018-05-17 02:16:04 -07:00
braketar = nil
2017-01-04 03:02:00 -08:00
end
2018-10-17 09:58:57 -07:00
--if train.tarvelocity and train.velocity==train.tarvelocity then
-- train.tarvelocity = nil
--end
2018-01-07 11:41:48 -08:00
if train.atc_wait_finish then
2018-10-17 09:58:57 -07:00
if not train.atc_brake_target and ( not train.tarvelocity or train.velocity == train.tarvelocity ) then
2018-01-07 11:41:48 -08:00
train.atc_wait_finish = nil
end
end
if train.atc_command then
2018-10-17 09:58:57 -07:00
if ( not train.atc_delay or train.atc_delay <= 0 ) and not train.atc_wait_finish then
2018-01-07 11:41:48 -08:00
advtrains.atc . execute_atc_command ( id , train )
else
train.atc_delay = train.atc_delay - dtime
end
2018-10-10 12:49:52 -07:00
elseif train.atc_delay then
train.atc_delay = nil
2018-01-07 11:41:48 -08:00
end
2018-10-10 12:49:52 -07:00
train.ctrl . atc = nil
2020-04-23 00:44:17 -07:00
if train.tarvelocity and train.tarvelocity > v0 then
v_target_apply ( v_targets , VLEVER_ACCEL , train.tarvelocity )
2018-10-10 12:49:52 -07:00
end
2020-04-23 00:44:17 -07:00
if train.tarvelocity and train.tarvelocity < v0 then
if ( braketar and braketar < v0 ) then
2018-05-17 02:16:04 -07:00
if emerg then
2020-04-23 00:44:17 -07:00
v_target_apply ( v_targets , VLEVER_EMERG , 0 )
2018-05-17 02:16:04 -07:00
else
2020-04-23 00:44:17 -07:00
v_target_apply ( v_targets , VLEVER_EMERG , braketar )
2018-05-17 02:16:04 -07:00
end
2018-01-07 11:41:48 -08:00
else
2020-04-27 07:02:28 -07:00
v_target_apply ( v_targets , VLEVER_ROLL , braketar )
2018-01-07 11:41:48 -08:00
end
2017-01-04 03:02:00 -08:00
end
end
2020-04-23 00:44:17 -07:00
--- 2b. look at v_target, determine the effective v_target and desired acceleration ---
local tv_target , tv_lever
for _ , lever in ipairs ( { VLEVER_EMERG , VLEVER_BRAKE , VLEVER_ROLL , VLEVER_ACCEL } ) do
if v_targets [ lever ] then
tv_target = v_targets [ lever ]
tv_lever = lever
break
end
2018-10-10 12:49:52 -07:00
end
2020-04-23 00:44:17 -07:00
--- 2c. If no tv_lever set, honor the user control ---
local a_lever = tv_lever
if not tv_lever then
a_lever = train.ctrl . user
if not a_lever then
-- default to holding current speed
a_lever = VLEVER_HOLD
end
2016-11-24 11:25:07 -08:00
end
2017-01-27 14:43:01 -08:00
2020-04-23 00:44:17 -07:00
train.lever = a_lever
--- 3a. calculate the acceleration required to reach the speed restriction in path_speed (LZB) ---
-- Iterates over the path nodes we WOULD pass if we were continuing with the speed assumed by actual_lever
-- and determines the MINIMUM of path_speed in this range.
-- Then, determines acceleration so that we can reach this 'overridden' target speed in this step (but short-circuited)
if not a_lever or a_lever > VLEVER_BRAKE then
-- only needs to run if we're not yet braking anyway
local tv_vdiff = advtrains.get_acceleration ( train , tv_lever ) * dtime
local dst_curr_v = ( v0 + tv_vdiff ) * dtime
local nindex_curr_v = advtrains.path_get_index_by_offset ( train , train.index , dst_curr_v )
local i = atfloor ( train.index )
local lzb_target
local psp
while true do
psp = train.path_speed [ i ]
if psp then
lzb_target = lzb_target and math.min ( lzb_target , psp ) or psp
2019-01-23 06:35:58 -08:00
end
2020-04-23 00:44:17 -07:00
if i > nindex_curr_v then
break
2016-05-29 11:27:30 -07:00
end
2020-04-23 00:44:17 -07:00
i = i + 1
2016-05-29 11:27:30 -07:00
end
2019-01-23 06:35:58 -08:00
2020-04-23 00:44:17 -07:00
local dv
if lzb_target and lzb_target <= v0 then
-- apply to tv_target after the actual calculation happened
a_lever = VLEVER_BRAKE
tv_target = tv_target and math.min ( tv_target , lzb_target ) or lzb_target
2018-01-07 11:41:48 -08:00
end
2020-04-23 00:44:17 -07:00
end
--- 3b. now that we know tv_target and a_lever, calculate effective new v and change it on train
local dv = advtrains.get_acceleration ( train , a_lever ) * dtime
local v1
local tv_effective = false
if tv_target and ( math.abs ( dv ) > math.abs ( tv_target - v0 ) ) then
v1 = tv_target
tv_effective = true
2016-09-28 23:41:05 -07:00
else
2020-04-23 00:44:17 -07:00
v1 = v0 + dv
end
if v1 > train.max_speed then
v1 = train.max_speed
end
if v1 < 0 then
v1 = 0
2016-05-29 11:27:30 -07:00
end
2020-04-23 00:44:17 -07:00
train.acceleration = v1 - v0
train.velocity = v1
2017-01-27 14:43:01 -08:00
--- 4. move train ---
2019-01-22 03:09:59 -08:00
2019-12-06 01:41:51 -08:00
local idx_floor = math.floor ( train.index )
local pdist = ( train.path_dist [ idx_floor + 1 ] - train.path_dist [ idx_floor ] )
2019-01-22 12:42:07 -08:00
local distance = ( train.velocity * dtime ) / pdist
2019-01-24 04:43:22 -08:00
--debugging code
2020-04-23 00:44:17 -07:00
--local debutg = advtrains.print_concat_table({"v0=",v0,"v1=",v1,"a_lever",a_lever,"tv_target",tv_target,"tv_eff",tv_effective})
--train.debug = debutg
if advtrains.DFLAG and v1 > 0 then error ( " DFLAG " ) end
2019-01-22 12:42:07 -08:00
train.index = train.index + distance
2018-05-17 12:37:01 -07:00
recalc_end_index ( train )
2018-04-23 06:51:50 -07:00
end
2018-04-25 07:38:12 -07:00
function advtrains . train_step_c ( id , train , dtime )
2019-01-21 14:57:19 -08:00
if train.no_step or train.wait_for_path or not train.path then return end
2017-05-03 07:31:13 -07:00
2018-04-23 06:51:50 -07:00
-- all location/extent-critical actions have been done.
-- calculate the new occupation window
2018-05-17 02:16:04 -07:00
run_callbacks_update ( id , train )
2017-02-05 08:22:13 -08:00
2019-01-21 14:57:19 -08:00
-- Return if something(TM) damaged the path
2019-01-22 03:11:14 -08:00
if train.no_step or train.wait_for_path or not train.path then return end
2019-01-21 14:57:19 -08:00
2018-04-23 06:51:50 -07:00
advtrains.path_clear_unused ( train )
2017-02-05 08:22:13 -08:00
2018-05-17 02:16:04 -07:00
advtrains.path_setrestore ( train )
2017-01-27 14:43:01 -08:00
2018-04-23 06:51:50 -07:00
-- less important stuff
2017-01-27 14:43:01 -08:00
train.check_trainpartload = ( train.check_trainpartload or 0 ) - dtime
if train.check_trainpartload <= 0 then
2018-04-23 06:51:50 -07:00
advtrains.spawn_wagons ( id )
2017-01-27 14:43:01 -08:00
train.check_trainpartload = 2
end
2017-10-25 01:31:07 -07:00
2017-02-08 15:11:28 -08:00
--- 8. check for collisions with other trains and damage players ---
2017-01-27 14:43:01 -08:00
local train_moves = ( train.velocity ~= 0 )
2018-05-17 03:30:30 -07:00
--- Check whether this train can be coupled to another, and set couple entities accordingly
if not train.was_standing and not train_moves then
advtrains.train_check_couples ( train )
end
train.was_standing = not train_moves
2017-01-27 14:43:01 -08:00
if train_moves then
2017-11-23 08:00:39 -08:00
2018-05-17 12:37:01 -07:00
local collided = false
2017-01-27 14:43:01 -08:00
local coll_grace = 1
2018-05-17 12:37:01 -07:00
local collindex = advtrains.path_get_index_by_offset ( train , train.index , - coll_grace )
local collpos = advtrains.path_get ( train , atround ( collindex ) )
2017-01-27 14:43:01 -08:00
if collpos then
local rcollpos = advtrains.round_vector_floor_y ( collpos )
2021-02-03 00:30:44 -08:00
local is_loaded_area = advtrains.is_node_loaded ( rcollpos )
2017-11-23 08:00:39 -08:00
for x =- train.extent_h , train.extent_h do
for z =- train.extent_h , train.extent_h do
2017-01-27 14:43:01 -08:00
local testpos = vector.add ( rcollpos , { x = x , y = 0 , z = z } )
2017-02-08 15:11:28 -08:00
--- 8a Check collision ---
2019-08-11 11:16:11 -07:00
if not collided then
local col_tr = advtrains.occ . check_collision ( testpos , id )
if col_tr then
2020-06-07 15:25:29 -07:00
advtrains.train_check_couples ( train )
train.velocity = 0
advtrains.atc . train_reset_command ( train )
collided = true
2019-08-11 11:16:11 -07:00
end
2020-06-07 15:25:29 -07:00
--- 8b damage players ---
if is_loaded_area and train.velocity > 3 and ( setting_overrun_mode == " drop " or setting_overrun_mode == " normal " ) then
local testpts = minetest.pos_to_string ( testpos )
local player = advtrains.playersbypts [ testpts ]
if player and player : get_hp ( ) > 0 and advtrains.is_damage_enabled ( player : get_player_name ( ) ) then
--atdebug("damage found",player:get_player_name())
if setting_overrun_mode == " drop " then
--instantly kill player
--drop inventory contents first, to not to spawn bones
local player_inv = player : get_inventory ( )
for i = 1 , player_inv : get_size ( " main " ) do
minetest.add_item ( testpos , player_inv : get_stack ( " main " , i ) )
end
for i = 1 , player_inv : get_size ( " craft " ) do
minetest.add_item ( testpos , player_inv : get_stack ( " craft " , i ) )
end
-- empty lists main and craft
player_inv : set_list ( " main " , { } )
player_inv : set_list ( " craft " , { } )
2019-03-09 02:29:22 -08:00
end
2020-06-07 15:25:29 -07:00
player : set_hp ( 0 )
2017-03-11 13:19:01 -08:00
end
2017-02-08 15:11:28 -08:00
end
end
2017-01-27 14:43:01 -08:00
end
end
2017-10-25 05:04:20 -07:00
--- 8c damage other objects ---
2019-06-05 06:49:53 -07:00
if is_loaded_area then
local objs = minetest.get_objects_inside_radius ( rcollpos , 2 )
for _ , obj in ipairs ( objs ) do
if not obj : is_player ( ) and obj : get_armor_groups ( ) . fleshy and obj : get_armor_groups ( ) . fleshy > 0
and obj : get_luaentity ( ) and obj : get_luaentity ( ) . name ~= " signs:text " then
obj : punch ( obj , 1 , { full_punch_interval = 1.0 , damage_groups = { fleshy = 1000 } , } , nil )
end
2017-10-25 05:04:20 -07:00
end
end
2017-01-27 14:43:01 -08:00
end
end
2016-06-02 05:42:01 -07:00
end
2016-05-29 11:27:30 -07:00
2018-05-17 02:16:04 -07:00
-- Default occupation callbacks for node callbacks
-- (remember, train.end_index is set separately because callbacks are
-- asserted to rely on this)
2018-06-29 07:16:55 -07:00
local function mknodecallback ( name )
local callt = { }
2019-02-19 06:49:01 -08:00
advtrains [ " tnc_register_on_ " .. name ] = function ( func , prio )
2018-06-29 07:16:55 -07:00
assertt ( func , " function " )
2019-02-19 06:49:01 -08:00
if prio then
table.insert ( callt , 1 , func )
else
table.insert ( callt , func )
end
2018-06-29 07:16:55 -07:00
end
2019-04-16 00:16:44 -07:00
return callt , function ( pos , id , train , index , paramx1 , paramx2 , paramx3 )
2018-06-29 07:16:55 -07:00
for _ , f in ipairs ( callt ) do
2019-04-16 00:16:44 -07:00
f ( pos , id , train , index , paramx1 , paramx2 , paramx3 )
2018-06-29 07:16:55 -07:00
end
end
end
-- enter/leave-node callbacks
-- signature is advtrains.tnc_register_on_enter/leave(function(pos, id, train, index) ... end)
local callbacks_enter_node , run_callbacks_enter_node = mknodecallback ( " enter " )
local callbacks_leave_node , run_callbacks_leave_node = mknodecallback ( " leave " )
2019-04-16 00:16:44 -07:00
-- Node callback for approaching
-- Might be called multiple times, whenever path is recalculated
-- signature is function(pos, id, train, index, lzbdata)
-- lzbdata: arbitrary data (shared between all callbacks), deleted when LZB is restarted.
-- These callbacks are called in order of distance as train progresses along tracks, so lzbdata can be used to
-- keep track of a train's state once it passes this point
local callbacks_approach_node , run_callbacks_approach_node = mknodecallback ( " approach " )
2018-06-29 07:16:55 -07:00
local function tnc_call_enter_callback ( pos , train_id , train , index )
2018-05-29 03:27:02 -07:00
--atdebug("tnc enter",pos,train_id)
2018-05-17 02:16:04 -07:00
local node = advtrains.ndb . get_node ( pos ) --this spares the check if node is nil, it has a name in any case
local mregnode = minetest.registered_nodes [ node.name ]
if mregnode and mregnode.advtrains and mregnode.advtrains . on_train_enter then
2018-06-29 07:16:55 -07:00
mregnode.advtrains . on_train_enter ( pos , train_id , train , index )
2018-05-17 02:16:04 -07:00
end
2018-06-29 07:16:55 -07:00
-- call other registered callbacks
run_callbacks_enter_node ( pos , train_id , train , index )
2020-06-10 11:56:35 -07:00
-- check for split points
if mregnode and mregnode.at_conns and # mregnode.at_conns == 3 and train.path_cp [ index ] == 3 then
-- train came from connection 3 of a switch, so it split points.
if not train.points_split then
train.points_split = { }
end
train.points_split [ advtrains.encode_pos ( pos ) ] = true
--atdebug(train_id,"split points at",pos)
end
2018-05-17 02:16:04 -07:00
end
2018-06-29 07:16:55 -07:00
local function tnc_call_leave_callback ( pos , train_id , train , index )
2018-05-29 03:27:02 -07:00
--atdebug("tnc leave",pos,train_id)
2018-05-17 02:16:04 -07:00
local node = advtrains.ndb . get_node ( pos ) --this spares the check if node is nil, it has a name in any case
local mregnode = minetest.registered_nodes [ node.name ]
if mregnode and mregnode.advtrains and mregnode.advtrains . on_train_leave then
2018-06-29 07:16:55 -07:00
mregnode.advtrains . on_train_leave ( pos , train_id , train , index )
end
-- call other registered callbacks
run_callbacks_leave_node ( pos , train_id , train , index )
2020-06-10 11:56:35 -07:00
-- split points do not matter anymore. clear them
if train.points_split then
if train.points_split [ advtrains.encode_pos ( pos ) ] then
train.points_split [ advtrains.encode_pos ( pos ) ] = nil
--atdebug(train_id,"has passed split points at",pos)
end
-- any entries left?
for _ , _ in pairs ( train.points_split ) do
return
end
train.points_split = nil
end
-- WARNING possibly unreachable place!
2018-05-17 02:16:04 -07:00
end
2019-04-16 00:16:44 -07:00
function advtrains . tnc_call_approach_callback ( pos , train_id , train , index , lzbdata )
--atdebug("tnc approach",pos,train_id, lzbdata)
local node = advtrains.ndb . get_node ( pos ) --this spares the check if node is nil, it has a name in any case
local mregnode = minetest.registered_nodes [ node.name ]
if mregnode and mregnode.advtrains and mregnode.advtrains . on_train_approach then
mregnode.advtrains . on_train_approach ( pos , train_id , train , index , lzbdata )
end
-- call other registered callbacks
run_callbacks_approach_node ( pos , train_id , train , index , lzbdata )
end
2018-06-29 07:16:55 -07:00
2018-05-17 02:16:04 -07:00
advtrains.te_register_on_new_path ( function ( id , train )
train.tnc = {
2018-05-17 03:30:30 -07:00
old_index = atround ( train.index ) ,
old_end_index = atround ( train.end_index ) ,
2018-05-17 02:16:04 -07:00
}
2018-05-29 03:27:02 -07:00
--atdebug(id,"tnc init",train.index,train.end_index)
2018-05-17 02:16:04 -07:00
end )
2018-05-17 12:37:01 -07:00
2018-05-17 02:16:04 -07:00
advtrains.te_register_on_update ( function ( id , train )
local new_index = atround ( train.index )
local new_end_index = atround ( train.end_index )
local old_index = train.tnc . old_index
local old_end_index = train.tnc . old_end_index
while old_index < new_index do
old_index = old_index + 1
2018-05-17 12:37:01 -07:00
local pos = advtrains.round_vector_floor_y ( advtrains.path_get ( train , old_index ) )
2018-06-29 07:16:55 -07:00
tnc_call_enter_callback ( pos , id , train , old_index )
2018-05-17 02:16:04 -07:00
end
2018-05-17 12:37:01 -07:00
while old_end_index < new_end_index do
local pos = advtrains.round_vector_floor_y ( advtrains.path_get ( train , old_end_index ) )
2018-06-29 07:16:55 -07:00
tnc_call_leave_callback ( pos , id , train , old_end_index )
2018-05-17 12:37:01 -07:00
old_end_index = old_end_index + 1
2018-05-17 02:16:04 -07:00
end
2018-05-17 12:37:01 -07:00
train.tnc . old_index = new_index
train.tnc . old_end_index = new_end_index
end )
advtrains.te_register_on_create ( function ( id , train )
local index = atround ( train.index )
local end_index = atround ( train.end_index )
while end_index <= index do
local pos = advtrains.round_vector_floor_y ( advtrains.path_get ( train , end_index ) )
2018-06-29 07:16:55 -07:00
tnc_call_enter_callback ( pos , id , train , end_index )
2018-05-17 12:37:01 -07:00
end_index = end_index + 1
end
2018-05-29 03:27:02 -07:00
--atdebug(id,"tnc create",train.index,train.end_index)
2018-05-17 12:37:01 -07:00
end )
advtrains.te_register_on_remove ( function ( id , train )
local index = atround ( train.index )
local end_index = atround ( train.end_index )
while end_index <= index do
local pos = advtrains.round_vector_floor_y ( advtrains.path_get ( train , end_index ) )
2018-06-29 07:16:55 -07:00
tnc_call_leave_callback ( pos , id , train , end_index )
2018-05-17 12:37:01 -07:00
end_index = end_index + 1
end
2018-05-29 03:27:02 -07:00
--atdebug(id,"tnc remove",train.index,train.end_index)
2018-05-17 02:16:04 -07:00
end )
2017-01-27 14:43:01 -08:00
--returns new id
2018-04-23 06:51:50 -07:00
function advtrains . create_new_train_at ( pos , connid , ioff , trainparts )
local new_id = advtrains.random_id ( )
while advtrains.trains [ new_id ] do new_id = advtrains.random_id ( ) end --ensure uniqueness
2017-01-27 14:43:01 -08:00
2018-04-25 07:38:12 -07:00
local t = { }
t.id = new_id
2017-02-04 12:07:18 -08:00
2018-04-23 06:51:50 -07:00
t.last_pos = pos
t.last_connid = connid
t.last_frac = ioff
2018-10-10 12:49:52 -07:00
--t.tarvelocity=0
2018-04-23 06:51:50 -07:00
t.velocity = 0
t.trainparts = trainparts
advtrains.trains [ new_id ] = t
2018-05-29 03:27:02 -07:00
--atdebug("Created new train:",t)
2018-04-23 06:51:50 -07:00
2018-10-29 13:19:49 -07:00
if not advtrains.train_ensure_init ( new_id , advtrains.trains [ new_id ] ) then
atwarn ( " create_new_train_at " , pos , connid , " failed! This might lead to temporary bugs. " )
return
end
2017-02-04 12:07:18 -08:00
2018-05-17 12:37:01 -07:00
run_callbacks_create ( new_id , advtrains.trains [ new_id ] )
2018-04-25 07:38:12 -07:00
return new_id
2017-01-27 14:43:01 -08:00
end
2018-04-23 06:51:50 -07:00
function advtrains . remove_train ( id )
local train = advtrains.trains [ id ]
2018-10-29 13:19:49 -07:00
if not advtrains.train_ensure_init ( id , train ) then
atwarn ( " remove_train " , id , " failed! This might lead to temporary bugs. " )
return
end
2018-04-25 07:38:12 -07:00
2018-05-17 12:37:01 -07:00
run_callbacks_remove ( id , train )
2018-05-17 02:16:04 -07:00
advtrains.path_invalidate ( train )
2018-05-17 12:37:01 -07:00
advtrains.couple_invalidate ( train )
2018-04-23 06:51:50 -07:00
local tp = train.trainparts
2018-05-29 03:27:02 -07:00
--atdebug("Removing train",id,"leftover trainparts:",tp)
2018-04-23 06:51:50 -07:00
advtrains.trains [ id ] = nil
return tp
2016-05-29 11:27:30 -07:00
end
2018-04-23 06:51:50 -07:00
2018-04-19 02:38:00 -07:00
function advtrains . add_wagon_to_train ( wagon_id , train_id , index )
2016-05-29 11:27:30 -07:00
local train = advtrains.trains [ train_id ]
2018-04-23 06:51:50 -07:00
2018-10-29 13:19:49 -07:00
if not advtrains.train_ensure_init ( train_id , train ) then
atwarn ( " Train " , train_id , " is not initialized! Operation aborted! " )
return
end
2018-04-23 06:51:50 -07:00
2016-05-29 11:27:30 -07:00
if index then
2018-04-19 02:38:00 -07:00
table.insert ( train.trainparts , index , wagon_id )
2016-05-29 11:27:30 -07:00
else
2018-04-19 02:38:00 -07:00
table.insert ( train.trainparts , wagon_id )
2016-05-29 11:27:30 -07:00
end
2018-04-23 06:51:50 -07:00
2016-05-29 11:27:30 -07:00
advtrains.update_trainpart_properties ( train_id )
2018-05-17 02:16:04 -07:00
recalc_end_index ( train )
run_callbacks_update ( train_id , train )
2016-05-29 11:27:30 -07:00
end
2018-04-19 02:38:00 -07:00
2018-11-10 03:12:52 -08:00
-- Note: safe_decouple_wagon() has been moved to wagons.lua
2018-05-29 03:27:02 -07:00
2018-04-19 02:38:00 -07:00
-- this function sets wagon's pos_in_train(parts) properties and train's max_speed and drives_on (and more)
2016-05-29 11:27:30 -07:00
function advtrains . update_trainpart_properties ( train_id , invert_flipstate )
local train = advtrains.trains [ train_id ]
2017-11-22 14:13:42 -08:00
train.drives_on = advtrains.merge_tables ( advtrains.all_tracktypes )
--FIX: deep-copy the table!!!
2017-01-04 12:23:15 -08:00
train.max_speed = 20
2017-11-23 08:00:39 -08:00
train.extent_h = 0 ;
2016-05-29 11:27:30 -07:00
local rel_pos = 0
local count_l = 0
2018-04-19 02:38:00 -07:00
local shift_dcpl_lock = false
2016-05-29 11:27:30 -07:00
for i , w_id in ipairs ( train.trainparts ) do
2018-04-19 02:38:00 -07:00
local data = advtrains.wagons [ w_id ]
-- 1st: update wagon data (pos_in_train a.s.o)
if data then
2018-07-17 12:15:34 -07:00
local wagon = advtrains.wagon_prototypes [ data.type or data.entity_name ]
2018-04-19 02:38:00 -07:00
if not wagon then
atwarn ( " Wagon ' " , data.type , " ' couldn't be found. Please check that all required modules are loaded! " )
2018-04-25 07:38:12 -07:00
wagon = advtrains.wagon_prototypes [ " advtrains:wagon_placeholder " ]
2018-07-17 12:15:34 -07:00
2017-01-02 09:40:49 -08:00
end
rel_pos = rel_pos + wagon.wagon_span
2018-04-19 02:38:00 -07:00
data.train_id = train_id
data.pos_in_train = rel_pos
data.pos_in_trainparts = i
2017-01-02 09:40:49 -08:00
if wagon.is_locomotive then
count_l = count_l + 1
end
if invert_flipstate then
2018-04-19 02:38:00 -07:00
data.wagon_flipped = not data.wagon_flipped
shift_dcpl_lock , data.dcpl_lock = data.dcpl_lock , shift_dcpl_lock
2017-01-02 09:40:49 -08:00
end
rel_pos = rel_pos + wagon.wagon_span
if wagon.drives_on then
for k , _ in pairs ( train.drives_on ) do
if not wagon.drives_on [ k ] then
train.drives_on [ k ] = nil
2016-12-22 09:55:10 -08:00
end
end
2016-05-29 11:27:30 -07:00
end
2017-01-02 09:40:49 -08:00
train.max_speed = math.min ( train.max_speed , wagon.max_speed )
2017-11-23 08:00:39 -08:00
train.extent_h = math.max ( train.extent_h , wagon.extent_h or 1 ) ;
2016-05-30 10:58:09 -07:00
end
2016-05-29 11:27:30 -07:00
end
2018-04-25 07:38:12 -07:00
train.trainlen = rel_pos
train.locomotives_in_train = count_l
2016-05-29 11:27:30 -07:00
end
2018-05-17 12:37:01 -07:00
2020-07-12 11:37:51 -07:00
local ablkrng = advtrains.wagon_load_range
2018-04-19 02:38:00 -07:00
-- This function checks whether entities need to be spawned for certain wagons, and spawns them.
2018-10-29 13:19:49 -07:00
-- Called from train_step_*(), not required to check init.
2018-04-19 02:38:00 -07:00
function advtrains . spawn_wagons ( train_id )
2018-04-23 06:51:50 -07:00
local train = advtrains.trains [ train_id ]
2018-04-19 02:38:00 -07:00
2018-05-29 03:27:02 -07:00
for i = 1 , # train.trainparts do
local w_id = train.trainparts [ i ]
2018-04-19 02:38:00 -07:00
local data = advtrains.wagons [ w_id ]
if data then
2018-05-17 12:37:01 -07:00
if data.train_id ~= train_id then
2020-07-12 11:37:51 -07:00
atwarn ( " Train " , train_id , " Wagon # " , i , " : Saved train ID " , data.train_id , " did not match! " )
2018-05-17 12:37:01 -07:00
data.train_id = train_id
end
2018-05-29 03:27:02 -07:00
if not advtrains.wagon_objects [ w_id ] or not advtrains.wagon_objects [ w_id ] : getyaw ( ) then
2018-04-19 02:38:00 -07:00
-- eventually need to spawn new object. check if position is loaded.
local index = advtrains.path_get_index_by_offset ( train , train.index , - data.pos_in_train )
local pos = advtrains.path_get ( train , atfloor ( index ) )
2021-02-03 00:30:44 -08:00
if advtrains.position_in_range ( pos , ablkrng ) then
2020-07-12 11:37:51 -07:00
--atdebug("wagon",w_id,"spawning")
2018-04-23 06:51:50 -07:00
local wt = advtrains.get_wagon_prototype ( data )
2018-04-25 07:38:12 -07:00
local wagon = minetest.add_entity ( pos , wt ) : get_luaentity ( )
2018-04-19 02:38:00 -07:00
wagon : set_id ( w_id )
end
end
2018-05-29 03:27:02 -07:00
else
atwarn ( " Train " , train_id , " Wagon # " , 1 , " : A wagon with id " , w_id , " does not exist! Wagon will be removed from train. " )
table.remove ( train.trainparts , i )
i = i - 1
2018-04-19 02:38:00 -07:00
end
end
end
2020-08-21 06:58:12 -07:00
function advtrains . split_train_at_fc ( train , count_empty , length_limit )
2020-07-28 11:39:35 -07:00
-- splits train at first different current FC by convention,
-- locomotives have empty FC so are ignored
-- count_empty is used to split off locomotives
2020-08-21 06:58:12 -07:00
-- length_limit limits the length of the first train to length_limit wagons
2020-07-26 12:07:47 -07:00
local train_id = train.id
local fc = false
local ind = 0
for i = 1 , # train.trainparts do
local w_id = train.trainparts [ i ]
local data = advtrains.wagons [ w_id ]
2020-08-21 06:58:12 -07:00
if length_limit and i > length_limit then
ind = i
break
end
2020-07-26 12:07:47 -07:00
if data then
local wfc = advtrains.get_cur_fc ( data )
2020-07-28 11:39:35 -07:00
if wfc ~= " " or count_empty then
2020-07-26 12:07:47 -07:00
if fc then
if fc ~= wfc then
ind = i
break
end
else
fc = wfc
end
end
end
end
if ind > 0 then
return advtrains.split_train_at_index ( train , ind ) , fc
end
if fc then
return nil , fc
end
end
2020-07-26 14:28:24 -07:00
function advtrains . train_step_fc ( train )
for i = 1 , # train.trainparts do
local w_id = train.trainparts [ i ]
local data = advtrains.wagons [ w_id ]
if data then
advtrains.step_fc ( data )
end
end
end
2019-08-11 08:42:30 -07:00
function advtrains . split_train_at_index ( train , index )
-- this function splits a train at index, creating a new train from the back part of the train.
local train_id = train.id
if index > # train.trainparts then
-- index specified too long
return
end
local w_id = train.trainparts [ index ]
local data = advtrains.wagons [ w_id ]
2018-04-23 06:51:50 -07:00
local _ , wagon = advtrains.get_wagon_prototype ( data )
2019-08-11 08:42:30 -07:00
if not advtrains.train_ensure_init ( train_id , train ) then
atwarn ( " Train " , train_id , " is not initialized! Operation aborted! " )
2018-10-29 13:19:49 -07:00
return
end
2020-06-28 16:30:27 -07:00
2019-08-11 08:42:30 -07:00
local p_index = advtrains.path_get_index_by_offset ( train , train.index , - data.pos_in_train + wagon.wagon_span )
local pos , connid , frac = advtrains.path_getrestore ( train , p_index )
2018-04-23 06:51:50 -07:00
local tp = { }
2016-05-29 11:27:30 -07:00
for k , v in ipairs ( train.trainparts ) do
2019-08-11 08:42:30 -07:00
if k >= index then
2018-04-23 06:51:50 -07:00
table.insert ( tp , v )
2019-08-11 08:42:30 -07:00
train.trainparts [ k ] = nil
2016-05-29 11:27:30 -07:00
end
end
2019-08-11 08:42:30 -07:00
advtrains.update_trainpart_properties ( train_id )
2018-05-29 03:27:02 -07:00
recalc_end_index ( train )
2019-08-11 08:42:30 -07:00
run_callbacks_update ( train_id , train )
2018-05-29 03:27:02 -07:00
2018-04-23 06:51:50 -07:00
--create subtrain
2018-05-17 02:16:04 -07:00
local newtrain_id = advtrains.create_new_train_at ( pos , connid , frac , tp )
2018-04-23 06:51:50 -07:00
local newtrain = advtrains.trains [ newtrain_id ]
2016-05-29 11:27:30 -07:00
newtrain.velocity = train.velocity
2019-08-09 10:11:26 -07:00
return newtrain_id -- return new train ID, so new train can be manipulated
2019-08-11 08:42:30 -07:00
end
function advtrains . split_train_at_wagon ( wagon_id )
--get train
local data = advtrains.wagons [ wagon_id ]
advtrains.split_train_at_index ( advtrains.trains [ data.train_id ] , data.pos_in_trainparts )
2016-05-29 11:27:30 -07:00
end
2018-05-17 03:30:30 -07:00
-- coupling
2018-05-17 12:37:01 -07:00
local CPL_CHK_DST = - 1
2018-05-17 03:30:30 -07:00
local CPL_ZONE = 2
2016-12-31 06:16:19 -08:00
2018-05-17 03:30:30 -07:00
-- train.couple_* contain references to ObjectRefs of couple objects, which contain all relevant information
-- These objectRefs will delete themselves once the couples no longer match
local function createcouple ( pos , train1 , t1_is_front , train2 , t2_is_front )
2020-06-07 15:25:29 -07:00
local id1 = train1.id
local id2 = train2.id
2020-06-28 16:30:27 -07:00
if train1.autocouple or train2.autocouple then
2020-06-07 15:25:29 -07:00
-- couple trains
2020-06-28 16:30:27 -07:00
train1.autocouple = nil
train2.autocouple = nil
2020-06-07 15:25:29 -07:00
minetest.after ( 0 , advtrains.safe_couple_trains , id1 , id2 , t1_is_front , t2_is_front , false , false , train1.velocity , train2.velocity )
return
end
2018-05-17 03:30:30 -07:00
local obj = minetest.add_entity ( pos , " advtrains:couple " )
if not obj then error ( " Failed creating couple object! " ) return end
local le = obj : get_luaentity ( )
2020-06-07 15:25:29 -07:00
le.train_id_1 = id1
le.train_id_2 = id2
2018-05-17 12:37:01 -07:00
le.t1_is_front = t1_is_front
le.t2_is_front = t2_is_front
2018-05-29 03:27:02 -07:00
--atdebug("created couple between",train1.id,t1_is_front,train2.id,t2_is_front)
2018-05-17 12:37:01 -07:00
if t1_is_front then
train1.cpl_front = obj
else
train1.cpl_back = obj
end
if t2_is_front then
train2.cpl_front = obj
else
train2.cpl_back = obj
end
2016-07-04 03:11:51 -07:00
end
2018-05-17 03:30:30 -07:00
function advtrains . train_check_couples ( train )
2018-05-29 03:27:02 -07:00
--atdebug("rechecking couples")
2018-05-17 03:30:30 -07:00
if train.cpl_front then
if not train.cpl_front : getyaw ( ) then
-- objectref is no longer valid. reset.
train.cpl_front = nil
2016-12-31 06:16:19 -08:00
end
end
2018-05-17 03:30:30 -07:00
if not train.cpl_front then
-- recheck front couple
local front_trains , pos = advtrains.occ . get_occupations ( train , atround ( train.index ) + CPL_CHK_DST )
2021-02-03 00:30:44 -08:00
if advtrains.is_node_loaded ( pos ) then -- if the position is loaded...
2019-11-26 07:43:47 -08:00
for tid , idx in pairs ( front_trains ) do
local other_train = advtrains.trains [ tid ]
if not advtrains.train_ensure_init ( tid , other_train ) then
atwarn ( " Train " , tid , " is not initialized! Couldn't check couples! " )
return
2018-05-17 03:30:30 -07:00
end
2019-11-26 07:43:47 -08:00
--atdebug(train.id,"front: ",idx,"on",tid,atround(other_train.index),atround(other_train.end_index))
if other_train.velocity == 0 then
if idx >= other_train.index and idx <= other_train.index + CPL_ZONE then
createcouple ( pos , train , true , other_train , true )
break
end
if idx <= other_train.end_index and idx >= other_train.end_index - CPL_ZONE then
createcouple ( pos , train , true , other_train , false )
break
end
2018-05-17 03:30:30 -07:00
end
end
end
2016-12-31 06:16:19 -08:00
end
2018-05-17 03:30:30 -07:00
if train.cpl_back then
if not train.cpl_back : getyaw ( ) then
-- objectref is no longer valid. reset.
train.cpl_back = nil
end
2018-01-09 12:30:56 -08:00
end
2018-05-17 03:30:30 -07:00
if not train.cpl_back then
-- recheck back couple
local back_trains , pos = advtrains.occ . get_occupations ( train , atround ( train.end_index ) - CPL_CHK_DST )
2021-02-03 00:30:44 -08:00
if advtrains.is_node_loaded ( pos ) then -- if the position is loaded...
2019-11-26 07:43:47 -08:00
for tid , idx in pairs ( back_trains ) do
local other_train = advtrains.trains [ tid ]
if not advtrains.train_ensure_init ( tid , other_train ) then
atwarn ( " Train " , tid , " is not initialized! Couldn't check couples! " )
return
2018-05-17 03:30:30 -07:00
end
2019-11-26 07:43:47 -08:00
if other_train.velocity == 0 then
if idx >= other_train.index and idx <= other_train.index + CPL_ZONE then
createcouple ( pos , train , false , other_train , true )
break
end
if idx <= other_train.end_index and idx >= other_train.end_index - CPL_ZONE then
createcouple ( pos , train , false , other_train , false )
break
end
2018-05-17 03:30:30 -07:00
end
2016-05-31 14:13:08 -07:00
end
end
2016-05-29 11:27:30 -07:00
end
end
2016-12-31 06:16:19 -08:00
2018-05-17 12:37:01 -07:00
function advtrains . couple_invalidate ( train )
if train.cpl_back then
train.cpl_back : remove ( )
train.cpl_back = nil
end
if train.cpl_front then
train.cpl_front : remove ( )
train.cpl_front = nil
end
train.was_standing = nil
end
2018-05-17 03:30:30 -07:00
-- relevant code for this comment is in couple.lua
--there are 4 cases:
--1/2. F<->R F<->R regular, put second train behind first
--->frontpos of first train will match backpos of second
--3. F<->R R<->F flip one of these trains, take the other as new train
--->backpos's will match
--4. R<->F F<->R flip one of these trains and take it as new parent
--->frontpos's will match
2019-08-11 11:16:11 -07:00
function advtrains . do_connect_trains ( first_id , second_id , vel )
2017-01-27 14:43:01 -08:00
local first , second = advtrains.trains [ first_id ] , advtrains.trains [ second_id ]
2018-10-29 13:19:49 -07:00
if not advtrains.train_ensure_init ( first_id , first ) then
atwarn ( " Train " , first_id , " is not initialized! Operation aborted! " )
return
end
if not advtrains.train_ensure_init ( second_id , second ) then
atwarn ( " Train " , second_id , " is not initialized! Operation aborted! " )
return
end
2017-04-29 10:44:43 -07:00
2017-01-27 14:43:01 -08:00
local first_wagoncnt =# first.trainparts
local second_wagoncnt =# second.trainparts
2016-05-29 11:27:30 -07:00
2017-01-27 14:43:01 -08:00
for _ , v in ipairs ( second.trainparts ) do
table.insert ( first.trainparts , v )
2016-05-29 11:27:30 -07:00
end
2018-05-17 03:30:30 -07:00
advtrains.remove_train ( second_id )
2020-07-26 12:07:47 -07:00
if vel < 0 then
advtrains.invert_train ( first_id )
vel = - vel
end
2019-08-11 11:16:11 -07:00
first.velocity = vel or 0
2018-05-17 12:37:01 -07:00
advtrains.update_trainpart_properties ( first_id )
advtrains.couple_invalidate ( first )
2017-04-29 10:44:43 -07:00
return true
2016-05-29 11:27:30 -07:00
end
function advtrains . invert_train ( train_id )
local train = advtrains.trains [ train_id ]
2018-10-29 13:19:49 -07:00
if not advtrains.train_ensure_init ( train_id , train ) then
atwarn ( " Train " , train_id , " is not initialized! Operation aborted! " )
return
end
2018-05-17 02:16:04 -07:00
advtrains.path_setrestore ( train , true )
2018-04-26 14:35:19 -07:00
-- rotate some other stuff
2018-05-17 12:37:01 -07:00
if train.door_open then
train.door_open = - train.door_open
end
2018-08-12 07:36:20 -07:00
if train.atc_command then
train.atc_arrow = not train.atc_arrow
end
2018-04-26 14:35:19 -07:00
2019-01-22 03:26:31 -08:00
advtrains.path_invalidate ( train , true )
2018-05-17 12:37:01 -07:00
advtrains.couple_invalidate ( train )
2018-04-26 14:35:19 -07:00
2016-05-29 11:27:30 -07:00
local old_trainparts = train.trainparts
train.trainparts = { }
for k , v in ipairs ( old_trainparts ) do
table.insert ( train.trainparts , 1 , v ) --notice insertion at first place
end
advtrains.update_trainpart_properties ( train_id , true )
2018-10-10 14:17:31 -07:00
2019-01-22 03:26:31 -08:00
-- recalculate path
advtrains.train_ensure_init ( train_id , train )
2018-12-08 08:12:57 -08:00
-- If interlocking present, check whether this train is in a section and then set as shunt move after reversion
if advtrains.interlocking and train.il_sections and # train.il_sections > 0 then
train.is_shunt = true
train.speed_restriction = advtrains.SHUNT_SPEED_MAX
else
train.is_shunt = false
train.speed_restriction = nil
end
2016-05-29 11:27:30 -07:00
end
2018-05-29 03:27:02 -07:00
-- returns: train id, index of one of the trains that stand at this position.
2017-02-01 15:02:11 -08:00
function advtrains . get_train_at_pos ( pos )
2018-05-29 03:27:02 -07:00
local t = advtrains.occ . get_trains_at ( pos )
for tid , idx in pairs ( t ) do
return tid , idx
end
2016-05-29 11:27:30 -07:00
end
2017-02-01 15:02:11 -08:00
2018-10-10 13:41:59 -07:00
-- ehm... I never adapted this function to the new path system ?!
2017-05-03 07:31:13 -07:00
function advtrains . invalidate_all_paths ( pos )
2018-10-10 13:41:59 -07:00
local tab
if pos then
-- if position given, check occupation system
tab = advtrains.occ . get_trains_over ( pos )
else
tab = advtrains.trains
end
for id , _ in pairs ( tab ) do
advtrains.invalidate_path ( id )
2016-05-29 11:27:30 -07:00
end
2016-07-04 03:11:51 -07:00
end
2018-01-07 10:00:43 -08:00
function advtrains . invalidate_path ( id )
2018-10-17 08:52:22 -07:00
--atdebug("Path invalidate:",id)
2018-01-07 10:00:43 -08:00
local v = advtrains.trains [ id ]
if not v then return end
2018-04-25 07:38:12 -07:00
advtrains.path_invalidate ( v )
2018-05-17 12:37:01 -07:00
advtrains.couple_invalidate ( v )
2018-04-25 07:38:12 -07:00
v.dirty = true
2018-01-07 10:00:43 -08:00
end
2016-08-28 11:59:54 -07:00
--not blocking trains group
function advtrains . train_collides ( node )
if node and minetest.registered_nodes [ node.name ] and minetest.registered_nodes [ node.name ] . walkable then
if not minetest.registered_nodes [ node.name ] . groups.not_blocking_trains then
return true
end
end
return false
end
local nonblocknodes = {
" default:fence_wood " ,
2016-11-06 14:21:03 -08:00
" default:fence_acacia_wood " ,
" default:fence_aspen_wood " ,
" default:fence_pine_wood " ,
" default:fence_junglewood " ,
2016-08-28 11:59:54 -07:00
" default:torch " ,
2018-10-17 08:52:22 -07:00
" bones:bones " ,
2016-09-15 02:50:37 -07:00
2016-08-28 11:59:54 -07:00
" default:sign_wall " ,
2016-09-15 02:50:37 -07:00
" signs:sign_wall " ,
" signs:sign_wall_blue " ,
" signs:sign_wall_brown " ,
" signs:sign_wall_orange " ,
" signs:sign_wall_green " ,
" signs:sign_yard " ,
" signs:sign_wall_white_black " ,
" signs:sign_wall_red " ,
" signs:sign_wall_white_red " ,
" signs:sign_wall_yellow " ,
2016-08-28 11:59:54 -07:00
" signs:sign_post " ,
2016-09-15 02:50:37 -07:00
" signs:sign_hanging " ,
2016-08-28 11:59:54 -07:00
}
minetest.after ( 0 , function ( )
for _ , name in ipairs ( nonblocknodes ) do
if minetest.registered_nodes [ name ] then
minetest.registered_nodes [ name ] . groups.not_blocking_trains = 1
end
end
end )