diff --git a/advtrains/tracks.lua b/advtrains/tracks.lua index fa7f84d..3771090 100644 --- a/advtrains/tracks.lua +++ b/advtrains/tracks.lua @@ -430,20 +430,71 @@ end advtrains.detector = {} advtrains.detector.on_node = {} -function advtrains.detector.enter_node(pos, train_id) +--Returns true when position is occupied by a train other than train_id, false when occupied by the same train as train_id and nil in case there's no train at all +function advtrains.detector.occupied(pos, train_id) local ppos=advtrains.round_vector_floor_y(pos) local pts=minetest.pos_to_string(ppos) - advtrains.detector.on_node[pts]=train_id + local s=advtrains.detector.on_node[pts] + if not s then return nil end + if s==train_id then return false end + --in case s is a table, it's always occupied by another train + return true +end +-- If given a train id as second argument, this is considered as 'not there'. +-- Returns the train id of (one of, nondeterministic) the trains at this position +function advtrains.detector.get(pos, train_id) + local ppos=advtrains.round_vector_floor_y(pos) + local pts=minetest.pos_to_string(ppos) + local s=advtrains.detector.on_node[pts] + if not s then return nil end + if type(s)=="table" then + for _,t in ipairs(s) do + if t~=train_id then return t end + end + return nil + end + return s +end + +function advtrains.detector.enter_node(pos, train_id) + advtrains.detector.stay_node(pos, train_id) + local ppos=advtrains.round_vector_floor_y(pos) advtrains.detector.call_enter_callback(ppos, train_id) end function advtrains.detector.leave_node(pos, train_id) local ppos=advtrains.round_vector_floor_y(pos) + local pts=minetest.pos_to_string(ppos) + local s=advtrains.detector.on_node[pts] + if type(s)=="table" then + local i + for j,t in ipairs(s) do + if t==train_id then i=j end + end + if not i then return end + s=table.remove(s,i) + if #s==0 then + s=nil + elseif #s==1 then + s=s[1] + end + advtrains.detector.on_node[pts]=s + else + advtrains.detector.on_node[pts]=nil + end advtrains.detector.call_leave_callback(ppos, train_id) end function advtrains.detector.stay_node(pos, train_id) local ppos=advtrains.round_vector_floor_y(pos) local pts=minetest.pos_to_string(ppos) - advtrains.detector.on_node[pts]=train_id + + local s=advtrains.detector.on_node[pts] + if not s then + advtrains.detector.on_node[pts]=train_id + elseif type(s)=="string" then + advtrains.detector.on_node[pts]={s, train_id} + elseif type(s)=="table" then + advtrains.detector.on_node[pts]=table.insert(s, train_id) + end end diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua index 64b40f1..228c3a0 100644 --- a/advtrains/trainlogic.lua +++ b/advtrains/trainlogic.lua @@ -394,15 +394,12 @@ function advtrains.train_step_a(id, train, dtime) --- 6b. call stay_node to register trains in the location table - actual enter_node stuff is done in step b --- - local ifn, ibn = atround(train.index), atround(train.end_index) + local ifo, ibo = train.detector_old_index, train.detector_old_end_index local path=train.path - for i=ibn, ifn do + for i=ibo, ifo do if path[i] then - local pts=minetest.pos_to_string(path[i]) - if not (advtrains.detector.on_node[pts] and advtrains.detector.on_node[pts]~=id) then - advtrains.detector.stay_node(path[i], id) - end + advtrains.detector.stay_node(path[i], id) end end @@ -519,13 +516,12 @@ function advtrains.train_step_b(id, train, dtime) if ifn>ifo then for i=ifo+1, ifn do if path[i] then - local pts=minetest.pos_to_string(path[i]) - if advtrains.detector.on_node[pts] and advtrains.detector.on_node[pts]~=id then + if advtrains.detector.occupied(path[i], id) then --if another train has signed up for this position first, it won't be recognized in train_step_b. So do collision here. - atprint("Collision detected in enter_node callbacks (front) @",pts,"with",sid(advtrains.detector.on_node[pts])) - advtrains.collide_and_spawn_couple(id, path[i], advtrains.detector.on_node[pts], false) + atprint("Collision detected in enter_node callbacks (front) @",path[i],"with",sid(advtrains.detector.get(path[i], id))) + advtrains.collide_and_spawn_couple(id, path[i], advtrains.detector.get(path[i], id), false) end - atprint("enter_node (front) @index",i,"@",pts,"on_node",sid(advtrains.detector.on_node[pts])) + atprint("enter_node (front) @index",i,"@",path[i],"on_node",sid(advtrains.detector.get(path[i], id))) advtrains.detector.enter_node(path[i], id) end end @@ -540,12 +536,12 @@ function advtrains.train_step_b(id, train, dtime) for i=ibn, ibo-1 do if path[i] then local pts=minetest.pos_to_string(path[i]) - if advtrains.detector.on_node[pts] and advtrains.detector.on_node[pts]~=id then + if advtrains.detector.occupied(path[i], id) then --if another train has signed up for this position first, it won't be recognized in train_step_b. So do collision here. - atprint("Collision detected in enter_node callbacks (back) @",pts,"on_node",sid(advtrains.detector.on_node[pts])) - advtrains.collide_and_spawn_couple(id, path[i], advtrains.detector.on_node[pts], true) + atprint("Collision detected in enter_node callbacks (back) @",path[i],"with",sid(advtrains.detector.get(path[i], id))) + advtrains.collide_and_spawn_couple(id, path[i], advtrains.detector.get(path[i], id), true) end - atprint("enter_node (back) @index",i,"@",pts,"with",sid(advtrains.detector.on_node[pts])) + atprint("enter_node (back) @index",i,"@",path[i],"on_node",sid(advtrains.detector.get(path[i], id))) advtrains.detector.enter_node(path[i], id) end end @@ -576,15 +572,14 @@ function advtrains.train_step_b(id, train, dtime) for z=-1,1 do local testpos=vector.add(rcollpos, {x=x, y=0, z=z}) --- 8a Check collision --- - local testpts=minetest.pos_to_string(testpos) - if advtrains.detector.on_node[testpts] and advtrains.detector.on_node[testpts]~=id then + if advtrains.detector.occupied(testpos, id) then --collides - advtrains.collide_and_spawn_couple(id, testpos, advtrains.detector.on_node[testpts], train.movedir==-1) + advtrains.collide_and_spawn_couple(id, testpos, advtrains.detector.get(testpos, id), train.movedir==-1) end --- 8b damage players --- if not minetest.settings:get_bool("creative_mode") then local player=advtrains.playersbypts[testpts] - if player and train.velocity>3 then + if player and not minetest.check_player_privs(player, "creative") and train.velocity>3 then --instantly kill player --drop inventory contents first, to not to spawn bones local player_inv=player:get_inventory() @@ -716,8 +711,8 @@ function advtrains.split_train_at_wagon(wagon) if not train.path then return end local real_pos_in_train=advtrains.get_real_path_index(train, wagon.pos_in_train) - local pos_for_new_train=train.path[math.floor(real_pos_in_train+wagon.wagon_span+1)] - local pos_for_new_train_prev=train.path[math.floor(real_pos_in_train+wagon.wagon_span)] + local pos_for_new_train=train.path[math.floor(real_pos_in_train+wagon.wagon_span)] + local pos_for_new_train_prev=train.path[math.floor(real_pos_in_train+wagon.wagon_span-1)] --before doing anything, check if both are rails. else do not allow if not pos_for_new_train then @@ -930,8 +925,7 @@ function advtrains.invert_train(train_id) end function advtrains.get_train_at_pos(pos) - local ph=minetest.pos_to_string(advtrains.round_vector_floor_y(pos)) - return advtrains.detector.on_node[ph] + return advtrains.detector.get(pos) end function advtrains.invalidate_all_paths(pos) diff --git a/advtrains_itrainmap/init.lua b/advtrains_itrainmap/init.lua index 756f1da..0443609 100644 --- a/advtrains_itrainmap/init.lua +++ b/advtrains_itrainmap/init.lua @@ -52,7 +52,7 @@ local function create_map_form(d) if x>=minx and x<=maxx then for z,y in pairs(itx) do if z>=minz and z<=maxz then - local adn=advtrains.detector.on_node[minetest.pos_to_string({x=x, y=y, z=z})] + local adn=advtrains.detector.get({x=x, y=y, z=z}) local color="gray" if adn then color="red" diff --git a/advtrains_luaautomation/atc_rail.lua b/advtrains_luaautomation/atc_rail.lua index 1fae28d..b90baf8 100644 --- a/advtrains_luaautomation/atc_rail.lua +++ b/advtrains_luaautomation/atc_rail.lua @@ -24,7 +24,7 @@ function r.fire_event(pos, evtdata) --prepare ingame API for ATC. Regenerate each time since pos needs to be known --If no train, then return false. - local train_id=advtrains.detector.on_node[ph] + local train_id=advtrains.detector.get(pos) local train, atc_arrow, tvel if train_id then train=advtrains.trains[train_id] end if train then