Train Coupling: Change behavior so that train direction of initiating train is kept, add ATC Cpl command

This commit is contained in:
orwell96 2021-10-25 19:54:58 +02:00
parent 4989da3663
commit e7d0a5fac2
5 changed files with 85 additions and 54 deletions

View File

@ -199,10 +199,16 @@ local matchptn={
return #match+1 return #match+1
end, end,
["B([0-9]+)"]=function(id, train, match) ["B([0-9]+)"]=function(id, train, match)
if train.velocity>tonumber(match) then local btar = tonumber(match)
train.atc_brake_target=tonumber(match) if train.velocity>btar then
if not train.tarvelocity or train.tarvelocity>train.atc_brake_target then train.atc_brake_target=btar
train.tarvelocity=train.atc_brake_target if not train.tarvelocity or train.tarvelocity>btar then
train.tarvelocity=btar
end
else
-- independent of brake target, must make sure that tarvelocity is not greater than it
if train.tarvelocity and train.tarvelocity>btar then
train.tarvelocity=btar
end end
end end
return #match+1 return #match+1
@ -267,6 +273,10 @@ local matchptn={
advtrains.interlocking.ars_set_disable(train, match=="0") advtrains.interlocking.ars_set_disable(train, match=="0")
return 2 return 2
end, end,
["Cpl"]=function(id, train)
train.atc_wait_autocouple=true
return 3
end,
} }
eval_conditional = function(command, arrow, speed) eval_conditional = function(command, arrow, speed)
@ -358,11 +368,13 @@ function atc.execute_atc_command(id, train)
local match=string.match(command, "^"..pattern) local match=string.match(command, "^"..pattern)
if match then if match then
local patlen=func(id, train, match) local patlen=func(id, train, match)
--atdebug("Executing: "..string.sub(command, 1, patlen))
atprint("Executing: "..string.sub(command, 1, patlen)) --atdebug("Train ATC State: tvel=",train.tarvelocity,"brktar=",train.atc_brake_target,"delay=",train.atc_delay,"wfinish=",train.atc_wait_finish,"wacpl=",train.atc_wait_autocouple)
train.atc_command=string.sub(command, patlen+1) train.atc_command=string.sub(command, patlen+1)
if train.atc_delay<=0 and not train.atc_wait_finish then if train.atc_delay<=0
and not train.atc_wait_finish
and not train.atc_wait_autocouple then
--continue (recursive, cmds shouldn't get too long, and it's a end-recursion.) --continue (recursive, cmds shouldn't get too long, and it's a end-recursion.)
atc.execute_atc_command(id, train) atc.execute_atc_command(id, train)
end end

View File

