Add track section concept and rework TCB design, implement new linking behavior
parent
5992618ee8
commit
e1ebbff23c
|
@ -200,6 +200,9 @@ function advtrains.avt_load()
|
||||||
advtrains.player_to_train_mapping = tbl.ptmap or {}
|
advtrains.player_to_train_mapping = tbl.ptmap or {}
|
||||||
advtrains.ndb.load_data(tbl.ndb)
|
advtrains.ndb.load_data(tbl.ndb)
|
||||||
advtrains.atc.load_data(tbl.atc)
|
advtrains.atc.load_data(tbl.atc)
|
||||||
|
if advtrains.interlocking then
|
||||||
|
--advtrains.interlocking.db.load(tbl.interlocking)
|
||||||
|
end
|
||||||
--remove wagon_save entries that are not part of a train
|
--remove wagon_save entries that are not part of a train
|
||||||
local todel=advtrains.merge_tables(advtrains.wagon_save)
|
local todel=advtrains.merge_tables(advtrains.wagon_save)
|
||||||
for tid, train in pairs(advtrains.trains) do
|
for tid, train in pairs(advtrains.trains) do
|
||||||
|
@ -291,12 +294,17 @@ advtrains.avt_save = function(remove_players_from_wagons)
|
||||||
|
|
||||||
--versions:
|
--versions:
|
||||||
-- 1 - Initial new save format.
|
-- 1 - Initial new save format.
|
||||||
|
local il_save
|
||||||
|
if advtrains.interlocking then
|
||||||
|
il_save = advtrains.interlocking.db.save()
|
||||||
|
end
|
||||||
local save_tbl={
|
local save_tbl={
|
||||||
trains = tmp_trains,
|
trains = tmp_trains,
|
||||||
wagon_save = advtrains.wagons,
|
wagon_save = advtrains.wagons,
|
||||||
ptmap = advtrains.player_to_train_mapping,
|
ptmap = advtrains.player_to_train_mapping,
|
||||||
atc = advtrains.atc.save_data(),
|
atc = advtrains.atc.save_data(),
|
||||||
ndb = advtrains.ndb.save_data(),
|
ndb = advtrains.ndb.save_data(),
|
||||||
|
interlocking = il_save,
|
||||||
version = 1,
|
version = 1,
|
||||||
}
|
}
|
||||||
local datastr = minetest.serialize(save_tbl)
|
local datastr = minetest.serialize(save_tbl)
|
||||||
|
|
|
@ -93,13 +93,20 @@ local TRAVERSER_LIMIT = 100
|
||||||
local ildb = {}
|
local ildb = {}
|
||||||
|
|
||||||
local track_circuit_breaks = {}
|
local track_circuit_breaks = {}
|
||||||
|
local track_sections = {}
|
||||||
|
|
||||||
function ildb.load(data)
|
function ildb.load(data)
|
||||||
|
if not data then return end
|
||||||
|
if data.tcbs then
|
||||||
|
track_circuit_breaks = data.tcbs
|
||||||
|
end
|
||||||
|
if data.ts then
|
||||||
|
track_sections = data.ts
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function ildb.save()
|
function ildb.save()
|
||||||
return {}
|
return {tcbs = track_circuit_breaks, ts=track_sections}
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -107,17 +114,7 @@ end
|
||||||
TCB data structure
|
TCB data structure
|
||||||
{
|
{
|
||||||
[1] = { -- Variant: with adjacent TCs.
|
[1] = { -- Variant: with adjacent TCs.
|
||||||
== Synchronized properties == Apply to the whole TC
|
ts_id = <id> -- ID of the assigned track section
|
||||||
adjacent = { <signal specifier>,... } -- Adjacent TCBs, forms a TC with these
|
|
||||||
conflict = { <signal specifier>,... } -- Conflicting TC's (chosen as a representative TCB member)
|
|
||||||
-- Used e.g. for crossing rails that do not have nodes in common (like it's currently done)
|
|
||||||
incomplete = <boolean> -- Set when the recursion counter hit during traverse. Probably needs to add
|
|
||||||
-- another tcb at some far-away place
|
|
||||||
route = {origin = <signal>, in_dir = <boolean>}
|
|
||||||
-- Set whenever a route has been set through this TC. It saves the origin tcb id and side
|
|
||||||
-- (=the origin signal). in_dir is set when the train will enter the TC from this side
|
|
||||||
|
|
||||||
== Unsynchronized properties == Apply only to this side of the TC
|
|
||||||
signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal
|
signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal
|
||||||
-- aspect will be set accordingly.
|
-- aspect will be set accordingly.
|
||||||
routetar = <signal> -- Route set from this signal. This is the entry that is cleared once
|
routetar = <signal> -- Route set from this signal. This is the entry that is cleared once
|
||||||
|
@ -128,12 +125,25 @@ TCB data structure
|
||||||
-- is set true, clearing the signal
|
-- is set true, clearing the signal
|
||||||
},
|
},
|
||||||
[2] = { -- Variant: end of track-circuited area (initial state of TC)
|
[2] = { -- Variant: end of track-circuited area (initial state of TC)
|
||||||
end_of_interlocking = true,
|
ts_id = nil, -- this is the indication for end_of_interlocking
|
||||||
section_free = <boolean>, --this can be set by an exit node via mesecons or atlatc,
|
section_free = <boolean>, --this can be set by an exit node via mesecons or atlatc,
|
||||||
-- or from the tc formspec.
|
-- or from the tc formspec.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Signal specifier (a pair of TCB/Side):
|
|
||||||
|
Track section
|
||||||
|
[id] = {
|
||||||
|
name = "Some human-readable name"
|
||||||
|
tc_breaks = { <signal specifier>,... } -- Bounding TC's (signal specifiers)
|
||||||
|
-- Can be direct ends (auto-detected), conflicting routes or TCBs that are too far away from each other
|
||||||
|
route = {origin = <signal>, from_tcb = <index>}
|
||||||
|
-- Set whenever a route has been set through this TC. It saves the origin tcb id and side
|
||||||
|
-- (=the origin signal). index is the TCB of the route origin
|
||||||
|
trains = {<id>, ...} -- Set whenever a train (or more) reside in this TC
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Signal specifier (sigd) (a pair of TCB/Side):
|
||||||
{p = <pos>, s = <1/2>}
|
{p = <pos>, s = <1/2>}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
|
@ -141,8 +151,8 @@ Signal specifier (a pair of TCB/Side):
|
||||||
--
|
--
|
||||||
function ildb.create_tcb(pos)
|
function ildb.create_tcb(pos)
|
||||||
local new_tcb = {
|
local new_tcb = {
|
||||||
[1] = {end_of_interlocking = true},
|
[1] = {},
|
||||||
[2] = {end_of_interlocking = true},
|
[2] = {},
|
||||||
}
|
}
|
||||||
local pts = advtrains.roundfloorpts(pos)
|
local pts = advtrains.roundfloorpts(pos)
|
||||||
track_circuit_breaks[pts] = new_tcb
|
track_circuit_breaks[pts] = new_tcb
|
||||||
|
@ -153,6 +163,48 @@ function ildb.get_tcb(pos)
|
||||||
return track_circuit_breaks[pts]
|
return track_circuit_breaks[pts]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ildb.get_tcbs(sigd)
|
||||||
|
local tcb = ildb.get_tcb(sigd.p)
|
||||||
|
if not tcb then return nil end
|
||||||
|
return tcb[sigd.s]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function ildb.create_ts(sigd)
|
||||||
|
local tcbs = ildb.get_tcbs(sigd)
|
||||||
|
local id = advtrains.random_id()
|
||||||
|
|
||||||
|
while track_sections[id] do
|
||||||
|
id = advtrains.random_id()
|
||||||
|
end
|
||||||
|
|
||||||
|
track_sections[id] = {
|
||||||
|
name = "Section "..id,
|
||||||
|
tc_breaks = { sigd }
|
||||||
|
}
|
||||||
|
tcbs.ts_id = id
|
||||||
|
end
|
||||||
|
|
||||||
|
function ildb.get_ts(id)
|
||||||
|
return track_sections[id]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- various helper functions handling sigd's
|
||||||
|
local function sigd_equal(sigd, cmp)
|
||||||
|
return vector.equals(sigd.p, cmp.p) and sigd.s==cmp.s
|
||||||
|
end
|
||||||
|
local function insert_sigd_nodouble(list, sigd)
|
||||||
|
for idx, cmp in pairs(list) do
|
||||||
|
if sigd_equal(sigd, cmp) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(list, sigd)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- This function will actually handle the node that is in connid direction from the node at pos
|
-- This function will actually handle the node that is in connid direction from the node at pos
|
||||||
-- so, this needs the conns of the node at pos, since these are already calculated
|
-- so, this needs the conns of the node at pos, since these are already calculated
|
||||||
local function traverser(found_tcbs, pos, conns, connid, count)
|
local function traverser(found_tcbs, pos, conns, connid, count)
|
||||||
|
@ -166,13 +218,14 @@ local function traverser(found_tcbs, pos, conns, connid, count)
|
||||||
local tcb = ildb.get_tcb(adj_pos)
|
local tcb = ildb.get_tcb(adj_pos)
|
||||||
if tcb then
|
if tcb then
|
||||||
-- done with this branch
|
-- done with this branch
|
||||||
table.insert(found_tcbs, {p=adj_pos, s=adj_connid})
|
atdebug("Traverser found tcb at",adj_pos, adj_connid)
|
||||||
|
insert_sigd_nodouble(found_tcbs, {p=adj_pos, s=adj_connid})
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- recursion abort condition
|
-- recursion abort condition
|
||||||
if count > TRAVERSER_LIMIT then
|
if count > TRAVERSER_LIMIT then
|
||||||
atdebug("Traverser hit counter at",adj_pos, adj_connid,"found tcb's:",found_tcbs)
|
atdebug("Traverser hit counter at",adj_pos, adj_connid)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
-- continue traversing
|
-- continue traversing
|
||||||
|
@ -185,62 +238,122 @@ local function traverser(found_tcbs, pos, conns, connid, count)
|
||||||
return counter_hit
|
return counter_hit
|
||||||
end
|
end
|
||||||
|
|
||||||
local function sigd_equal(sigd, cmp)
|
|
||||||
return vector.equals(sigd.p, cmp.p) and sigd.s==cmp.s
|
|
||||||
|
-- Merges the TS with merge_id into root_id and then deletes merge_id
|
||||||
|
local function merge_ts(root_id, merge_id)
|
||||||
|
local rts = ildb.get_ts(root_id)
|
||||||
|
local mts = ildb.get_ts(merge_id)
|
||||||
|
|
||||||
|
-- cobble together the list of TCBs
|
||||||
|
for _, msigd in ipairs(mts.tc_breaks) do
|
||||||
|
local tcbs = ildb.get_tcbs(msigd)
|
||||||
|
if tcbs then
|
||||||
|
insert_sigd_nodouble(rts.tc_breaks, msigd)
|
||||||
|
tcbs.ts_id = root_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- done
|
||||||
|
track_sections[merge_id] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- TODO temporary
|
||||||
|
local lntrans = { "A", "B" }
|
||||||
|
local function sigd_to_string(sigd)
|
||||||
|
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for near TCBs and connect to their TS if they have one, and syncs their data.
|
||||||
|
function ildb.sync_tcb_neighbors(pos, connid)
|
||||||
|
|
||||||
-- Updates the neighbors of this TCB using the traverser function (see comments above)
|
|
||||||
-- returns true if the traverser hit the counter, which means that there could be another
|
|
||||||
-- TCB outside of the traversed range.
|
|
||||||
function ildb.update_tcb_neighbors(pos, connid)
|
|
||||||
local found_tcbs = { {p = pos, s = connid} }
|
local found_tcbs = { {p = pos, s = connid} }
|
||||||
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||||
if not node_ok then
|
if not node_ok then
|
||||||
error("update_tcb_neighbors but node is NOK: "..minetest.pos_to_string(pos))
|
error("update_tcb_neighbors but node is NOK: "..minetest.pos_to_string(pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
atdebug("Traversing from ",pos, connid)
|
||||||
local counter_hit = traverser(found_tcbs, pos, conns, connid, 0, hit_counter)
|
local counter_hit = traverser(found_tcbs, pos, conns, connid, 0, hit_counter)
|
||||||
|
|
||||||
|
local ts_id
|
||||||
|
local list_eoi = {}
|
||||||
|
local list_ok = {}
|
||||||
|
local list_mismatch = {}
|
||||||
|
local ts_to_merge = {}
|
||||||
|
|
||||||
for idx, sigd in pairs(found_tcbs) do
|
for idx, sigd in pairs(found_tcbs) do
|
||||||
local tcb = ildb.get_tcb(sigd.p)
|
local tcbs = ildb.get_tcbs(sigd)
|
||||||
local tcbs = tcb[sigd.s]
|
if not tcbs.ts_id then
|
||||||
|
atdebug("Sync: put",sigd_to_string(sigd),"into list_eoi")
|
||||||
tcbs.end_of_interlocking = nil
|
table.insert(list_eoi, sigd)
|
||||||
tcbs.incomplete = counter_hit
|
elseif not ts_id and tcbs.ts_id then
|
||||||
tcbs.adjacent = {}
|
if not ildb.get_ts(tcbs.ts_id) then
|
||||||
|
atwarn("Track section database is inconsistent, there's no TS with ID=",tcbs.ts_id)
|
||||||
for idx2, other_sigd in pairs(found_tcbs) do
|
tcbs.ts_id = nil
|
||||||
if idx~=idx2 then
|
table.insert(list_eoi, sigd)
|
||||||
ildb.add_adjacent(tcbs, sigd.p, sigd.s, other_sigd)
|
else
|
||||||
|
atdebug("Sync: put",sigd_to_string(sigd),"into list_ok")
|
||||||
|
ts_id = tcbs.ts_id
|
||||||
|
table.insert(list_ok, sigd)
|
||||||
end
|
end
|
||||||
|
elseif ts_id and tcbs.ts_id and tcbs.ts_id ~= ts_id then
|
||||||
|
atwarn("Track section database is inconsistent, sections share track!")
|
||||||
|
atwarn("Merging",tcbs.ts_id,"into",ts_id,".")
|
||||||
|
table.insert(list_mismatch, sigd)
|
||||||
|
table.insert(ts_to_merge, tcbs.ts_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ts_id then
|
||||||
|
local ts = ildb.get_ts(ts_id)
|
||||||
|
for _, sigd in ipairs(list_eoi) do
|
||||||
|
local tcbs = ildb.get_tcbs(sigd)
|
||||||
|
tcbs.ts_id = ts_id
|
||||||
|
table.insert(ts.tc_breaks, sigd)
|
||||||
|
end
|
||||||
|
for _, mts in ipairs(ts_to_merge) do
|
||||||
|
merge_ts(ts_id, mts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return hit_counter
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add the adjacency entry into the tcbs, but without duplicating it
|
function ildb.link_track_sections(merge_id, root_id)
|
||||||
-- and without adding a self-reference
|
if merge_id == root_id then
|
||||||
function ildb.add_adjacent(tcbs, this_pos, this_connid, sigd)
|
|
||||||
if sigd_equal(sigd, {p=this_pos, s=this_connid}) then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
tcbs.end_of_interlocking = nil
|
merge_ts(root_id, merge_id)
|
||||||
if not tcbs.adjacent then
|
end
|
||||||
tcbs.adjacent = {}
|
|
||||||
end
|
function ildb.remove_from_interlocking(sigd)
|
||||||
for idx, cmp in pairs(tcbs.adjacent) do
|
local tcbs = ildb.get_tcbs(sigd)
|
||||||
if sigd_equal(sigd, cmp) then
|
if tcbs.ts_id then
|
||||||
|
local tsid = tcbs.ts_id
|
||||||
|
local ts = ildb.get_ts(tsid)
|
||||||
|
if not ts then
|
||||||
|
tcbs.ts_id = nil
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- remove entry from the list
|
||||||
|
local idx = 1
|
||||||
|
while idx <= #ts.tc_breaks do
|
||||||
|
local cmp = ts.tc_breaks[idx]
|
||||||
|
if sigd_equal(sigd, cmp) then
|
||||||
|
table.remove(ts.tc_breaks, idx)
|
||||||
|
else
|
||||||
|
idx = idx + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tcbs.ts_id = nil
|
||||||
|
|
||||||
|
ildb.sync_tcb_neighbors(sigd.p, sigd.s)
|
||||||
|
|
||||||
|
if #ts.tc_breaks == 0 then
|
||||||
|
track_sections[tsid] = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
table.insert(tcbs.adjacent, sigd)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
advtrains.interlocking.db = ildb
|
advtrains.interlocking.db = ildb
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,6 @@ advtrains.interlocking = {}
|
||||||
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
|
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
|
||||||
|
|
||||||
dofile(modpath.."database.lua")
|
dofile(modpath.."database.lua")
|
||||||
dofile(modpath.."tcb.lua")
|
dofile(modpath.."tcb_ts_ui.lua")
|
||||||
dofile(modpath.."signal_api.lua")
|
dofile(modpath.."signal_api.lua")
|
||||||
dofile(modpath.."demosignals.lua")
|
dofile(modpath.."demosignals.lua")
|
||||||
|
|
|
@ -1,180 +0,0 @@
|
||||||
-- Track Circuit Breaks - Player interaction
|
|
||||||
|
|
||||||
local players_assign_tcb = {}
|
|
||||||
local players_addfar_tcb = {}
|
|
||||||
|
|
||||||
local lntrans = { "A", "B" }
|
|
||||||
|
|
||||||
local function sigd_to_string(sigd)
|
|
||||||
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_node("advtrains_interlocking:tcb_node", {
|
|
||||||
drawtype = "mesh",
|
|
||||||
paramtype="light",
|
|
||||||
paramtype2="facedir",
|
|
||||||
walkable = true,
|
|
||||||
selection_box = {
|
|
||||||
type = "fixed",
|
|
||||||
fixed = {-1/4, -1/2, -1/4, 1/4, 1/2, 1/4},
|
|
||||||
},
|
|
||||||
mesh = "at_il_tcb_node.obj",
|
|
||||||
tiles = {"at_il_tcb_node.png"},
|
|
||||||
description="Track Circuit Break",
|
|
||||||
sunlight_propagates=true,
|
|
||||||
groups = {
|
|
||||||
cracky=3,
|
|
||||||
not_blocking_trains=1,
|
|
||||||
--save_in_at_nodedb=2,
|
|
||||||
},
|
|
||||||
after_place_node = function(pos, node, player)
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
meta:set_string("infotext", "Unconfigured Track Circuit Break, right-click to assign.")
|
|
||||||
end,
|
|
||||||
on_rightclick = function(pos, node, player)
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
local tcbpts = meta:get_string("tcb_pos")
|
|
||||||
local pname = player:get_player_name()
|
|
||||||
if tcbpts ~= "" then
|
|
||||||
local tcbpos = minetest.string_to_pos(tcbpts)
|
|
||||||
advtrains.interlocking.show_tcb_form(tcbpos, pname)
|
|
||||||
else
|
|
||||||
--unconfigured
|
|
||||||
--TODO security
|
|
||||||
minetest.chat_send_player(pname, "Configuring TCB: Please punch the rail you want to assign this TCB to.")
|
|
||||||
|
|
||||||
players_assign_tcb[pname] = pos
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_punch = function(pos, node, player)
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
atwarn("Would show tcb marker.")
|
|
||||||
-- TODO TCB-Marker anzeigen
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
|
|
||||||
local pname = player:get_player_name()
|
|
||||||
local tcbnpos = players_assign_tcb[pname]
|
|
||||||
if tcbnpos then
|
|
||||||
if vector.distance(pos, tcbnpos)<=20 then
|
|
||||||
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
|
||||||
if node_ok and #conns == 2 then
|
|
||||||
advtrains.interlocking.db.create_tcb(pos)
|
|
||||||
|
|
||||||
advtrains.interlocking.db.update_tcb_neighbors(pos, 1)
|
|
||||||
advtrains.interlocking.db.update_tcb_neighbors(pos, 2)
|
|
||||||
|
|
||||||
local meta = minetest.get_meta(tcbnpos)
|
|
||||||
meta:set_string("tcb_pos", minetest.pos_to_string(pos))
|
|
||||||
meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
|
|
||||||
minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB")
|
|
||||||
else
|
|
||||||
minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.")
|
|
||||||
end
|
|
||||||
players_assign_tcb[pname] = nil
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
local function mkformspec(tcbs, btnpref, offset, pname)
|
|
||||||
local form = "label[0.5,"..offset..";Side "..btnpref..": "..(tcbs.end_of_interlocking and "End of interlocking" or "Track Circuit").."]"
|
|
||||||
if tcbs.end_of_interlocking then
|
|
||||||
form = form.."button[0.5,"..(offset+1)..";3,1;"..btnpref.."_clearadj;Activate Interlocking]"
|
|
||||||
if tcbs.section_free then
|
|
||||||
form = form.."button[4.5,"..(offset+1)..";3,1;"..btnpref.."_setlocked;Section is free]"
|
|
||||||
else
|
|
||||||
form = form.."button[4.5,"..(offset+1)..";3,1;"..btnpref.."_setfree;Section is blocked]"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local strtab = {}
|
|
||||||
for idx, sigd in ipairs(tcbs.adjacent) do
|
|
||||||
strtab[idx] = minetest.formspec_escape(sigd_to_string(sigd))
|
|
||||||
end
|
|
||||||
form = form.."textlist[0.5,"..(offset+1)..";5,3;"..btnpref.."_adjlist;"..table.concat(strtab, ",").."]"
|
|
||||||
if players_addfar_tcb[pname] then
|
|
||||||
local sigd = players_addfar_tcb[pname]
|
|
||||||
form = form.."button[5.5,"..(offset+2)..";2.5,1;"..btnpref.."_addadj;Link TCB to "..sigd_to_string(sigd).."]"
|
|
||||||
form = form.."button[8,"..(offset+2)..";0.5,1;"..btnpref.."_canceladdadj;X]"
|
|
||||||
else
|
|
||||||
form = form.."button[5.5,"..(offset+2)..";3,1;"..btnpref.."_addadj;Add far TCB]"
|
|
||||||
end
|
|
||||||
form = form.."button[5.5,"..(offset+1)..";3,1;"..btnpref.."_clearadj;Clear&Update]"
|
|
||||||
form = form.."button[5.5,"..(offset+3)..";3,1;"..btnpref.."_mknonint;Make non-interlocked]"
|
|
||||||
if tcbs.incomplete then
|
|
||||||
form = form.."label[0.5,"..(offset+0.5)..";Warning: You possibly need to add TCBs manually!]"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return form
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function advtrains.interlocking.show_tcb_form(pos, pname)
|
|
||||||
local tcb = advtrains.interlocking.db.get_tcb(pos)
|
|
||||||
if not tcb then return end
|
|
||||||
|
|
||||||
local form = "size[10,10] label[0.5,0.5;Track Circuit Break Configuration]"
|
|
||||||
form = form .. mkformspec(tcb[1], "A", 1, pname)
|
|
||||||
form = form .. mkformspec(tcb[2], "B", 6, pname)
|
|
||||||
|
|
||||||
minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
||||||
local pname = player:get_player_name()
|
|
||||||
local pts = string.match(formname, "^at_il_tcbconfig_(.+)$")
|
|
||||||
local pos
|
|
||||||
if pts then
|
|
||||||
pos = minetest.string_to_pos(pts)
|
|
||||||
end
|
|
||||||
if pos and not fields.quit then
|
|
||||||
local tcb = advtrains.interlocking.db.get_tcb(pos)
|
|
||||||
if not tcb then return end
|
|
||||||
local f_clearadj = {fields.A_clearadj, fields.B_clearadj}
|
|
||||||
local f_addadj = {fields.A_addadj, fields.B_addadj}
|
|
||||||
local f_canceladdadj = {fields.A_canceladdadj, fields.B_canceladdadj}
|
|
||||||
local f_setlocked = {fields.A_setlocked, fields.B_setlocked}
|
|
||||||
local f_setfree = {fields.A_setfree, fields.B_setfree}
|
|
||||||
local f_mknonint = {fields.A_mknonint, fields.B_mknonint}
|
|
||||||
|
|
||||||
for connid=1,2 do
|
|
||||||
if f_clearadj[connid] then
|
|
||||||
advtrains.interlocking.db.update_tcb_neighbors(pos, connid)
|
|
||||||
end
|
|
||||||
if f_mknonint[connid] then
|
|
||||||
--TODO: remove this from the other tcb's
|
|
||||||
tcb[connid].end_of_interlocking = true
|
|
||||||
end
|
|
||||||
if f_addadj[connid] then
|
|
||||||
if players_addfar_tcb[pname] then
|
|
||||||
local sigd = players_addfar_tcb[pname]
|
|
||||||
advtrains.interlocking.db.add_adjacent(tcb[connid], pos, connid, sigd)
|
|
||||||
players_addfar_tcb[pname] = nil
|
|
||||||
else
|
|
||||||
players_addfar_tcb[pname] = {p = pos, s = connid}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if f_canceladdadj[connid] then
|
|
||||||
players_addfar_tcb[pname] = nil
|
|
||||||
end
|
|
||||||
if f_setfree[connid] then
|
|
||||||
tcb[connid].section_free = true
|
|
||||||
end
|
|
||||||
if f_setlocked[connid] then
|
|
||||||
tcb[connid].section_free = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
advtrains.interlocking.show_tcb_form(pos, pname)
|
|
||||||
end
|
|
||||||
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
-- Track Circuit Breaks and Track Sections - Player interaction
|
||||||
|
|
||||||
|
local players_assign_tcb = {}
|
||||||
|
local players_link_ts = {}
|
||||||
|
|
||||||
|
local lntrans = { "A", "B" }
|
||||||
|
|
||||||
|
local function sigd_to_string(sigd)
|
||||||
|
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_node("advtrains_interlocking:tcb_node", {
|
||||||
|
drawtype = "mesh",
|
||||||
|
paramtype="light",
|
||||||
|
paramtype2="facedir",
|
||||||
|
walkable = true,
|
||||||
|
selection_box = {
|
||||||
|
type = "fixed",
|
||||||
|
fixed = {-1/4, -1/2, -1/4, 1/4, 1/2, 1/4},
|
||||||
|
},
|
||||||
|
mesh = "at_il_tcb_node.obj",
|
||||||
|
tiles = {"at_il_tcb_node.png"},
|
||||||
|
description="Track Circuit Break",
|
||||||
|
sunlight_propagates=true,
|
||||||
|
groups = {
|
||||||
|
cracky=3,
|
||||||
|
not_blocking_trains=1,
|
||||||
|
--save_in_at_nodedb=2,
|
||||||
|
},
|
||||||
|
after_place_node = function(pos, node, player)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
meta:set_string("infotext", "Unconfigured Track Circuit Break, right-click to assign.")
|
||||||
|
end,
|
||||||
|
on_rightclick = function(pos, node, player)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
local tcbpts = meta:get_string("tcb_pos")
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
if tcbpts ~= "" then
|
||||||
|
local tcbpos = minetest.string_to_pos(tcbpts)
|
||||||
|
advtrains.interlocking.show_tcb_form(tcbpos, pname)
|
||||||
|
else
|
||||||
|
--unconfigured
|
||||||
|
--TODO security
|
||||||
|
minetest.chat_send_player(pname, "Configuring TCB: Please punch the rail you want to assign this TCB to.")
|
||||||
|
|
||||||
|
players_assign_tcb[pname] = pos
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_punch = function(pos, node, player)
|
||||||
|
local meta = minetest.get_meta(pos)
|
||||||
|
atwarn("Would show tcb marker.")
|
||||||
|
-- TODO TCB-Marker anzeigen
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
local tcbnpos = players_assign_tcb[pname]
|
||||||
|
if tcbnpos then
|
||||||
|
if vector.distance(pos, tcbnpos)<=20 then
|
||||||
|
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||||
|
if node_ok and #conns == 2 then
|
||||||
|
advtrains.interlocking.db.create_tcb(pos)
|
||||||
|
|
||||||
|
advtrains.interlocking.db.sync_tcb_neighbors(pos, 1)
|
||||||
|
advtrains.interlocking.db.sync_tcb_neighbors(pos, 2)
|
||||||
|
|
||||||
|
local meta = minetest.get_meta(tcbnpos)
|
||||||
|
meta:set_string("tcb_pos", minetest.pos_to_string(pos))
|
||||||
|
meta:set_string("infotext", "TCB assigned to "..minetest.pos_to_string(pos))
|
||||||
|
minetest.chat_send_player(pname, "Configuring TCB: Successfully configured TCB")
|
||||||
|
else
|
||||||
|
minetest.chat_send_player(pname, "Configuring TCB: This is not a normal two-connection rail! Aborted.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
minetest.chat_send_player(pname, "Configuring TCB: Node is too far away. Aborted.")
|
||||||
|
end
|
||||||
|
players_assign_tcb[pname] = nil
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
-- TCB Form
|
||||||
|
|
||||||
|
local function mktcbformspec(tcbs, btnpref, offset, pname)
|
||||||
|
local form = ""
|
||||||
|
local ts
|
||||||
|
if tcbs.ts_id then
|
||||||
|
ts = advtrains.interlocking.db.get_ts(tcbs.ts_id)
|
||||||
|
end
|
||||||
|
if ts then
|
||||||
|
form = form.."label[0.5,"..offset..";Side "..btnpref..": "..ts.name.."]"
|
||||||
|
form = form.."button[0.5,"..(offset+1)..";5,1;"..btnpref.."_gotots;Show track section]"
|
||||||
|
form = form.."button[0.5,"..(offset+2)..";2.5,1;"..btnpref.."_update;Update near TCBs]"
|
||||||
|
form = form.."button[3 ,"..(offset+2)..";2.5,1;"..btnpref.."_remove;Remove from section]"
|
||||||
|
else
|
||||||
|
tcbs.ts_id = nil
|
||||||
|
form = form.."label[0.5,"..offset..";Side "..btnpref..": ".."End of interlocking]"
|
||||||
|
form = form.."button[0.5,"..(offset+1)..";5,1;"..btnpref.."_makeil;Create Interlocked Track Section]"
|
||||||
|
if tcbs.section_free then
|
||||||
|
form = form.."button[0.5,"..(offset+2)..";5,1;"..btnpref.."_setlocked;Section is free]"
|
||||||
|
else
|
||||||
|
form = form.."button[0.5,"..(offset+2)..";5,1;"..btnpref.."_setfree;Section is blocked]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return form
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function advtrains.interlocking.show_tcb_form(pos, pname)
|
||||||
|
local tcb = advtrains.interlocking.db.get_tcb(pos)
|
||||||
|
if not tcb then return end
|
||||||
|
|
||||||
|
local form = "size[6,7] label[0.5,0.5;Track Circuit Break Configuration]"
|
||||||
|
form = form .. mktcbformspec(tcb[1], "A", 1, pname)
|
||||||
|
form = form .. mktcbformspec(tcb[2], "B", 4, pname)
|
||||||
|
|
||||||
|
minetest.show_formspec(pname, "at_il_tcbconfig_"..minetest.pos_to_string(pos), form)
|
||||||
|
end
|
||||||
|
|
||||||
|
--helper: length of nil table is 0
|
||||||
|
local function nlen(t)
|
||||||
|
if not t then return 0 end
|
||||||
|
return #t
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
local pts = string.match(formname, "^at_il_tcbconfig_(.+)$")
|
||||||
|
local pos
|
||||||
|
if pts then
|
||||||
|
pos = minetest.string_to_pos(pts)
|
||||||
|
end
|
||||||
|
if pos and not fields.quit then
|
||||||
|
local tcb = advtrains.interlocking.db.get_tcb(pos)
|
||||||
|
if not tcb then return end
|
||||||
|
local f_gotots = {fields.A_gotots, fields.B_gotots}
|
||||||
|
local f_update = {fields.A_update, fields.B_update}
|
||||||
|
local f_remove = {fields.A_remove, fields.B_remove}
|
||||||
|
local f_makeil = {fields.A_makeil, fields.B_makeil}
|
||||||
|
local f_setlocked = {fields.A_setlocked, fields.B_setlocked}
|
||||||
|
local f_setfree = {fields.A_setfree, fields.B_setfree}
|
||||||
|
|
||||||
|
for connid=1,2 do
|
||||||
|
local tcbs = tcb[connid]
|
||||||
|
if tcbs.ts_id then
|
||||||
|
if f_gotots[connid] then
|
||||||
|
advtrains.interlocking.show_ts_form(tcbs.ts_id, pname)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if f_update[connid] then
|
||||||
|
advtrains.interlocking.db.sync_tcb_neighbors(pos, connid)
|
||||||
|
end
|
||||||
|
if f_remove[connid] then
|
||||||
|
advtrains.interlocking.db.remove_from_interlocking({p=pos, s=connid})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if f_makeil[connid] then
|
||||||
|
advtrains.interlocking.db.create_ts({p=pos, s=connid})
|
||||||
|
advtrains.interlocking.db.sync_tcb_neighbors(pos, connid)
|
||||||
|
end
|
||||||
|
-- non-interlocked
|
||||||
|
if f_setfree[connid] then
|
||||||
|
tcbs.section_free = true
|
||||||
|
end
|
||||||
|
if f_setlocked[connid] then
|
||||||
|
tcbs.section_free = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
advtrains.interlocking.show_tcb_form(pos, pname)
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- TS Formspec
|
||||||
|
|
||||||
|
function advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
|
||||||
|
local ts = advtrains.interlocking.db.get_ts(ts_id)
|
||||||
|
if not ts_id then return end
|
||||||
|
|
||||||
|
local form = "size[10,10]label[0.5,0.5;Track Section Detail - "..ts_id.."]"
|
||||||
|
form = form.."field[0.8,2;5.2,1;name;Section name;"..ts.name.."]"
|
||||||
|
form = form.."button[5.5,1.7;1,1;setname;Set]"
|
||||||
|
local hint
|
||||||
|
|
||||||
|
local strtab = {}
|
||||||
|
for idx, sigd in ipairs(ts.tc_breaks) do
|
||||||
|
strtab[#strtab+1] = minetest.formspec_escape(sigd_to_string(sigd))
|
||||||
|
end
|
||||||
|
|
||||||
|
form = form.."textlist[0.5,3;5,3;tcblist;"..table.concat(strtab, ",").."]"
|
||||||
|
if players_link_ts[pname] then
|
||||||
|
local other_id = players_link_ts[pname]
|
||||||
|
local other_ts = advtrains.interlocking.db.get_ts(other_id)
|
||||||
|
if other_ts then
|
||||||
|
form = form.."button[5.5,3.5;3.5,1;mklink;Join with "..other_ts.name.."]"
|
||||||
|
form = form.."button[9 ,3.5;0.5,1;cancellink;X]"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
form = form.."button[5.5,3.5;4,1;link;Join into other section]"
|
||||||
|
hint = 1
|
||||||
|
end
|
||||||
|
if sel_tcb then
|
||||||
|
form = form.."button[5.5,4.5;4,1;del_tcb;Remove selected TCB]"
|
||||||
|
hint = 2
|
||||||
|
end
|
||||||
|
if hint == 1 then
|
||||||
|
form = form.."label[0.5,0.75;Use the 'Join' button to designate rail crosses and link not listed far-away TCBs]"
|
||||||
|
elseif hint == 2 then
|
||||||
|
form = form.."label[0.5,0.75;Removing a TCB will set it to non-interlocked mode.]"
|
||||||
|
form = form.."label[0.5,1;Trying to remove a TCB directly connected to this track will not work.]"
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.show_formspec(pname, "at_il_tsconfig_"..ts_id, form)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||||
|
local pname = player:get_player_name()
|
||||||
|
local ts_id = string.match(formname, "^at_il_tsconfig_(.+)$")
|
||||||
|
if ts_id and not fields.quit then
|
||||||
|
local ts = advtrains.interlocking.db.get_ts(ts_id)
|
||||||
|
if not ts then return end
|
||||||
|
|
||||||
|
local sel_tcb
|
||||||
|
if fields.tcblist then
|
||||||
|
local tev = minetest.explode_textlist_event(fields.tcblist)
|
||||||
|
sel_tcb = tev.index
|
||||||
|
end
|
||||||
|
|
||||||
|
if players_link_ts[pname] then
|
||||||
|
if fields.cancellink then
|
||||||
|
players_link_ts[pname] = nil
|
||||||
|
elseif fields.mklink then
|
||||||
|
advtrains.interlocking.db.link_track_sections(players_link_ts[pname], ts_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.del_tcb and sel_tcb and sel_tcb > 0 and sel_tcb <= #ts.tc_breaks then
|
||||||
|
advtrains.interlocking.db.remove_from_interlocking(ts.tc_breaks[sel_tcb])
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.link then
|
||||||
|
players_link_ts[pname] = ts_id
|
||||||
|
end
|
||||||
|
|
||||||
|
if fields.setname then
|
||||||
|
ts.name = fields.name
|
||||||
|
if ts.name == "" then
|
||||||
|
ts.name = "Section "..ts_id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
advtrains.interlocking.show_ts_form(ts_id, pname, sel_tcb)
|
||||||
|
end
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue