Implement collisions. (does not work yet, still code errors)

master
orwell96 2018-05-17 12:30:30 +02:00
parent b420a71939
commit 5dca155333
4 changed files with 225 additions and 217 deletions

View File

@ -61,15 +61,12 @@ minetest.register_entity("advtrains:discouple", {
end,
})
--advtrains:couple
--when two trains overlap with their end-positions, this entity will be spawned and both trains set its id into appropiate fields for them to know when to free them again. The entity will destroy automatically when it recognizes that any of the trains left the common position.
--[[fields
train_id_1
train_id_2
train1_is_backpos
train2_is_backpos
]]
-- advtrains:couple
-- Couple entity
local function lockmarker(obj)
minetest.spawn_entity(obj:get_pos(), "advtrains.lockmarker")
obj:remove()
end
minetest.register_entity("advtrains:couple", {
visual="sprite",
@ -99,30 +96,61 @@ minetest.register_entity("advtrains:couple", {
if type(clicker)~="string" then pname=clicker:get_player_name() end
if not minetest.check_player_privs(pname, "train_operator") then return end
local train1=advtrains.trains[self.train_id_1]
local train2=advtrains.trains[self.train_id_2]
advtrains.train_ensure_init(self.train_id_1, train1)
advtrains.train_ensure_init(self.train_id_2, train2)
local id1, id2=self.train_id_1, self.train_id_2
if self.train1_is_backpos and not self.train2_is_backpos then
advtrains.do_connect_trains(id1, id2, clicker)
--case 2 (second train is front)
elseif self.train2_is_backpos and not self.train1_is_backpos then
advtrains.do_connect_trains(id2, id1, clicker)
--case 3
elseif self.train1_is_backpos and self.train2_is_backpos then
advtrains.invert_train(id2)
advtrains.do_connect_trains(id1, id2, clicker)
--case 4
elseif not self.train1_is_backpos and not self.train2_is_backpos then
advtrains.invert_train(id1)
local bp1, bp2 = self.t1_is_backpos, self.t2_is_backpos
if not bp1 then
if train1.couple_lock_front then
lockmarker(self.object)
return
end
if not bp2 then
if train2.couple_lock_front then
lockmarker(self.object)
return
end
advtrains.invert_train(id1)
advtrains.do_connect_trains(id1, id2, clicker)
else
if train2.couple_lock_back then
lockmarker(self.object)
return
end
advtrains.do_connect_trains(id2, id1, clicker)
end
else
if train1.couple_lock_back then
lockmarker(self.object)
return
end
if not bp2 then
if train2.couple_lock_front then
lockmarker(self.object)
return
end
advtrains.do_connect_trains(id1, id2, clicker)
else
if train2.couple_lock_back then
lockmarker(self.object)
return
end
advtrains.invert_train(id2)
advtrains.do_connect_trains(id1, id2, clicker)
end
end
atprint("Coupled trains", id1, id2)
self.object:remove()
end)
end,
on_step=function(self, dtime)
return advtrains.pcall(function()
advtrains.atprint_context_tid=sid(self.train_id_1)
advtrains.atprint_context_tid_full=self.train_id_1
local t=os.clock()
advtrains.atprint_context_tid=self.train_id_1
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 train1=advtrains.trains[self.train_id_1]
local train2=advtrains.trains[self.train_id_2]
@ -131,10 +159,9 @@ minetest.register_entity("advtrains:couple", {
self.object:remove()
return
end
if not train1.path or not train2.path or not train1.index or not train2.index or not train1.end_index or not train2.end_index then
atprint("Couple: paths or end_index missing. Might happen when paths got cleared")
return
end
advtrains.train_ensure_init(self.train_id_1, train1)
advtrains.train_ensure_init(self.train_id_2, train2)
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
@ -148,15 +175,15 @@ minetest.register_entity("advtrains:couple", {
if not self.position_set then
local tp1
if not self.train1_is_backpos then
tp1=advtrains.get_real_index_position(train1.path, train1.index)
tp1=advtrains.path_get_interpolated(train1, train1.index)
else
tp1=advtrains.get_real_index_position(train1.path, train1.end_index)
tp1=advtrains.path_get_interpolated(train1, train1.end_index)
end
local tp2
if not self.train2_is_backpos then
tp2=advtrains.get_real_index_position(train2.path, train2.index)
tp2=advtrains.path_get_interpolated(train2, train2.index)
else
tp2=advtrains.get_real_index_position(train2.path, train2.end_index)
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
@ -166,7 +193,7 @@ minetest.register_entity("advtrains:couple", {
end
atprintbm("couple step", t)
advtrains.atprint_context_tid=nil
advtrains.atprint_context_tid_full=nil
end)
end,
})

View File

@ -105,6 +105,7 @@ atwarn=function(t, ...)
end
sid=function(id) if id then return string.sub(id, -6) end end
--ONLY use this function for temporary debugging. for consistent debug prints use atprint
atdebug=function(t, ...)
local text=advtrains.print_concat_table({t, ...})
@ -124,6 +125,12 @@ if minetest.settings:get_bool("advtrains_enable_debugging") then
dofile(advtrains.modpath.."/debugringbuffer.lua")
end
function assertt(var, typ)
if type(var)~=typ then
error("Assertion failed, variable has to be of type "..typ)
end
end
dofile(advtrains.modpath.."/helpers.lua");
--dofile(advtrains.modpath.."/debugitems.lua");
@ -196,6 +203,7 @@ function advtrains.avt_load()
--remove wagon_save entries that are not part of a train
local todel=advtrains.merge_tables(advtrains.wagon_save)
for tid, train in pairs(advtrains.trains) do
train.id = tid
for _, wid in ipairs(train.trainparts) do
todel[wid]=nil
end

View File

@ -145,8 +145,6 @@ function o.clear_item(train_id, pos)
atwarn("Duplicate occupation entry at",pos,"for train",train_id,":",t)
i = i - 2
end
local oldoid = t[i+1] or 0
addchg(pos, train_id, oldoid, 0)
moving = true
end
if moving then
@ -167,7 +165,7 @@ function o.check_collision(pos, train_id)
if t[i]~=train_id then
local idx = t[i+1]
local train = advtrains.trains[train_id]
advtrains.train_ensure_clean(train_id, train)
advtrains.train_ensure_init(train_id, train)
if idx >= train.end_index and idx <= train.index then
return true
end
@ -177,4 +175,27 @@ function o.check_collision(pos, train_id)
return false
end
-- Gets a mapping of train id's to indexes of trains that share this path item with this train
-- The train itself will not be included.
-- If the requested index position is off-track, returns {}.
-- returns (table with train_id->index), position
function o.get_occupations(train, index)
local ppos, ontrack = advtrains.path_get(train, index)
if not ontrack then
atdebug("Train",train.id,"get_occupations requested off-track",index)
return {}, pos
end
local pos = advtrains.round_vector_floor_y(ppos)
local t = occget(pos)
local r = {}
local i = 1
local train_id = train.id
while t[i] do
if t[i]~=train_id then
r[train_id] = t[i+1]
end
i = i + 2
end
return r, pos
end
advtrains.occ = o

View File

@ -69,26 +69,20 @@ advtrains.mainloop_trainlogic=function(dtime)
for k,v in pairs(advtrains.trains) do
advtrains.atprint_context_tid=sid(k)
advtrains.atprint_context_tid_full=k
advtrains.train_ensure_clean(k, v)
advtrains.train_ensure_init(k, v)
end
for k,v in pairs(advtrains.trains) do
advtrains.atprint_context_tid=sid(k)
advtrains.atprint_context_tid_full=k
advtrains.train_step_b(k, v, dtime)
end
for k,v in pairs(advtrains.trains) do
advtrains.atprint_context_tid=sid(k)
advtrains.atprint_context_tid_full=k
advtrains.train_step_c(k, v, dtime)
end
advtrains.atprint_context_tid=nil
advtrains.atprint_context_tid_full=nil
advtrains.occ.end_step()
atprintbm("trainsteps", t)
endstep()
@ -174,39 +168,6 @@ local function assertdef(tbl, var, def)
end
-- Calculates the indices where the window borders of the occupation windows are.
-- TODO adapt this code to new system, probably into a callback
local function calc_occwindows(id, train)
local end_index = advtrains.path_get_index_by_offset(train, train.index, -train.trainlen)
train.end_index = end_index
local cpl_b = end_index - COUPLE_ZONE
local safety_b = advtrains.path_get_index_by_offset(train, cpl_b, -SAFETY_ZONE)
local cpl_f = end_index + COUPLE_ZONE
local safety_f = advtrains.path_get_index_by_offset(train, cpl_f, SAFETY_ZONE)
-- calculate brake distance
local acc_all = t_accel_all[1]
local acc_eng = t_accel_eng[1]
local nwagons = #train.trainparts
local acc = acc_all + (acc_eng*train.locomotives_in_train)/nwagons
local vel = train.velocity
local brakedst = (vel*vel) / (2*acc)
local brake_i = math.max(advtrains.path_get_index_by_offset(train, train.index, brakedst + BRAKE_SPACE), safety_f)
local aware_i = advtrains.path_get_index_by_offset(train, brake_i, AWARE_ZONE)
return {
safety_b,
cpl_b,
end_index,
train.index,
cpl_f,
safety_f,
brake_i,
aware_i,
}
end
-- Small local util function to recalculate train's end index
local function recalc_end_index(train)
train.end_index = advtrains.path_get_index_by_offset(train, train.index, -train.trainlen)
@ -218,11 +179,11 @@ end
local callbacks_new_path = {}
local callbacks_update = {}
function advtrains.tb_register_on_new_path(func)
function advtrains.te_register_on_new_path(func)
assertt(func, "function")
table.insert(callbacks_new_path, func)
end
function advtrains.tb_register_on_update(func)
function advtrains.te_register_on_update(func)
assertt(func, "function")
table.insert(callbacks_update, func)
end
@ -239,11 +200,11 @@ local function run_callbacks_update(id, train)
end
-- train_ensure_clean: responsible for creating a state that we can work on, after one of the following events has happened:
-- 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
-- - save files were loaded
-- Additionally, this gets called outside the step cycle to initialize and/or remove a train, then occ_write_mode is set.
function advtrains.train_ensure_clean(id, train)
function advtrains.train_ensure_init(id, train)
train.dirty = true
if train.no_step then return end
@ -451,6 +412,12 @@ if train.no_step or train.wait_for_path then return end
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)
end
train.was_standing = not train_moves
if train_moves then
local collpos
@ -529,8 +496,8 @@ end
advtrains.te_register_on_new_path(function(id, train)
train.tnc = {
old_index = atround(train.index)
old_end_index = atround(train.end_index)
old_index = atround(train.index),
old_end_index = atround(train.end_index),
}
end)
advtrains.te_register_on_update(function(id, train)
@ -550,6 +517,39 @@ advtrains.te_register_on_update(function(id, train)
end
end)
-- Calculates the indices where the window borders of the occupation windows are.
-- TODO adapt this code to new system, probably into a callback (probably only the brake distance code is needed)
local function calc_occwindows(id, train)
local end_index = advtrains.path_get_index_by_offset(train, train.index, -train.trainlen)
train.end_index = end_index
local cpl_b = end_index - COUPLE_ZONE
local safety_b = advtrains.path_get_index_by_offset(train, cpl_b, -SAFETY_ZONE)
local cpl_f = end_index + COUPLE_ZONE
local safety_f = advtrains.path_get_index_by_offset(train, cpl_f, SAFETY_ZONE)
-- calculate brake distance
local acc_all = t_accel_all[1]
local acc_eng = t_accel_eng[1]
local nwagons = #train.trainparts
local acc = acc_all + (acc_eng*train.locomotives_in_train)/nwagons
local vel = train.velocity
local brakedst = (vel*vel) / (2*acc)
local brake_i = math.max(advtrains.path_get_index_by_offset(train, train.index, brakedst + BRAKE_SPACE), safety_f)
local aware_i = advtrains.path_get_index_by_offset(train, brake_i, AWARE_ZONE)
return {
safety_b,
cpl_b,
end_index,
train.index,
cpl_f,
safety_f,
brake_i,
aware_i,
}
end
--TODO: Collisions!
@ -573,7 +573,7 @@ function advtrains.create_new_train_at(pos, connid, ioff, trainparts)
atdebug("Created new train:",t)
advtrains.train_ensure_clean(new_id, advtrains.trains[new_id])
advtrains.train_ensure_init(new_id, advtrains.trains[new_id])
return new_id
end
@ -581,7 +581,7 @@ end
function advtrains.remove_train(id)
local train = advtrains.trains[id]
advtrains.train_ensure_clean(id, train)
advtrains.train_ensure_init(id, train)
advtrains.path_invalidate(train)
@ -597,7 +597,7 @@ end
function advtrains.add_wagon_to_train(wagon_id, train_id, index)
local train=advtrains.trains[train_id]
advtrains.train_ensure_clean(train_id, train)
advtrains.train_ensure_init(train_id, train)
if index then
table.insert(train.trainparts, index, wagon_id)
@ -691,7 +691,7 @@ function advtrains.split_train_at_wagon(wagon_id)
local train=advtrains.trains[old_id]
local _, wagon = advtrains.get_wagon_prototype(data)
advtrains.train_ensure_clean(old_id, train)
advtrains.train_ensure_init(old_id, train)
local index=advtrains.path_get_index_by_offset(train, train.index, -(data.pos_in_train + wagon.wagon_span))
@ -727,6 +727,77 @@ function advtrains.split_train_at_wagon(wagon_id)
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 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=train1.id
le.train_id_2=train2.id
le.train1_is_backpos=not t1_is_front
le.train2_is_backpos=not t2_is_front
end
function advtrains.train_check_couples(train)
if train.cpl_front then
if not train.cpl_front:getyaw() 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)
for tid, idx in pairs(front_trains) do
local other_train = advtrains.trains[tid]
advtrains.train_ensure_init(tid, other_train)
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
if train.cpl_back then
if not train.cpl_back:getyaw() 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)
for tid, idx in pairs(back_trains) do
local other_train = advtrains.trains[tid]
advtrains.train_ensure_init(tid, other_train)
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
-- 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
@ -735,132 +806,16 @@ end
--4. R<->F F<->R flip one of these trains and take it as new parent
--->frontpos's will match
--true when trains are facing each other. needed on colliding.
-- check done by iterating paths and checking their direction
--returns nil when not on the same track at all OR when required path items are not generated. this distinction may not always be needed.
-- TODO Will be changed when implementing coupling.
function advtrains.trains_facing(train1, train2)
local sr_pos=train1.path[atround(train1.index)]
local sr_pos_p=train1.path[atround(train1.index)-1]
for i=advtrains.minN(train2.path), advtrains.maxN(train2.path) do
if vector.equals(sr_pos, train2.path[i]) then
if train2.path[i+1] and vector.equals(sr_pos_p, train2.path[i+1]) then return true end
if train2.path[i-1] and vector.equals(sr_pos_p, train2.path[i-1]) then return false end
return nil
end
end
return nil
end
function advtrains.collide_and_spawn_couple(id1, pos, id2, t1_is_backpos)
if minetest.settings:get_bool("advtrains_disable_collisions") then
return
end
atprint("COLLISION: ",sid(id1)," and ",sid(id2)," at ",pos,", t1_is_backpos=",(t1_is_backpos and "true" or "false"))
--TODO:
local train1=advtrains.trains[id1]
-- do collision
train1.recently_collided_with_env=true
train1.velocity=0.5*train1.velocity
train1.movedir=train1.movedir*-1
train1.tarvelocity=0
local train2=advtrains.trains[id2]
if not train1 or not train2 then return end
local found
for i=advtrains.minN(train1.path), advtrains.maxN(train1.path) do
if vector.equals(train1.path[i], pos) then
found=true
end
end
if not found then
atprint("Err: pos not in path. Not spawning a couple")
return
end
local frontpos2=train2.path[atround(train2.detector_old_index)]
local backpos2=train2.path[atround(train2.detector_old_end_index)]
local t2_is_backpos
atprint("End positions: ",frontpos2,backpos2)
t2_is_backpos = vector.distance(backpos2, pos) < vector.distance(frontpos2, pos)
atprint("t2_is_backpos="..(t2_is_backpos and "true" or "false"))
local t1_has_couple, t1_couple_lck
if t1_is_backpos then
t1_has_couple=train1.couple_eid_back
t1_couple_lck=train1.couple_lck_back
else
t1_has_couple=train1.couple_eid_front
t1_couple_lck=train1.couple_lck_front
end
local t2_has_couple, t2_couple_lck
if t2_is_backpos then
t2_has_couple=train2.couple_eid_back
t2_couple_lck=train2.couple_lck_back
else
t2_has_couple=train2.couple_eid_front
t2_couple_lck=train2.couple_lck_front
end
if t1_has_couple then
if minetest.object_refs[t1_has_couple] then minetest.object_refs[t1_has_couple]:remove() end
end
if t2_has_couple then
if minetest.object_refs[t2_has_couple] then minetest.object_refs[t2_has_couple]:remove() end
end
if t1_couple_lck or t2_couple_lck then
minetest.add_entity(pos, "advtrains:lockmarker")
return
end
local obj=minetest.add_entity(pos, "advtrains:couple")
if not obj then atprint("failed creating object") return end
local le=obj:get_luaentity()
le.train_id_1=id1
le.train_id_2=id2
le.train1_is_backpos=t1_is_backpos
le.train2_is_backpos=t2_is_backpos
--find in object_refs
local p_aoi
for aoi, compare in pairs(minetest.object_refs) do
if compare==obj then
if t1_is_backpos then
train1.couple_eid_back=aoi
else
train1.couple_eid_front=aoi
end
if t2_is_backpos then
train2.couple_eid_back=aoi
else
train2.couple_eid_front=aoi
end
p_aoi=aoi
end
end
atprint("Couple spawned (ActiveObjectID ",p_aoi,")")
end
--order of trains may be irrelevant in some cases. check opposite cases. TODO does this work?
--pos1 and pos2 are just needed to form a median.
function advtrains.do_connect_trains(first_id, second_id, player)
function advtrains.do_connect_trains(first_id, second_id)
local first, second=advtrains.trains[first_id], advtrains.trains[second_id]
if not first or not second or not first.index or not second.index or not first.end_index or not second.end_index then
return false
end
advtrains.train_ensure_init(first_id, first)
advtrains.train_ensure_init(second_id, second)
if first.couple_lck_back or second.couple_lck_front then
-- trains are ordered correctly!
if player then
minetest.chat_send_player(player:get_player_name(), "Can't couple: couples locked!")
end
-- Note, this is already checked in the rightclick step of the couple entity before trains are actually reversed
return
end
@ -870,24 +825,21 @@ function advtrains.do_connect_trains(first_id, second_id, player)
for _,v in ipairs(second.trainparts) do
table.insert(first.trainparts, v)
end
--kick it like physics (with mass being #wagons)
local new_velocity=((first.velocity*first_wagoncnt)+(second.velocity*second_wagoncnt))/(first_wagoncnt+second_wagoncnt)
local tmp_cpl_lck=second.couple_lck_back
advtrains.trains[second_id]=nil
advtrains.update_trainpart_properties(first_id)
local train1=advtrains.trains[first_id]
train1.velocity=new_velocity
train1.tarvelocity=0
train1.couple_eid_front=nil
train1.couple_eid_back=nil
train1.couple_lck_back=tmp_cpl_lck
advtrains.remove_train(second_id)
first.velocity=0
first.tarvelocity=0
first.couple_lck_back=tmp_cpl_lck
return true
end
function advtrains.invert_train(train_id)
local train=advtrains.trains[train_id]
advtrains.train_ensure_clean(train_id, train)
advtrains.train_ensure_init(train_id, train)
advtrains.path_setrestore(train, true)