Rework train same-track collision system and deterministic coupling
- Adds a separate collision system for trains sharing a path - Moved some coupling-related code to couple.lua and refactor it - Handle coupling in a way that the initiating train always keeps its ID - As a side effect, engine has its direction reversed after couplingmaster
parent
c623a33860
commit
1f3a4c3bfc
|
@ -1,14 +1,290 @@
|
||||||
--couple.lua
|
--couple.lua
|
||||||
--defines couple entities.
|
--Handles coupling and discoupling of trains, and defines the coupling entities
|
||||||
|
--Rework June 2021 - some functions from trainlogic.lua have been moved here
|
||||||
|
|
||||||
--advtrains:discouple
|
-- COUPLING --
|
||||||
--set into existing trains to split them when punched.
|
-- During coupling rework, the behavior of coupling was changed to make automation easier. It is now as follows:
|
||||||
--they are attached to the wagons.
|
-- Coupling is only ever initiated when a train is standing somewhere (not moving) and another train drives onto one of its ends
|
||||||
--[[fields
|
-- with a speed greater than 0
|
||||||
wagon
|
-- "stationary" train is the one standing there - in old code called "train2"
|
||||||
|
-- "initiating" train is the one that approached it and bumped into it - typically an engine - in old code called "train1"
|
||||||
|
-- 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 keep their couple entity minetest-internal id inside the field discouple_id. if it refers to nowhere, they will spawn a new one if player is near
|
-- 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)
|
||||||
|
local function create_couple_entity(pos, train1, t1_is_front, train2, t2_is_front)
|
||||||
|
local id1 = train1.id
|
||||||
|
local id2 = train2.id
|
||||||
|
|
||||||
|
-- delete previous couple entities
|
||||||
|
if t1_is_front then
|
||||||
|
if train1.cpl_front then train1.cpl_front:remove() end
|
||||||
|
else
|
||||||
|
if train1.cpl_back then train1.cpl_back:remove() end
|
||||||
|
end
|
||||||
|
if t2_is_front then
|
||||||
|
if train2.cpl_front then train2.cpl_front:remove() end
|
||||||
|
else
|
||||||
|
if train2.cpl_back then train2.cpl_back:remove() end
|
||||||
|
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.t1_is_front=t1_is_front
|
||||||
|
le.train_id_2=id2
|
||||||
|
le.t2_is_front=t2_is_front
|
||||||
|
--atdebug("created couple between",train1.id,train2.id,t2_is_front)
|
||||||
|
|
||||||
|
if t1_is_front then
|
||||||
|
train1.cpl_front = obj
|
||||||
|
else
|
||||||
|
train2.cpl_back = obj
|
||||||
|
end
|
||||||
|
if t2_is_front then
|
||||||
|
train2.cpl_front = obj
|
||||||
|
else
|
||||||
|
train2.cpl_back = obj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Old static couple checking. Never used for autocouple, only used for standing trains if train did not approach
|
||||||
|
local CPL_CHK_DST = -1
|
||||||
|
local CPL_ZONE = 2
|
||||||
|
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
|
||||||
|
create_couple_entity(pos, train, true, other_train, true)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if idx<=other_train.end_index and idx>=other_train.end_index - CPL_ZONE then
|
||||||
|
create_couple_entity(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
|
||||||
|
--atdebug(train.id,"back: ",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
|
||||||
|
create_couple_entity(pos, train, false, other_train, true)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if idx<=other_train.end_index and idx>=other_train.end_index - CPL_ZONE then
|
||||||
|
create_couple_entity(pos, train, false, other_train, false)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Deletes couple entities from the train
|
||||||
|
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.couples_up_to_date = nil
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check if the player has permission for the first/last wagon of the train
|
||||||
|
local function check_twagon_owner(train, b_first, pname)
|
||||||
|
local wtp = b_first and 1 or #train.trainparts
|
||||||
|
local wid = train.trainparts[wtp]
|
||||||
|
local wdata = advtrains.wagons[wid]
|
||||||
|
if wdata then
|
||||||
|
return advtrains.check_driving_couple_protection(pname, wdata.owner, wdata.whitelist)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Perform coupling, but check if the player is authorized to couple
|
||||||
|
function advtrains.safe_couple_trains(train1, t1_is_front, train2, t2_is_front, pname)
|
||||||
|
|
||||||
|
if pname and not minetest.check_player_privs(pname, "train_operator") then
|
||||||
|
minetest.chat_send_player(pname, "Missing train_operator privilege")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local wck_t1, wck_t2
|
||||||
|
if pname then
|
||||||
|
wck_t1 = check_twagon_owner(train1, t1_is_front, pname)
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
|
||||||
|
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!")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not advtrains.train_ensure_init(second.id, second) then
|
||||||
|
atwarn("Coupling: second 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)
|
||||||
|
|
||||||
|
first.velocity = 0
|
||||||
|
|
||||||
|
advtrains.update_trainpart_properties(first.id)
|
||||||
|
advtrains.couple_invalidate(first)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- DECOUPLING --
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
-- split_train_at_index() is in trainlogic.lua because it needs access to two local functions
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
-- COUPLE ENTITIES --
|
||||||
|
|
||||||
local couple_max_dist=3
|
local couple_max_dist=3
|
||||||
|
|
||||||
|
@ -36,8 +312,6 @@ minetest.register_entity("advtrains:discouple", {
|
||||||
if pname and pname~="" and self.wagon then
|
if pname and pname~="" and self.wagon then
|
||||||
if advtrains.safe_decouple_wagon(self.wagon.id, pname) then
|
if advtrains.safe_decouple_wagon(self.wagon.id, pname) then
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
else
|
|
||||||
minetest.add_entity(self.object:getpos(), "advtrains:lockmarker")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
@ -60,10 +334,6 @@ minetest.register_entity("advtrains:discouple", {
|
||||||
|
|
||||||
-- advtrains:couple
|
-- advtrains:couple
|
||||||
-- Couple entity
|
-- Couple entity
|
||||||
local function lockmarker(obj)
|
|
||||||
minetest.add_entity(obj:get_pos(), "advtrains:lockmarker")
|
|
||||||
obj:remove()
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_entity("advtrains:couple", {
|
minetest.register_entity("advtrains:couple", {
|
||||||
visual="sprite",
|
visual="sprite",
|
||||||
|
@ -75,107 +345,71 @@ minetest.register_entity("advtrains:couple", {
|
||||||
is_couple=true,
|
is_couple=true,
|
||||||
static_save = false,
|
static_save = false,
|
||||||
on_activate=function(self, staticdata)
|
on_activate=function(self, staticdata)
|
||||||
if staticdata=="COUPLE" then
|
if staticdata=="COUPLE" then
|
||||||
--couple entities have no right to exist further...
|
--couple entities have no right to exist further...
|
||||||
atprint("Couple loaded from staticdata, destroying")
|
--atdebug("Couple loaded from staticdata, destroying")
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.object:set_armor_groups({immmortal=1})
|
self.object:set_armor_groups({immmortal=1})
|
||||||
end,
|
end,
|
||||||
get_staticdata=function(self) return "COUPLE" end,
|
get_staticdata=function(self) return "COUPLE" end,
|
||||||
on_rightclick=function(self, clicker)
|
on_rightclick=function(self, clicker)
|
||||||
if not self.train_id_1 or not self.train_id_2 then return end
|
if not self.train_id_1 or not self.train_id_2 then return end
|
||||||
|
|
||||||
local pname=clicker
|
|
||||||
if type(clicker)~="string" then pname=clicker:get_player_name() end
|
|
||||||
|
|
||||||
if advtrains.safe_couple_trains(self.train_id_1, self.train_id_2, self.t1_is_front, self.t2_is_front, pname) then
|
|
||||||
self.object:remove()
|
|
||||||
else
|
|
||||||
lockmarker(self.object)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_step=function(self, dtime)
|
|
||||||
if advtrains.wagon_outside_range(self.object:getpos()) then
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.train_id_1 or not self.train_id_2 then atprint("Couple: train ids not set!") self.object:remove() return end
|
local pname=clicker
|
||||||
local train1=advtrains.trains[self.train_id_1]
|
if type(clicker)~="string" then pname=clicker:get_player_name() end
|
||||||
local train2=advtrains.trains[self.train_id_2]
|
|
||||||
if not train1 or not train2 then
|
local train1=advtrains.trains[self.train_id_1]
|
||||||
atprint("Couple: trains missing, destroying")
|
local train2=advtrains.trains[self.train_id_2]
|
||||||
self.object:remove()
|
|
||||||
return
|
advtrains.safe_couple_trains(train1, self.t1_is_front, train2, self.t2_is_front, pname)
|
||||||
end
|
self.object:remove()
|
||||||
|
|
||||||
--shh, silence here, this is an on-step callback!
|
|
||||||
if not advtrains.train_ensure_init(self.train_id_1, train1) then
|
|
||||||
--atwarn("Train",self.train_id_1,"is not initialized! Operation aborted!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if not advtrains.train_ensure_init(self.train_id_2, train2) then
|
|
||||||
--atwarn("Train",self.train_id_2,"is not initialized! Operation aborted!")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if train1.velocity>0 or train2.velocity>0 then
|
|
||||||
if not self.position_set then --ensures that train stands a single time before check fires. Using flag below
|
|
||||||
return
|
|
||||||
end
|
|
||||||
atprint("Couple: train is moving, destroying")
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.position_set then
|
|
||||||
local tp1
|
|
||||||
if self.t1_is_front then
|
|
||||||
tp1=advtrains.path_get_interpolated(train1, train1.index)
|
|
||||||
else
|
|
||||||
tp1=advtrains.path_get_interpolated(train1, train1.end_index)
|
|
||||||
end
|
|
||||||
local tp2
|
|
||||||
if self.t2_is_front then
|
|
||||||
tp2=advtrains.path_get_interpolated(train2, train2.index)
|
|
||||||
else
|
|
||||||
tp2=advtrains.path_get_interpolated(train2, train2.end_index)
|
|
||||||
end
|
|
||||||
local pos_median=advtrains.pos_median(tp1, tp2)
|
|
||||||
if not vector.equals(pos_median, self.object:getpos()) then
|
|
||||||
self.object:set_pos(pos_median)
|
|
||||||
end
|
|
||||||
self.position_set=true
|
|
||||||
end
|
|
||||||
advtrains.atprint_context_tid=nil
|
|
||||||
end,
|
end,
|
||||||
})
|
|
||||||
minetest.register_entity("advtrains:lockmarker", {
|
|
||||||
visual="sprite",
|
|
||||||
textures = {"advtrains_cpl_lock.png"},
|
|
||||||
collisionbox = {-0.3,-0.3,-0.3, 0.3,0.3,0.3},
|
|
||||||
visual_size = {x=0.7, y=0.7},
|
|
||||||
initial_sprite_basepos = {x=0, y=0},
|
|
||||||
|
|
||||||
is_lockmarker=true,
|
|
||||||
static_save = false,
|
|
||||||
on_activate=function(self, staticdata)
|
|
||||||
if staticdata=="COUPLE" then
|
|
||||||
--couple entities have no right to exist further...
|
|
||||||
atprint("Couple loaded from staticdata, destroying")
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self.object:set_armor_groups({immmortal=1})
|
|
||||||
self.life=5
|
|
||||||
end,
|
|
||||||
get_staticdata=function(self) return "COUPLE" end,
|
|
||||||
on_step=function(self, dtime)
|
on_step=function(self, dtime)
|
||||||
self.life=(self.life or 5)-dtime
|
if advtrains.wagon_outside_range(self.object:getpos()) then
|
||||||
if self.life<0 then
|
--atdebug("Couple Removing outside range")
|
||||||
self.object:remove()
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.train_id_1 or not self.train_id_2 then
|
||||||
|
--atdebug("Couple Removing ids missing")
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local train1=advtrains.trains[self.train_id_1]
|
||||||
|
local train2=advtrains.trains[self.train_id_2]
|
||||||
|
if not train1 or not train2 then
|
||||||
|
--atdebug("Couple Removing trains missing")
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.position_set and train1.velocity>0 or train2.velocity>0 then
|
||||||
|
--atdebug("Couple: train is moving, destroying")
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.position_set then
|
||||||
|
local tp1
|
||||||
|
if self.t1_is_front then
|
||||||
|
tp1=advtrains.path_get_interpolated(train1, train1.index)
|
||||||
|
else
|
||||||
|
tp1=advtrains.path_get_interpolated(train1, train1.end_index)
|
||||||
|
end
|
||||||
|
local tp2
|
||||||
|
if self.t2_is_front then
|
||||||
|
tp2=advtrains.path_get_interpolated(train2, train2.index)
|
||||||
|
else
|
||||||
|
tp2=advtrains.path_get_interpolated(train2, train2.end_index)
|
||||||
|
end
|
||||||
|
local pos_median=advtrains.pos_median(tp1, tp2)
|
||||||
|
if not vector.equals(pos_median, self.object:getpos()) then
|
||||||
|
self.object:set_pos(pos_median)
|
||||||
|
end
|
||||||
|
self.position_set=true
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|
|
@ -417,3 +417,42 @@ function advtrains.path_lookup(train, pos)
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Projects the path of "train" onto the path of "onto_train_id", and returns the index on onto_train's path
|
||||||
|
-- that corresponds to "index" on "train"'s path, as well as whether both trains face each other
|
||||||
|
-- index may be fractional
|
||||||
|
-- returns: res_index, trains_facing
|
||||||
|
-- returns nil when path can not be projected, either because trains are on different tracks or
|
||||||
|
-- node at "index" happens to be on a turnout and it's the wrong direction
|
||||||
|
-- Note - duplicate with similar functionality is in train_step_b() - that code combines train detection with projecting
|
||||||
|
function advtrains.path_project(train, index, onto_train_id)
|
||||||
|
local base_idx = atfloor(index)
|
||||||
|
local frac_part = index - base_idx
|
||||||
|
local base_pos = advtrains.path_get(train, base_idx)
|
||||||
|
local base_cn = train.path_cn[base_idx]
|
||||||
|
local otrn = advtrains.trains[onto_train_id]
|
||||||
|
-- query occupation
|
||||||
|
local occ = advtrains.occ.get_trains_over(base_pos)
|
||||||
|
-- is wanted train id contained?
|
||||||
|
local ob_idx = occ[onto_train_id]
|
||||||
|
if not ob_idx then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- retrieve other train's cn and cp
|
||||||
|
local ocn = otrn.path_cn[ob_idx]
|
||||||
|
local ocp = otrn.path_cp[ob_idx]
|
||||||
|
|
||||||
|
if base_cn == ocn then
|
||||||
|
-- same direction
|
||||||
|
return ob_idx + frac_part, false
|
||||||
|
elseif base_cn == ocp then
|
||||||
|
-- facing trains - subtract index frac
|
||||||
|
return ob_idx - frac_part, true
|
||||||
|
else
|
||||||
|
-- same path item but no common connections - deny
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@ minetest.register_on_joinplayer(function(player)
|
||||||
advtrains.hhud[player:get_player_name()] = nil
|
advtrains.hhud[player:get_player_name()] = nil
|
||||||
--independent of this, cause all wagons of the train which are loaded to reattach their players
|
--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()
|
--needed because already loaded wagons won't call reattach_all()
|
||||||
|
local pname = player:get_player_name()
|
||||||
local id=advtrains.player_to_train_mapping[pname]
|
local id=advtrains.player_to_train_mapping[pname]
|
||||||
if id then
|
if id then
|
||||||
for _,wagon in pairs(minetest.luaentities) do
|
for _,wagon in pairs(minetest.luaentities) do
|
||||||
|
@ -394,7 +395,7 @@ function advtrains.train_step_b(id, train, dtime)
|
||||||
local back_off_track=train.end_index<train.path_trk_b
|
local back_off_track=train.end_index<train.path_trk_b
|
||||||
train.off_track = front_off_track or back_off_track
|
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")
|
--atprint("in train_step_b: applying back_off_track")
|
||||||
sit_v_cap = 1
|
sit_v_cap = 1
|
||||||
elseif front_off_track then
|
elseif front_off_track then
|
||||||
|
@ -587,12 +588,76 @@ function advtrains.train_step_b(id, train, dtime)
|
||||||
else
|
else
|
||||||
--atprint("in train_step_b: movement calculation reusing from LZB newindex=",new_index_curr_tv)
|
--atprint("in train_step_b: movement calculation reusing from LZB newindex=",new_index_curr_tv)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if the zeroappr mechanism has hit, go no further than zeroappr index
|
-- 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
|
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)
|
--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
|
new_index_curr_tv = lzb_next_zero_barrier
|
||||||
end
|
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.get_trains_over(base_pos)
|
||||||
|
-- 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)
|
||||||
|
--atdebug("Backprojected our_index",our_index)
|
||||||
|
if our_index and our_index <= new_index_curr_tv then
|
||||||
|
-- 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
|
train.index = new_index_curr_tv
|
||||||
|
|
||||||
recalc_end_index(train)
|
recalc_end_index(train)
|
||||||
|
@ -638,21 +703,39 @@ function advtrains.train_step_c(id, train, dtime)
|
||||||
advtrains.spawn_wagons(id)
|
advtrains.spawn_wagons(id)
|
||||||
train.check_trainpartload=2
|
train.check_trainpartload=2
|
||||||
end
|
end
|
||||||
|
|
||||||
--- 8. check for collisions with other trains and damage players ---
|
|
||||||
|
|
||||||
local train_moves=(train.velocity~=0)
|
local train_moves=(train.velocity~=0)
|
||||||
|
|
||||||
--- Check whether this train can be coupled to another, and set couple entities accordingly
|
--- On-track collision handling - detected in train_step_b, but handled here so all other train movements have already happened.
|
||||||
if not train.was_standing and not train_moves then
|
if train.ontrack_collision_info then
|
||||||
advtrains.train_check_couples(train)
|
train.velocity = 0
|
||||||
|
train.acceleration = 0
|
||||||
|
advtrains.atc.train_reset_command(train)
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
|
||||||
|
train.ontrack_collision_info = nil
|
||||||
|
train.couples_up_to_date = true
|
||||||
end
|
end
|
||||||
train.was_standing = not train_moves
|
|
||||||
|
-- handle couples if on_track collision handling did not fire
|
||||||
if train_moves then
|
if train_moves then
|
||||||
|
train.couples_up_to_date = nil
|
||||||
|
elseif not train.couples_up_to_date then
|
||||||
|
advtrains.train_check_couples(train) -- no guarantee for train order here
|
||||||
|
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 collided = false
|
||||||
local coll_grace=1
|
local coll_grace=2
|
||||||
local collindex = advtrains.path_get_index_by_offset(train, train.index, -coll_grace)
|
local collindex = advtrains.path_get_index_by_offset(train, train.index, -coll_grace)
|
||||||
local collpos = advtrains.path_get(train, atround(collindex))
|
local collpos = advtrains.path_get(train, atround(collindex))
|
||||||
if collpos then
|
if collpos then
|
||||||
|
@ -666,8 +749,8 @@ function advtrains.train_step_c(id, train, dtime)
|
||||||
|
|
||||||
local col_tr = advtrains.occ.check_collision(testpos, id)
|
local col_tr = advtrains.occ.check_collision(testpos, id)
|
||||||
if col_tr then
|
if col_tr then
|
||||||
advtrains.train_check_couples(train)
|
|
||||||
train.velocity = 0
|
train.velocity = 0
|
||||||
|
train.acceleration = 0
|
||||||
advtrains.atc.train_reset_command(train)
|
advtrains.atc.train_reset_command(train)
|
||||||
collided = true
|
collided = true
|
||||||
end
|
end
|
||||||
|
@ -899,7 +982,7 @@ function advtrains.remove_train(id)
|
||||||
|
|
||||||
run_callbacks_remove(id, train)
|
run_callbacks_remove(id, train)
|
||||||
|
|
||||||
advtrains.path_invalidate(train)
|
advtrains.path_invalidate(train, true)
|
||||||
advtrains.couple_invalidate(train)
|
advtrains.couple_invalidate(train)
|
||||||
|
|
||||||
local tp = train.trainparts
|
local tp = train.trainparts
|
||||||
|
@ -1019,53 +1102,6 @@ function advtrains.spawn_wagons(train_id)
|
||||||
end
|
end
|
||||||
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)
|
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.
|
-- this function splits a train at index, creating a new train from the back part of the train.
|
||||||
|
|
||||||
|
@ -1121,167 +1157,6 @@ function advtrains.split_train_at_index(train, index)
|
||||||
|
|
||||||
end
|
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)
|
function advtrains.invert_train(train_id)
|
||||||
local train=advtrains.trains[train_id]
|
local train=advtrains.trains[train_id]
|
||||||
|
|
||||||
|
|
|
@ -1240,70 +1240,6 @@ function wagon:reattach_all()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_twagon_owner(train, b_first, pname)
|
|
||||||
local wtp = b_first and 1 or #train.trainparts
|
|
||||||
local wid = train.trainparts[wtp]
|
|
||||||
local wdata = advtrains.wagons[wid]
|
|
||||||
if wdata then
|
|
||||||
return advtrains.check_driving_couple_protection(pname, wdata.owner, wdata.whitelist)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function advtrains.safe_couple_trains(id1, id2, t1f, t2f, pname, try_run,v1,v2)
|
|
||||||
|
|
||||||
if pname and not minetest.check_player_privs(pname, "train_operator") then
|
|
||||||
minetest.chat_send_player(pname, "Missing train_operator privilege")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local train1=advtrains.trains[id1]
|
|
||||||
local train2=advtrains.trains[id2]
|
|
||||||
|
|
||||||
if not advtrains.train_ensure_init(id1, train1)
|
|
||||||
or not advtrains.train_ensure_init(id2, train2) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
local wck_t1, wck_t2
|
|
||||||
if pname then
|
|
||||||
wck_t1 = check_twagon_owner(train1, t1f, pname)
|
|
||||||
wck_t2 = check_twagon_owner(train2, t2f, pname)
|
|
||||||
end
|
|
||||||
if (wck_t1 or wck_t2) or not pname then
|
|
||||||
if not v1 then
|
|
||||||
v1 = 0
|
|
||||||
end
|
|
||||||
if not v2 then
|
|
||||||
v2 = 0
|
|
||||||
end
|
|
||||||
if try_run then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
if t1f then
|
|
||||||
if t2f then
|
|
||||||
v1 = -v1
|
|
||||||
advtrains.invert_train(id1)
|
|
||||||
advtrains.do_connect_trains(id1, id2, v1+v2)
|
|
||||||
else
|
|
||||||
advtrains.do_connect_trains(id2, id1, v1+v2)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if t2f then
|
|
||||||
advtrains.do_connect_trains(id1, id2, v1+v2)
|
|
||||||
else
|
|
||||||
v2 = -v2
|
|
||||||
advtrains.invert_train(id2)
|
|
||||||
advtrains.do_connect_trains(id1, id2, v1+v2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
minetest.chat_send_player(pname, "You must be authorized for at least one wagon.")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function advtrains.safe_decouple_wagon(w_id, pname, try_run)
|
function advtrains.safe_decouple_wagon(w_id, pname, try_run)
|
||||||
if not minetest.check_player_privs(pname, "train_operator") then
|
if not minetest.check_player_privs(pname, "train_operator") then
|
||||||
minetest.chat_send_player(pname, "Missing train_operator privilege")
|
minetest.chat_send_player(pname, "Missing train_operator privilege")
|
||||||
|
|
Loading…
Reference in New Issue