aboutsummaryrefslogtreecommitdiff
path: root/advtrains/trainlogic.lua
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains/trainlogic.lua')
-rw-r--r--advtrains/trainlogic.lua479
1 files changed, 201 insertions, 278 deletions
diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua
index 187e5ba..f136577 100644
--- a/advtrains/trainlogic.lua
+++ b/advtrains/trainlogic.lua
@@ -139,11 +139,15 @@ minetest.register_on_joinplayer(function(player)
advtrains.hhud[player:get_player_name()] = nil
--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()
+ local pname = player:get_player_name()
local id=advtrains.player_to_train_mapping[pname]
if id then
for _,wagon in pairs(minetest.luaentities) do
- if wagon.is_wagon and wagon.initialized and wagon.train_id==id then
- wagon:reattach_all()
+ if wagon.is_wagon and wagon.initialized and wagon.id then
+ local wdata = advtrains.wagons[wagon.id]
+ if wdata and wdata.train_id == id then
+ wagon:reattach_all()
+ end
end
end
end
@@ -250,6 +254,11 @@ local callbacks_update, run_callbacks_update = mkcallback("update")
local callbacks_create, run_callbacks_create = mkcallback("create")
local callbacks_remove, run_callbacks_remove = mkcallback("remove")
+-- required to call from couple.lua
+function advtrains.update_train_start_and_end(train)
+ recalc_end_index(train)
+ run_callbacks_update(train.id, train)
+end
-- train_ensure_init: responsible for creating a state that we can work on, after one of the following events has happened:
-- - the train's path got cleared
@@ -386,7 +395,7 @@ function advtrains.train_step_b(id, train, dtime)
-- interlocking speed restriction
elseif train.speed_restriction then
--atprint("in train_step_b: applying interlocking speed restriction",train.speed_restriction)
- sit_v_cap = train.speed_restriction
+ sit_v_cap = math.min(sit_v_cap or math.huge, train.speed_restriction)
end
--apply off-track handling:
@@ -394,7 +403,7 @@ function advtrains.train_step_b(id, train, dtime)
local back_off_track=train.end_index<train.path_trk_b
train.off_track = front_off_track or back_off_track
- if back_off_track and (not v_cap or v_cap > 1) then
+ if back_off_track and (not sit_v_cap or sit_v_cap > 1) then
--atprint("in train_step_b: applying back_off_track")
sit_v_cap = 1
elseif front_off_track then
@@ -417,9 +426,11 @@ function advtrains.train_step_b(id, train, dtime)
ctrl_lever = userc
else
if train.atc_command then
- if (not train.atc_delay or train.atc_delay<=0) and not train.atc_wait_finish then
+ if (not train.atc_delay or train.atc_delay<=0)
+ and not train.atc_wait_finish
+ and not train.atc_wait_autocouple then
advtrains.atc.execute_atc_command(id, train)
- else
+ elseif train.atc_delay and train.atc_delay > 0 then
train.atc_delay=train.atc_delay-dtime
end
elseif train.atc_delay then
@@ -587,12 +598,77 @@ function advtrains.train_step_b(id, train, dtime)
else
--atprint("in train_step_b: movement calculation reusing from LZB newindex=",new_index_curr_tv)
end
-
+
-- if the zeroappr mechanism has hit, go no further than zeroappr index
if lzb_next_zero_barrier and new_index_curr_tv > lzb_next_zero_barrier then
--atprint("in train_step_b: Zero barrier hit, clipping to newidx_tv=",new_index_curr_tv, "zb_idx=",lzb_next_zero_barrier)
new_index_curr_tv = lzb_next_zero_barrier
end
+
+ -- New same-track collision system - check for any other trains within the range we're going to move
+ -- do the checks if we either are moving or about to start moving
+ if new_index_curr_tv > train.index or accelerating then -- only if train is actually advancing
+ -- Note: duplicate code from path_project() because of subtle differences: no frac processing and scanning all occupations
+ --[[train.debug = ""
+ local atdebug = function(t, ...)
+ local text=advtrains.print_concat_table({t, ...})
+ train.debug = train.debug..text.."\n"
+ end]]
+ local base_idx = atfloor(new_index_curr_tv + 1)
+ local base_pos = advtrains.path_get(train, base_idx)
+ local base_cn = train.path_cn[base_idx]
+ --atdebug(id,"Begin Checking for on-track collisions new_idx=",new_index_curr_tv,"base_idx=",base_idx,"base_pos=",base_pos,"base_cn=",base_cn)
+ -- query occupation
+ local occ = advtrains.occ.reverse_lookup_sel(base_pos, "close_proximity")
+ -- iterate other trains
+ for otid, ob_idx in pairs(occ) do
+ if otid ~= id then
+ --atdebug(id,"Found other train",otid," with matching index ",ob_idx)
+ -- Phase 1 - determine if trains are facing and which is the relefant stpo index
+ local otrn = advtrains.trains[otid]
+
+ -- retrieve other train's cn and cp
+ local ocn = otrn.path_cn[ob_idx]
+ local ocp = otrn.path_cp[ob_idx]
+
+ local target_is_inside, ref_index, facing
+
+ if base_cn == ocn then
+ -- same direction
+ ref_index = otrn.end_index
+ same_dir = true
+ target_is_inside = (ob_idx >= ref_index)
+ --atdebug("Same direction: ref_index",ref_index,"inside=",target_is_inside)
+ elseif base_cn == ocp then
+ -- facing trains - subtract index frac
+ ref_index = otrn.index
+ same_dir = false
+ target_is_inside = (ob_idx <= ref_index)
+ --atdebug("Facing direction: ref_index",ref_index,"inside=",target_is_inside)
+ end
+
+ -- Phase 2 - project ref_index back onto our path and check again (necessary because there might be a turnout on the way and we are driving into the flank
+ if target_is_inside then
+ local our_index = advtrains.path_project(otrn, ref_index, id, "before_end")
+ --atdebug("Backprojected our_index",our_index)
+ if our_index and our_index <= new_index_curr_tv
+ and our_index >= train.index then --FIX: If train was already past the collision point in the previous step, there is no collision! Fixes bug with split_at_index
+ -- ON_TRACK COLLISION IS HAPPENING
+ -- the actual collision is handled in train_step_c, so set appropriate signal variables
+ train.ontrack_collision_info = {
+ otid = otid,
+ same_dir = same_dir,
+ }
+ -- clip newindex
+ --atdebug("-- Collision detected!")
+ new_index_curr_tv = our_index
+ end
+ end
+ end
+ end
+ end
+
+ -- ## Movement happens here ##
train.index = new_index_curr_tv
recalc_end_index(train)
@@ -638,21 +714,45 @@ function advtrains.train_step_c(id, train, dtime)
advtrains.spawn_wagons(id)
train.check_trainpartload=2
end
-
- --- 8. check for collisions with other trains and damage players ---
-
+
local train_moves=(train.velocity~=0)
-
- --- 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)
+ local very_short_train = train.trainlen < 3
+
+ --- On-track collision handling - detected in train_step_b, but handled here so all other train movements have already happened.
+ if train.ontrack_collision_info then
+ train.velocity = 0
+ train.acceleration = 0
+ --advtrains.atc.train_reset_command(train) will occur in couple_initiate_with if required
+
+ local otrn = advtrains.trains[train.ontrack_collision_info.otid]
+
+ if otrn.velocity == 0 then -- other train must be standing, else don't initiate coupling
+ advtrains.couple_initiate_with(train, otrn, not train.ontrack_collision_info.same_dir)
+ else
+ -- other collision - stop any ATC control
+ advtrains.atc.train_reset_command(train)
+ end
+
+ train.ontrack_collision_info = nil
+ train.couples_up_to_date = true
end
- train.was_standing = not train_moves
-
+
+ -- handle couples if on_track collision handling did not fire
if train_moves then
-
+ train.couples_up_to_date = nil
+ elseif not train.couples_up_to_date then
+ if not very_short_train then -- old coupling system is buggy for short trains
+ advtrains.train_check_couples(train) -- no guarantee for train order here
+ end
+ train.couples_up_to_date = true
+ end
+
+ --- 8. check for collisions with other trains and damage players ---
+ if train_moves then
+ -- Note: this code handles collisions with trains that are not on the same path as the current train
+ -- The same-track collisions and coupling handling is found in couple.lua and handled from train_step_b() and code 2 blocks above.
local collided = false
- local coll_grace=1
+ local coll_grace=2
local collindex = advtrains.path_get_index_by_offset(train, train.index, -coll_grace)
local collpos = advtrains.path_get(train, atround(collindex))
if collpos then
@@ -663,13 +763,14 @@ function advtrains.train_step_c(id, train, dtime)
local testpos=vector.add(rcollpos, {x=x, y=0, z=z})
--- 8a Check collision ---
if not collided then
-
- local col_tr = advtrains.occ.check_collision(testpos, id)
- if col_tr then
- advtrains.train_check_couples(train)
- train.velocity = 0
- advtrains.atc.train_reset_command(train)
- collided = true
+ if not very_short_train then -- position collision system is buggy for short trains
+ local col_tr = advtrains.occ.check_collision(testpos, id)
+ if col_tr then
+ train.velocity = 0
+ train.acceleration = 0
+ advtrains.atc.train_reset_command(train)
+ collided = true
+ end
end
--- 8b damage players ---
@@ -703,7 +804,7 @@ function advtrains.train_step_c(id, train, dtime)
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
+ and obj:get_luaentity() and obj:get_luaentity().name~="signs_lib:text" then
obj:punch(obj, 1, { full_punch_interval = 1.0, damage_groups = {fleshy = 1000}, }, nil)
end
end
@@ -899,7 +1000,7 @@ function advtrains.remove_train(id)
run_callbacks_remove(id, train)
- advtrains.path_invalidate(train)
+ advtrains.path_invalidate(train, true)
advtrains.couple_invalidate(train)
local tp = train.trainparts
@@ -952,7 +1053,16 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate)
if data then
local wagon = advtrains.wagon_prototypes[data.type or data.entity_name]
if not wagon then
- atwarn("Wagon '",data.type,"' couldn't be found. Please check that all required modules are loaded!")
+ local ent = advtrains.wagon_objects[w_id]
+ local pdesc
+ if ent then
+ pdesc = "at " .. minetest.pos_to_string(ent:get_pos())
+ elseif train.last_pos then
+ pdesc = "near " .. minetest.pos_to_string(train.last_pos)
+ else
+ pdesc = "at an unknown location"
+ end
+ atwarn(string.format("Wagon %q %s could not be found. Please check that all required modules are loaded!", data.type, pdesc))
wagon = advtrains.wagon_prototypes["advtrains:wagon_placeholder"]
end
@@ -1019,55 +1129,9 @@ function advtrains.spawn_wagons(train_id)
end
end
-function advtrains.split_train_at_fc(train, count_empty, length_limit)
- -- splits train at first different current FC by convention,
- -- locomotives have empty FC so are ignored
- -- count_empty is used to split off locomotives
- -- length_limit limits the length of the first train to length_limit wagons
- 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]
- if length_limit and i > length_limit then
- ind = i
- break
- end
- if data then
- local wfc = advtrains.get_cur_fc(data)
- if wfc ~= "" or count_empty then
- 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
-
-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
-
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.
+ --atdebug("split_train_at_index invoked on",train.id,"index",index)
local train_id=train.id
if index > #train.trainparts then
@@ -1090,6 +1154,7 @@ function advtrains.split_train_at_index(train, index)
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)
+ --atdebug("new train position p_index",p_index,"pos",pos,"connid",connid,"frac",frac)
local tp = {}
for k,v in ipairs(train.trainparts) do
if k >= index then
@@ -1099,12 +1164,14 @@ function advtrains.split_train_at_index(train, index)
end
advtrains.update_trainpart_properties(train_id)
recalc_end_index(train)
+ --atdebug("old train index",train.index,"end_index",train.end_index)
run_callbacks_update(train_id, train)
--create subtrain
local newtrain_id=advtrains.create_new_train_at(pos, connid, frac, tp)
local newtrain=advtrains.trains[newtrain_id]
-
+ --atdebug("new train created with ID",newtrain_id,"index",newtrain.index,"end_index",newtrain.end_index)
+
newtrain.velocity=train.velocity
-- copy various properties from the old to the new train
newtrain.door_open = train.door_open
@@ -1113,6 +1180,7 @@ function advtrains.split_train_at_index(train, index)
newtrain.line = train.line
newtrain.routingcode = train.routingcode
newtrain.speed_restriction = train.speed_restriction
+ newtrain.speed_restrictions_t = table.copy(train.speed_restrictions_t or {main=train.speed_restriction})
newtrain.is_shunt = train.is_shunt
newtrain.points_split = advtrains.merge_tables(train.points_split)
newtrain.autocouple = train.autocouple
@@ -1121,167 +1189,6 @@ function advtrains.split_train_at_index(train, index)
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)
-end
-
--- coupling
-local CPL_CHK_DST = -1
-local CPL_ZONE = 2
-
--- 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)
- local id1 = train1.id
- local id2 = train2.id
- if train1.autocouple or train2.autocouple then
- -- couple trains
- train1.autocouple = nil
- train2.autocouple = nil
- minetest.after(0, advtrains.safe_couple_trains, id1, id2, t1_is_front, t2_is_front, false, false, train1.velocity, train2.velocity)
- return
- end
-
- local obj=minetest.add_entity(pos, "advtrains:couple")
- if not obj then error("Failed creating couple object!") return end
- local le=obj:get_luaentity()
- le.train_id_1=id1
- le.train_id_2=id2
- le.t1_is_front=t1_is_front
- le.t2_is_front=t2_is_front
- --atdebug("created couple between",train1.id,t1_is_front,train2.id,t2_is_front)
- 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
-
-end
-
-function advtrains.train_check_couples(train)
- --atdebug("rechecking couples")
- if train.cpl_front then
- if not train.cpl_front:get_yaw() then
- -- objectref is no longer valid. reset.
- train.cpl_front = nil
- end
- end
- if not train.cpl_front then
- -- recheck front couple
- local front_trains, pos = advtrains.occ.get_occupations(train, atround(train.index) + CPL_CHK_DST)
- if advtrains.is_node_loaded(pos) then -- if the position is loaded...
- 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
- end
- --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
- end
- end
- end
- end
- if train.cpl_back then
- if not train.cpl_back:get_yaw() then
- -- objectref is no longer valid. reset.
- train.cpl_back = nil
- end
- end
- 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)
- if advtrains.is_node_loaded(pos) then -- if the position is loaded...
- 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
- end
- 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
- end
- end
- end
- end
-end
-
-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
-
--- 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
-
-
-function advtrains.do_connect_trains(first_id, second_id, vel)
- local first, second=advtrains.trains[first_id], advtrains.trains[second_id]
-
- 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
-
- local first_wagoncnt=#first.trainparts
- local second_wagoncnt=#second.trainparts
-
- for _,v in ipairs(second.trainparts) do
- table.insert(first.trainparts, v)
- end
-
- advtrains.remove_train(second_id)
- if vel < 0 then
- advtrains.invert_train(first_id)
- vel = -vel
- end
- first.velocity= vel or 0
-
- advtrains.update_trainpart_properties(first_id)
- advtrains.couple_invalidate(first)
- return true
-end
-
function advtrains.invert_train(train_id)
local train=advtrains.trains[train_id]
@@ -1311,15 +1218,14 @@ function advtrains.invert_train(train_id)
advtrains.update_trainpart_properties(train_id, true)
-- recalculate path
- advtrains.train_ensure_init(train_id, train)
-- 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
+ advtrains.speed.set_restriction(train, "main", advtrains.SHUNT_SPEED_MAX)
else
train.is_shunt = false
- train.speed_restriction = nil
+ advtrains.speed.set_restriction(train, "main", -1)
end
end
@@ -1337,7 +1243,7 @@ function advtrains.invalidate_all_paths(pos)
local tab
if pos then
-- if position given, check occupation system
- tab = advtrains.occ.get_trains_over(pos)
+ tab = advtrains.occ.reverse_lookup_quick(pos)
else
tab = advtrains.trains
end
@@ -1350,7 +1256,7 @@ end
-- Calls invalidate_path_ahead on all trains occupying (having paths over) this node
-- Can be called during train step.
function advtrains.invalidate_all_paths_ahead(pos)
- local tab = advtrains.occ.get_trains_over(pos)
+ local tab = advtrains.occ.reverse_lookup_sel(pos, "first_ahead")
for id,index in pairs(tab) do
local train = advtrains.trains[id]
@@ -1368,44 +1274,61 @@ function advtrains.invalidate_path(id)
end
--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 minetest.settings:get_bool("advtrains_forgiving_collision") then
+ function advtrains.train_collides(node)
+ if node and minetest.registered_nodes[node.name] then
+ local ndef = minetest.registered_nodes[node.name]
+ -- if the node is drawtype normal (that is a full cube) then it does collide
+ if ndef.drawtype == "normal" then
+ -- except if it is not_blocking_trains
+ if ndef.groups.not_blocking_trains and ndef.groups.not_blocking_trains ~= 0 then
+ return false
+ end
+ return true
+ end
+ end
+ return false
+ end
+else
+ 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",
- "default:fence_acacia_wood",
- "default:fence_aspen_wood",
- "default:fence_pine_wood",
- "default:fence_junglewood",
- "default:torch",
- "bones:bones",
-
- "default:sign_wall",
- "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",
- "signs:sign_post",
- "signs:sign_hanging",
-
-
-}
-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
+ return false
end
-end)
+
+ local nonblocknodes={
+ "default:fence_wood",
+ "default:fence_acacia_wood",
+ "default:fence_aspen_wood",
+ "default:fence_pine_wood",
+ "default:fence_junglewood",
+ "default:torch",
+ "bones:bones",
+
+ "default:sign_wall",
+ "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",
+ "signs:sign_post",
+ "signs:sign_hanging",
+
+ }
+ 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)
+end