aboutsummaryrefslogtreecommitdiff
path: root/advtrains_interlocking
diff options
context:
space:
mode:
authororwell96 <orwell@bleipb.de>2018-06-21 20:34:20 +0200
committerorwell96 <orwell@bleipb.de>2018-06-21 20:34:20 +0200
commite1ebbff23c10213c4bc9f54bff0870810430cf9d (patch)
treeeabd17b21a3f035cd029d4fb088ede153d226fcc /advtrains_interlocking
parent5992618ee837351124f59c1c0289391a9d501ade (diff)
downloadadvtrains-e1ebbff23c10213c4bc9f54bff0870810430cf9d.tar.gz
advtrains-e1ebbff23c10213c4bc9f54bff0870810430cf9d.tar.bz2
advtrains-e1ebbff23c10213c4bc9f54bff0870810430cf9d.zip
Add track section concept and rework TCB design, implement new linking behavior
Diffstat (limited to 'advtrains_interlocking')
-rw-r--r--advtrains_interlocking/database.lua213
-rw-r--r--advtrains_interlocking/init.lua2
-rw-r--r--advtrains_interlocking/tcb.lua180
-rw-r--r--advtrains_interlocking/tcb_ts_ui.lua265
4 files changed, 429 insertions, 231 deletions
diff --git a/advtrains_interlocking/database.lua b/advtrains_interlocking/database.lua
index e318dd2..b4932ee 100644
--- a/advtrains_interlocking/database.lua
+++ b/advtrains_interlocking/database.lua
@@ -93,13 +93,20 @@ local TRAVERSER_LIMIT = 100
local ildb = {}
local track_circuit_breaks = {}
+local track_sections = {}
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
function ildb.save()
- return {}
+ return {tcbs = track_circuit_breaks, ts=track_sections}
end
--
@@ -107,17 +114,7 @@ end
TCB data structure
{
[1] = { -- Variant: with adjacent TCs.
- == Synchronized properties == Apply to the whole TC
- 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
+ ts_id = <id> -- ID of the assigned track section
signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal
-- aspect will be set accordingly.
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
},
[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,
-- 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>}
]]
@@ -141,8 +151,8 @@ Signal specifier (a pair of TCB/Side):
--
function ildb.create_tcb(pos)
local new_tcb = {
- [1] = {end_of_interlocking = true},
- [2] = {end_of_interlocking = true},
+ [1] = {},
+ [2] = {},
}
local pts = advtrains.roundfloorpts(pos)
track_circuit_breaks[pts] = new_tcb
@@ -153,6 +163,48 @@ function ildb.get_tcb(pos)
return track_circuit_breaks[pts]
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
-- so, this needs the conns of the node at pos, since these are already calculated
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)
if tcb then
-- 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
end
end
-- recursion abort condition
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
end
-- continue traversing
@@ -185,62 +238,122 @@ local function traverser(found_tcbs, pos, conns, connid, count)
return counter_hit
end
-local function sigd_equal(sigd, cmp)
- return vector.equals(sigd.p, cmp.p) and sigd.s==cmp.s
-end
-
+-- 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
+-- TODO temporary
+local lntrans = { "A", "B" }
+local function sigd_to_string(sigd)
+ return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
+end
--- 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)
+-- Check for near TCBs and connect to their TS if they have one, and syncs their data.
+function ildb.sync_tcb_neighbors(pos, connid)
local found_tcbs = { {p = pos, s = connid} }
local node_ok, conns, rhe = advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
if not node_ok then
error("update_tcb_neighbors but node is NOK: "..minetest.pos_to_string(pos))
end
+ atdebug("Traversing from ",pos, connid)
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
- local tcb = ildb.get_tcb(sigd.p)
- local tcbs = tcb[sigd.s]
-
- tcbs.end_of_interlocking = nil
- tcbs.incomplete = counter_hit
- tcbs.adjacent = {}
-
- for idx2, other_sigd in pairs(found_tcbs) do
- if idx~=idx2 then
- ildb.add_adjacent(tcbs, sigd.p, sigd.s, other_sigd)
+ local tcbs = ildb.get_tcbs(sigd)
+ if not tcbs.ts_id then
+ atdebug("Sync: put",sigd_to_string(sigd),"into list_eoi")
+ table.insert(list_eoi, sigd)
+ elseif not ts_id and tcbs.ts_id then
+ if not ildb.get_ts(tcbs.ts_id) then
+ atwarn("Track section database is inconsistent, there's no TS with ID=",tcbs.ts_id)
+ tcbs.ts_id = nil
+ table.insert(list_eoi, sigd)
+ else
+ atdebug("Sync: put",sigd_to_string(sigd),"into list_ok")
+ ts_id = tcbs.ts_id
+ table.insert(list_ok, sigd)
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
-
- return hit_counter
end
--- Add the adjacency entry into the tcbs, but without duplicating it
--- and without adding a self-reference
-function ildb.add_adjacent(tcbs, this_pos, this_connid, sigd)
- if sigd_equal(sigd, {p=this_pos, s=this_connid}) then
+function ildb.link_track_sections(merge_id, root_id)
+ if merge_id == root_id then
return
end
- tcbs.end_of_interlocking = nil
- if not tcbs.adjacent then
- tcbs.adjacent = {}
- end
- for idx, cmp in pairs(tcbs.adjacent) do
- if sigd_equal(sigd, cmp) then
+ merge_ts(root_id, merge_id)
+end
+
+function ildb.remove_from_interlocking(sigd)
+ local tcbs = ildb.get_tcbs(sigd)
+ 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
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
- table.insert(tcbs.adjacent, sigd)
end
+
+
advtrains.interlocking.db = ildb
diff --git a/advtrains_interlocking/init.lua b/advtrains_interlocking/init.lua
index e3be234..54fdb68 100644
--- a/advtrains_interlocking/init.lua
+++ b/advtrains_interlocking/init.lua
@@ -6,6 +6,6 @@ advtrains.interlocking = {}
local modpath = minetest.get_modpath(minetest.get_current_modname()) .. DIR_DELIM
dofile(modpath.."database.lua")
-dofile(modpath.."tcb.lua")
+dofile(modpath.."tcb_ts_ui.lua")
dofile(modpath.."signal_api.lua")
dofile(modpath.."demosignals.lua")
diff --git a/advtrains_interlocking/tcb.lua b/advtrains_interlocking/tcb.lua
deleted file mode 100644
index 742bb62..0000000
--- a/advtrains_interlocking/tcb.lua
+++ /dev/null
@@ -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)
-
-
-
-
-
diff --git a/advtrains_interlocking/tcb_ts_ui.lua b/advtrains_interlocking/tcb_ts_ui.lua
new file mode 100644
index 0000000..b2f1ef8
--- /dev/null
+++ b/advtrains_interlocking/tcb_ts_ui.lua
@@ -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)
+
+