diff --git a/mods/advtrains/advtrains/api_doc.txt b/mods/advtrains/advtrains/api_doc.txt index 8ac49860..1e49df32 100644 --- a/mods/advtrains/advtrains/api_doc.txt +++ b/mods/advtrains/advtrains/api_doc.txt @@ -75,6 +75,13 @@ advtrains.register_wagon(name, prototype, description, inventory_image) ^- Getting on by walking in then takes effect. ^- Positive values mean front, negative ones back. Resulting position is automatically shifted to the right side. + coupler_types_front = {scharfenberg=true}, + coupler_types_back = {chain=true}, + ^- Defines the available coupler types on this wagon on the front and back side. Wagon will only couple to wagons that have a matching coupler. (this property does not have any visual impact) + ^- Default: not given (nil) - causes the wagon to couple to any other wagon regardless of coupler type. + ^- Empty table ({}): This wagon does not couple to any other wagon (e.g. for Linetrack vehicles) + ^- Register coupler types using ''advtrains.register_coupler_type(type, name)''. advtrains defines the default types "chain" (Buffer and Chain) and "scharfenberg" (Scharfenberg coupler). + wagon_span=2, ^- How far this wagon extends from its base position. Is the half of the wagon length. ^- Used to determine in which distance the other wagons have to be positioned. Will require tweaking. diff --git a/mods/advtrains/advtrains/atc.lua b/mods/advtrains/advtrains/atc.lua index 64cdceca..8cb3e8f4 100644 --- a/mods/advtrains/advtrains/atc.lua +++ b/mods/advtrains/advtrains/atc.lua @@ -199,10 +199,16 @@ local matchptn={ return #match+1 end, ["B([0-9]+)"]=function(id, train, match) - if train.velocity>tonumber(match) then - train.atc_brake_target=tonumber(match) - if not train.tarvelocity or train.tarvelocity>train.atc_brake_target then - train.tarvelocity=train.atc_brake_target + local btar = tonumber(match) + if train.velocity>btar then + train.atc_brake_target=btar + 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 return #match+1 @@ -267,6 +273,10 @@ local matchptn={ advtrains.interlocking.ars_set_disable(train, match=="0") return 2 end, + ["Cpl"]=function(id, train) + train.atc_wait_autocouple=true + return 3 + end, } eval_conditional = function(command, arrow, speed) @@ -358,11 +368,13 @@ function atc.execute_atc_command(id, train) local match=string.match(command, "^"..pattern) if match then local patlen=func(id, train, match) - - atprint("Executing: "..string.sub(command, 1, patlen)) - + --atdebug("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) - 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.) atc.execute_atc_command(id, train) end diff --git a/mods/advtrains/advtrains/couple.lua b/mods/advtrains/advtrains/couple.lua index 336a6d42..c421f610 100644 --- a/mods/advtrains/advtrains/couple.lua +++ b/mods/advtrains/advtrains/couple.lua @@ -11,17 +11,24 @@ -- When the initiating train has autocouple set, trains are immediately coupled -- 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: - -- 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 - -- wagons of stationary train are inserted at the beginning of initiating 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 -- These objectRefs will delete themselves once the couples no longer match (see below) + +advtrains.coupler_types = {} + +function advtrains.register_coupler_type(code, name) + advtrains.coupler_types[code] = name +end + +-- Register some default couplers +advtrains.register_coupler_type("chain", attrans("Buffer and Chain Coupler")) +advtrains.register_coupler_type("scharfenberg", attrans("Scharfenberg Coupler")) + + local function create_couple_entity(pos, train1, t1_is_front, train2, t2_is_front) local id1 = train1.id local id2 = train2.id @@ -142,14 +149,22 @@ 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) -- Depending on autocouple, either couples immediately or spawns a couple entity 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) - if init_train.autocouple then - advtrains.couple_trains(init_train, true, stat_train, stat_is_front) - else - local pos = advtrains.path_get_interpolated(init_train, init_train.index) - create_couple_entity(pos, init_train, true, stat_train, stat_is_front) + --atdebug("Couple init autocouple=",init_train.autocouple,"atc_w_acpl=",init_train.atc_wait_autocouple) + if init_train.autocouple or init_train.atc_wait_autocouple then + local cplmatch, msg = advtrains.check_matching_coupler_types(init_train, true, stat_train, stat_is_front) + if cplmatch then + advtrains.couple_trains(init_train, false, stat_train, stat_is_front) + -- clear atc couple waiting blocker + init_train.atc_wait_autocouple = nil + return + end end - + -- get here if either autocouple is not on or couples dont match + local pos = advtrains.path_get_interpolated(init_train, init_train.index) + 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 -- check if the player has permission for the first/last wagon of the train @@ -177,54 +192,145 @@ function advtrains.safe_couple_trains(train1, t1_is_front, train2, t2_is_front, wck_t2 = check_twagon_owner(train2, t2_is_front, pname) end if (wck_t1 or wck_t2) or not pname then - advtrains.couple_trains(train1, t1_is_front, train2, t2_is_front) + + local cplmatch, msg = advtrains.check_matching_coupler_types(train1, t1_is_front, train2, t2_is_front) + if cplmatch then + advtrains.couple_trains(train1, not t1_is_front, train2, t2_is_front) + else + minetest.chat_send_player(pname, msg) + end end end -- Actually performs the train coupling. Always retains train ID of train1 -function advtrains.couple_trains(train1, t1_is_front, train2, t2_is_front) - --atdebug("Couple trains init=",init_train.id,"stat=",stat_train.id,"statreverse=",stat_must_reverse) - -- 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 +function advtrains.couple_trains(init_train, invert_init_train, stat_train, stat_train_opposite) + --atdebug("Couple trains init=",init_train.id,"initinv=",invert_init_train,"stat=",stat_train.id,"statreverse=",stat_train_opposite) - advtrains.do_connect_trains(train1, train2) -end - --- 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!") + if not advtrains.train_ensure_init(init_train.id, init_train) then + atwarn("Coupling: initiating train",init_train.id,"is not initialized! Operation aborted!") return end - if not advtrains.train_ensure_init(second.id, second) then - atwarn("Coupling: second train",second.id,"is not initialized! Operation aborted!") + if not advtrains.train_ensure_init(stat_train.id, stat_train) then + atwarn("Coupling: stationary train",stat_train.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) - first.velocity = 0 + -- only used with the couple entity + if invert_init_train then + advtrains.invert_train(init_train.id) + end + + 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 + + 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 + + -- TODO: migrate some of the properties from stat_train to init_train? - advtrains.update_trainpart_properties(first.id) - 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 end +-- Couple types matching check +-- returns: true, nil if OK +-- false, errmsg if there is an error +function advtrains.check_matching_coupler_types(t1, t1_front, t2, t2_front) + -- 1. get wagons + local t1_wid + if t1_front then + t1_wid = t1.trainparts[1] + else + t1_wid = t1.trainparts[#t1.trainparts] + end + local t2_wid + if t2_front then + t2_wid = t2.trainparts[1] + else + t2_wid = t2.trainparts[#t2.trainparts] + end + --atdebug("CMCT: t1_wid",t1_wid,"t2_wid",t2_wid,"") + + if not t1_wid or not t2_wid then + return false, "Unable to retrieve wagons from train"--note: no translation needed, case should not occur + end + + local t1_wagon = advtrains.wagons[t1_wid] + local t2_wagon = advtrains.wagons[t2_wid] + + if not t1_wagon or not t2_wagon then + return false, "At least one of wagons "..t1_wagon.." or "..t2_wagon.." does not exist"--note: no translation needed, case should not occur + end + + -- these calls do not fail, they may return placeholder - doesn't matter + local _,t1_wpro = advtrains.get_wagon_prototype(t1_wagon) + local _,t2_wpro = advtrains.get_wagon_prototype(t2_wagon) + + -- get correct couplers table (front/back) + local t1_cplt + if not t1_front == not t1_wagon.wagon_flipped then --fancy XOR + t1_cplt = t1_wpro.coupler_types_back + else + t1_cplt = t1_wpro.coupler_types_front + end + local t2_cplt + if not t2_front == not t2_wagon.wagon_flipped then --fancy XOR + t2_cplt = t2_wpro.coupler_types_back + else + t2_cplt = t2_wpro.coupler_types_front + end + + --atdebug("CMCT: t1",t1_cplt,"t2",t2_cplt,"") + + -- if at least one of the trains has no couplers table, it always couples (fallback behavior and mode for universal shunters) + if not t1_cplt or not t2_cplt then + return true + end + + -- have common coupler? + for typ,_ in pairs(t1_cplt) do + if t2_cplt[typ] then + --atdebug("CMCT: Matching type",typ) + return true + end + end + --no match, give user an info + local t1_cplhr, t2_cplhr = {},{} + for typ,_ in pairs(t1_cplt) do + table.insert(t1_cplhr, advtrains.coupler_types[typ] or typ) + end + if #t1_cplhr==0 then t1_cplhr[1]=attrans("") end + for typ,_ in pairs(t2_cplt) do + table.insert(t2_cplhr, advtrains.coupler_types[typ] or typ) + end + if #t2_cplhr==0 then t2_cplhr[1]=attrans("") end + return false, attrans("Can not couple: The couplers of the trains do not match (@1 and @2).", table.concat(t1_cplhr, ","), table.concat(t2_cplhr, ",")) +end -- DECOUPLING -- function advtrains.split_train_at_fc(train, count_empty, length_limit) diff --git a/mods/advtrains/advtrains/init.lua b/mods/advtrains/advtrains/init.lua index 083281e4..0882237a 100644 --- a/mods/advtrains/advtrains/init.lua +++ b/mods/advtrains/advtrains/init.lua @@ -468,7 +468,7 @@ advtrains.avt_save = function(remove_players_from_wagons) "atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open", "text_outside", "text_inside", "line", "routingcode", "il_sections", "speed_restriction", "is_shunt", - "points_split", "autocouple", "ars_disable", + "points_split", "autocouple", "atc_wait_autocouple", "ars_disable", }) --then save it tmp_trains[id]=v diff --git a/mods/advtrains/advtrains/locale/advtrains.de.tr b/mods/advtrains/advtrains/locale/advtrains.de.tr index cd43eed9..6abbc123 100644 --- a/mods/advtrains/advtrains/locale/advtrains.de.tr +++ b/mods/advtrains/advtrains/locale/advtrains.de.tr @@ -70,3 +70,8 @@ This track can not be removed!=Diese Schiene kann nicht entfernt werden! Position is occupied by a train.=Ein Zug steht an dieser Position. There's a Track Circuit Break here.=Hier ist eine Gleisabschnittsgrenze (TCB). There's a Signal Influence Point here.=Hier ist ein Signal-Beeinflussungspunkt. +Buffer and Chain Coupler=Schraubenkupplung +Scharfenberg Coupler=Scharfenbergkupplung +Japanese Train Inter-Wagon Connection=Waggonzwischenverbindung Japanischer Personenzug +Can not couple: The couplers of the trains do not match (@1 and @2).=Kann nicht ankuppeln: Die Kupplungen der Züge passen nicht zueinander (@1 und @2) += diff --git a/mods/advtrains/advtrains/trainlogic.lua b/mods/advtrains/advtrains/trainlogic.lua index 00c04bf1..4650f9ec 100644 --- a/mods/advtrains/advtrains/trainlogic.lua +++ b/mods/advtrains/advtrains/trainlogic.lua @@ -418,9 +418,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 @@ -711,12 +713,15 @@ function advtrains.train_step_c(id, train, dtime) if train.ontrack_collision_info then train.velocity = 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] 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 diff --git a/mods/advtrains/advtrains_interlocking/ars.lua b/mods/advtrains/advtrains_interlocking/ars.lua index 434ae2ce..4f50df98 100644 --- a/mods/advtrains/advtrains_interlocking/ars.lua +++ b/mods/advtrains/advtrains_interlocking/ars.lua @@ -133,9 +133,11 @@ function advtrains.interlocking.ars_check(sigd, train) local tcbs = il.db.get_tcbs(sigd) if not tcbs or not tcbs.routes then return end - if tcbs.ars_disabled then + if tcbs.ars_disabled or tcbs.ars_ignore_next then -- No-ARS mode of signal. -- ignore... + -- Note: ars_ignore_next is set by signalling formspec when route is cancelled + tcbs.ars_ignore_next = nil return end diff --git a/mods/advtrains/advtrains_interlocking/tcb_ts_ui.lua b/mods/advtrains/advtrains_interlocking/tcb_ts_ui.lua index 34fbf7ff..0cc10da1 100755 --- a/mods/advtrains/advtrains_interlocking/tcb_ts_ui.lua +++ b/mods/advtrains/advtrains_interlocking/tcb_ts_ui.lua @@ -723,11 +723,17 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) connid = tonumber(connids) if not connid or connid<1 or connid>2 then return end end - if pos and connid and not fields.quit then + if pos and connid then local sigd = {p=pos, s=connid} local tcbs = ildb.get_tcbs(sigd) if not tcbs then return end - + + if fields.quit then + -- form quit: disable temporary ARS ignore + tcbs.ars_ignore_next = nil + return + end + local sel_rte if fields.rtelist then local tev = minetest.explode_textlist_event(fields.rtelist) @@ -740,7 +746,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end if tcbs.routeset and fields.cancelroute then if tcbs.routes[tcbs.routeset] and tcbs.routes[tcbs.routeset].ars then - tcbs.ars_disabled = true + tcbs.ars_ignore_next = true end -- if route committed, cancel route ts info ilrs.update_route(sigd, tcbs, nil, true) @@ -749,6 +755,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.newroute and hasprivs then advtrains.interlocking.init_route_prog(pname, sigd) minetest.close_formspec(pname, formname) + tcbs.ars_ignore_next = nil return end if sel_rte and tcbs.routes[sel_rte] then diff --git a/mods/advtrains/atc_command.txt b/mods/advtrains/atc_command.txt index 9f4eb501..5a1c8fff 100644 --- a/mods/advtrains/atc_command.txt +++ b/mods/advtrains/atc_command.txt @@ -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 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: I; diff --git a/mods/basic_trains/advtrains_train_industrial/init.lua b/mods/basic_trains/advtrains_train_industrial/init.lua index f22f8065..c798e513 100755 --- a/mods/basic_trains/advtrains_train_industrial/init.lua +++ b/mods/basic_trains/advtrains_train_industrial/init.lua @@ -29,6 +29,8 @@ advtrains.register_wagon("engine_industrial", { }, }, assign_to_seat_group = {"dstand"}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=2.6, is_locomotive=true, @@ -66,6 +68,8 @@ advtrains.register_wagon("engine_industrial_big", { }, }, assign_to_seat_group = {"dstand"}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=4, is_locomotive=true, @@ -79,6 +83,8 @@ advtrains.register_wagon("wagon_tank", { seats = {}, drives_on={default=true}, max_speed=20, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=2.2, collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0}, @@ -95,6 +101,8 @@ advtrains.register_wagon("wagon_wood", { seats = {}, drives_on={default=true}, max_speed=20, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=1.8, collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0}, diff --git a/mods/basic_trains/advtrains_train_japan/init.lua b/mods/basic_trains/advtrains_train_japan/init.lua index a7848454..930ffc83 100644 --- a/mods/basic_trains/advtrains_train_japan/init.lua +++ b/mods/basic_trains/advtrains_train_japan/init.lua @@ -1,5 +1,8 @@ local S = attrans +-- note: scharfenberg coupler is defined in advtrains core +advtrains.register_coupler_type("train_japan_interwagon", attrans("Japanese Train Inter-Wagon Connection")) + advtrains.register_wagon("engine_japan", { mesh="advtrains_engine_japan.b3d", textures = {"advtrains_engine_japan.png"}, @@ -61,6 +64,8 @@ advtrains.register_wagon("engine_japan", { [1]={frames={x=60, y=80}, time=1} } }, + coupler_types_front = {scharfenberg=true}, + coupler_types_back = {train_japan_interwagon=true}, door_entry={-1.7}, visual_size = {x=1, y=1}, wagon_span=2.5, @@ -131,6 +136,8 @@ advtrains.register_wagon("wagon_japan", { [1]={frames={x=60, y=80}, time=1} } }, + coupler_types_front = {train_japan_interwagon=true}, + coupler_types_back = {train_japan_interwagon=true}, door_entry={-1.7, 1.7}, visual_size = {x=1, y=1}, wagon_span=2.3, diff --git a/mods/basic_trains/advtrains_train_steam/init.lua b/mods/basic_trains/advtrains_train_steam/init.lua index 2f46ac29..5e8b0d3e 100755 --- a/mods/basic_trains/advtrains_train_steam/init.lua +++ b/mods/basic_trains/advtrains_train_steam/init.lua @@ -31,6 +31,8 @@ advtrains.register_wagon("newlocomotive", { }, }, assign_to_seat_group = {"dstand"}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=2.3, collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0}, @@ -100,6 +102,8 @@ advtrains.register_wagon("detailed_steam_engine", { }, }, assign_to_seat_group = {"dstand"}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=2.05, collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0}, @@ -193,6 +197,8 @@ advtrains.register_wagon("wagon_default", { }, assign_to_seat_group = {"pass"}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=2.634, collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0}, @@ -206,6 +212,8 @@ advtrains.register_wagon("wagon_box", { drives_on={default=true}, max_speed=10, seats = {}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, visual_size = {x=1, y=1}, wagon_span=2, collisionbox = {-1.0,-0.5,-1.0, 1.0,2.5,1.0}, diff --git a/mods/basic_trains/advtrains_train_subway/init.lua b/mods/basic_trains/advtrains_train_subway/init.lua index add961b4..c25b1b66 100644 --- a/mods/basic_trains/advtrains_train_subway/init.lua +++ b/mods/basic_trains/advtrains_train_subway/init.lua @@ -51,6 +51,8 @@ advtrains.register_wagon("subway_wagon", { }, }, assign_to_seat_group = {"pass", "dstand"}, + coupler_types_front = {chain=true}, + coupler_types_back = {chain=true}, doors={ open={ [-1]={frames={x=0, y=20}, time=1},