@ -11,14 +11,9 @@
-- When the initiating train has autocouple set, trains are immediately coupled -- When the initiating train has autocouple set, trains are immediately coupled
-- When not, a couple entity is spawned and coupling commences on click -- When not, a couple entity is spawned and coupling commences on click
-- Coupling MUST preserve the train ID of the initiating train, so it is done like this: -- Coupling MUST preserve the train ID of the initiating train, so it is done like this:
-- initiating train is reversed
-- stationary train is reversed if required, so that it points towards the initiating train
-- do_connect_trains(initiating, stationary)
-- As a result, the coupled train is reversed in direction. Alternative way of doing things (might be considered later):
-- stationary train is reversed if required, so that it points away from the initiating train
-- index of initiating train is set so that it matches the front pos of stationary train -- index of initiating train is set so that it matches the front pos of stationary train
-- wagons of stationary train are inserted at the beginning of initiating train
-- remove stationary train -- remove stationary train
-- wagons of stationary train are inserted at the beginning of initiating train (considers direction of stat_train and inserts reverse if required)
-- train.couple_* contain references to ObjectRefs of couple objects, which contain all relevant information -- 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 (see below) -- These objectRefs will delete themselves once the couples no longer match (see below)
@ -142,12 +137,16 @@ end
-- Called from train_step_b() when the current train (init_train) just stopped at one of the end indices of another train (stat_train) -- Called from train_step_b() when the current train (init_train) just stopped at one of the end indices of another train (stat_train)
-- Depending on autocouple, either couples immediately or spawns a couple entity -- Depending on autocouple, either couples immediately or spawns a couple entity
function advtrains.couple_initiate_with(init_train, stat_train, stat_is_front) function advtrains.couple_initiate_with(init_train, stat_train, stat_is_front)
--atdebug("Initiating couplign between init=",init_train.id,"stat=",stat_train.id,"backside=",stat_is_backside) --atdebug("Couple init autocouple=",init_train.autocouple,"atc_w_acpl=",init_train.atc_wait_autocouple)
if init_train.autocouple then if init_train.autocouple or init_train.atc_wait_autocouple then
advtrains.couple_trains(init_train, true, stat_train, stat_is_front) advtrains.couple_trains(init_train, false, stat_train, stat_is_front)
-- clear atc couple waiting blocker
init_train.atc_wait_autocouple = nil
else else
local pos = advtrains.path_get_interpolated(init_train, init_train.index) local pos = advtrains.path_get_interpolated(init_train, init_train.index)
create_couple_entity(pos, init_train, true, stat_train, stat_is_front) create_couple_entity(pos, init_train, true, stat_train, stat_is_front)
-- clear ATC command on collision
advtrains.atc.train_reset_command(init_train)
end end
end end
@ -177,50 +176,61 @@ function advtrains.safe_couple_trains(train1, t1_is_front, train2, t2_is_front,
wck_t2 = check_twagon_owner(train2, t2_is_front, pname) wck_t2 = check_twagon_owner(train2, t2_is_front, pname)
end end
if (wck_t1 or wck_t2) or not pname then if (wck_t1 or wck_t2) or not pname then
advtrains.couple_trains(train1, t1_is_front, train2, t2_is_front) advtrains.couple_trains(train1, not t1_is_front, train2, t2_is_front)
end end
end end
-- Actually performs the train coupling. Always retains train ID of train1 -- Actually performs the train coupling. Always retains train ID of train1
function advtrains.couple_trains(train1, t1_is_front, train2, t2_is_front) function advtrains.couple_trains(init_train, invert_init_train, stat_train, stat_train_opposite)
--atdebug("Couple trains init=",init_train.id,"stat=",stat_train.id,"statreverse=",stat_must_reverse) --atdebug("Couple trains init=",init_train.id,"initinv=",invert_init_train,"stat=",stat_train.id,"statreverse=",stat_train_opposite)
-- see comment on top of file
if t1_is_front then
advtrains.invert_train(train1.id)
end
if not t2_is_front then
advtrains.invert_train(train2.id)
end
advtrains.do_connect_trains(train1, train2) if not advtrains.train_ensure_init(init_train.id, init_train) then
end atwarn("Coupling: initiating train",init_train.id,"is not initialized! Operation aborted!")
-- Adds the wagons of first to second and deletes second_id afterwards
-- Assumes that second_id stands right behind first_id and both trains point to the same direction
function advtrains.do_connect_trains(first, second)
if not advtrains.train_ensure_init(first.id, first) then
atwarn("Coupling: first train",first.id,"is not initialized! Operation aborted!")
return return
end end
if not advtrains.train_ensure_init(second.id, second) then if not advtrains.train_ensure_init(stat_train.id, stat_train) then
atwarn("Coupling: second train",second.id,"is not initialized! Operation aborted!") atwarn("Coupling: stationary train",stat_train.id,"is not initialized! Operation aborted!")
return return
end end
local first_wagoncnt=#first.trainparts -- only used with the couple entity
local second_wagoncnt=#second.trainparts if invert_init_train then
advtrains.invert_train(init_train.id)
for _,v in ipairs(second.trainparts) do
table.insert(first.trainparts, v)
end end
advtrains.remove_train(second.id) local itp = init_train.trainparts
local init_wagoncnt = #itp
local stp = stat_train.trainparts
local stat_wagoncnt = #stp
local stat_trainlen = stat_train.trainlen -- save the train length of stat train, to be added to index
first.velocity = 0 if stat_train_opposite then
-- insert wagons in inverse order and set their wagon_flipped state
for i=1,stat_wagoncnt do
table.insert(itp, 1, stp[i])
local wdata = advtrains.wagons[stp[i]]
if wdata then
wdata.wagon_flipped = not wdata.wagon_flipped
else
atwarn("While coupling, wagon",stp[i],"of stationary train",stat_train.id,"not found!")
end
end
else
--insert wagons in normal order
for i=stat_wagoncnt,1,-1 do
table.insert(itp, 1, stp[i])
end
end
advtrains.update_trainpart_properties(first.id) -- TODO: migrate some of the properties from stat_train to init_train?
advtrains.couple_invalidate(first)
advtrains.remove_train(stat_train.id)
-- Set train index forward
init_train.index = advtrains.path_get_index_by_offset(init_train, init_train.index, stat_trainlen)
advtrains.update_trainpart_properties(init_train.id)
advtrains.couple_invalidate(init_train)
return true return true
end end

View File

@ -468,7 +468,7 @@ advtrains.avt_save = function(remove_players_from_wagons)
"atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open",
"text_outside", "text_inside", "line", "routingcode", "text_outside", "text_inside", "line", "routingcode",
"il_sections", "speed_restriction", "is_shunt", "il_sections", "speed_restriction", "is_shunt",
"points_split", "autocouple", "ars_disable", "points_split", "autocouple", "atc_wait_autocouple", "ars_disable",
}) })
--then save it --then save it
tmp_trains[id]=v tmp_trains[id]=v

View File

@ -418,9 +418,11 @@ function advtrains.train_step_b(id, train, dtime)
ctrl_lever = userc ctrl_lever = userc
else else
if train.atc_command then 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) 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 train.atc_delay=train.atc_delay-dtime
end end
elseif train.atc_delay then elseif train.atc_delay then
@ -711,12 +713,15 @@ function advtrains.train_step_c(id, train, dtime)
if train.ontrack_collision_info then if train.ontrack_collision_info then
train.velocity = 0 train.velocity = 0
train.acceleration = 0 train.acceleration = 0
advtrains.atc.train_reset_command(train) --advtrains.atc.train_reset_command(train) will occur in couple_initiate_with if required
local otrn = advtrains.trains[train.ontrack_collision_info.otid] local otrn = advtrains.trains[train.ontrack_collision_info.otid]
if otrn.velocity == 0 then -- other train must be standing, else don't initiate coupling 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) 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 end
train.ontrack_collision_info = nil train.ontrack_collision_info = nil

View File

@ -51,6 +51,10 @@ Kick all passengers out of the trains
This command kicks all passengers (non-driving players) off the train. This command works only This command kicks all passengers (non-driving players) off the train. This command works only
if the train is stopped and its doors are open. if the train is stopped and its doors are open.
Cpl
Temporarily switch the train to "Autocouple" mode and wait for coupling.
This command makes the train continue at its current speed until it hits another standing wagon or train. Then, it couples to this train and ATC command execution continues.
# conditional statements: # conditional statements:
I<condition><code>; I<condition><code>;